Complete console.trace and added a testcases for all 13 functions.

This commit is contained in:
NeetBhagat 2017-06-13 20:25:01 +05:30
parent 5c324264a2
commit 9646eba873
15 changed files with 204 additions and 165 deletions

View file

@ -66,9 +66,9 @@ Windows.Window {
}
}
function fromConsole() {
if (root.dynamicContent && root.dynamicContent.fromConsole) {
root.dynamicContent.fromConsole();
function clearDebugWindow() {
if (root.dynamicContent && root.dynamicContent.clearWindow) {
root.dynamicContent.clearWindow();
}
}

View file

@ -5577,7 +5577,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage);
connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage);
connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::OnClearConsole);
connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow);
}
bool Application::canAcceptURL(const QString& urlString) const {

View file

@ -2,12 +2,14 @@
// ConsoleScriptingInterface.cpp
// libraries/script-engine/src
//
// Created by volansystech on 6/1/17.
// Created by NeetBhagat on 6/1/17.
// Copyright 2014 High Fidelity, Inc.
//
// ConsoleScriptingInterface is responsible to print logs
// using "console" object on debug Window and Logs/log file with proper tags.
// Used in Javascripts file.
// ConsoleScriptingInterface is responsible for following functionality
// Printing logs with various tags and grouping on debug Window and Logs/log file.
// Debugging functionalities like Timer start-end, assertion, trace.
// To use these functionalities, use "console" object in Javascript files.
// For examples please refer "scripts/developer/tests/consoleObjectTest.js"
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -16,6 +18,10 @@
#include "ConsoleScriptingInterface.h"
#include "ScriptEngine.h"
#define INDENTATION 4
const QString LINE_SEPARATOR = "\n ";
const QString STACK_TRACE_FORMAT = "\n[Stacktrace]%1%2";
void ConsoleScriptingInterface::info(QString message) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->scriptInfoMessage(message);
@ -23,12 +29,12 @@ void ConsoleScriptingInterface::info(QString message) {
}
void ConsoleScriptingInterface::log(QString message) {
if (_isGroupEnd) {
if (_groupDetails.count() == 0) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->scriptPrintedMessage(message);
}
} else {
consoleGroupLog(_selectedGroup, message);
this->logGroupMessage(message);
}
}
@ -57,127 +63,79 @@ void ConsoleScriptingInterface::exception(QString message) {
}
void ConsoleScriptingInterface::time(QString labelName) {
QDateTime _currentTime = QDateTime::currentDateTime().toUTC();
_listOfTimeValues.insert(labelName, _currentTime.toUTC());
_timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC());
QString message = QString("%1: timer started").arg(labelName);
this->log(message);
}
void ConsoleScriptingInterface::timeEnd(QString labelName) {
QDateTime _dateTimeOfLabel;
QString message;
qint64 millisecondsDiff = 0;
bool _labelInList = _listOfTimeValues.contains(labelName);
//Check if not exist items in list
if (!_labelInList) {
message = "No such label found " + labelName;
error(message);
if (!_timerDetails.contains(labelName)) {
this->error("No such label found " + labelName);
return;
}
QDateTime _currentDateTimeValue = QDateTime::currentDateTime().toUTC();
_dateTimeOfLabel = _listOfTimeValues.value(labelName);
if (!_dateTimeOfLabel.isNull()) {
_listOfTimeValues.remove(labelName);
if (_timerDetails.value(labelName).isNull()) {
_timerDetails.remove(labelName);
this->error("Invalid start time for " + labelName);
return;
}
QDateTime _startTime = _timerDetails.value(labelName);
QDateTime _endTime = QDateTime::currentDateTime().toUTC();
qint64 diffInMS = _startTime.msecsTo(_endTime);
QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS));
_timerDetails.remove(labelName);
if (_dateTimeOfLabel.toString(TIME_FORMAT) == _currentDateTimeValue.toString(TIME_FORMAT)) {
millisecondsDiff = _dateTimeOfLabel.msecsTo(_currentDateTimeValue);
message = QString::number(millisecondsDiff) + " ms";
} else {
message = secondsToString(_dateTimeOfLabel.secsTo(_currentDateTimeValue));
}
log("Time : " + message);
}
QString ConsoleScriptingInterface::secondsToString(qint64 seconds)
{
qint64 days = seconds / DAY;
QTime timeDuration = QTime(0, 0).addSecs(seconds % DAY);
return QString("%1 days, %2 hours, %3 minutes, %4 seconds")
.arg(QString::number(days)).arg(QString::number(timeDuration.hour())).arg(QString::number(timeDuration.minute())).arg(QString::number(timeDuration.second()));
this->log(message);
}
void ConsoleScriptingInterface::asserts(bool condition, QString message) {
if (!condition) {
QString assertFailed = "Assertion failed";
QString assertionResult;
if (message.isEmpty()) {
message = assertFailed;
assertionResult = "Assertion failed";
} else {
QString inputType = typeid(message).name();
if (!inputType.compare("QString")) {
message = assertFailed;
} else {
message = assertFailed + " " + message;
}
assertionResult = QString("Assertion failed : %1").arg(message);
}
error(message);
this->error(assertionResult);
}
}
void ConsoleScriptingInterface::trace() {
const auto lineSeparator = "\n ";
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
QStringList backtrace = scriptEngine->currentContext()->backtrace();
auto message = QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
scriptEngine->scriptPrintedMessage(message);
scriptEngine->scriptPrintedMessage
(QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR,
scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR)));
}
}
void ConsoleScriptingInterface::clear() {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->clearConsole();
scriptEngine->clearDebugLogWindow();
}
}
void ConsoleScriptingInterface::group(QString groupName) {
_selectedGroup = groupName;
consoleGroupLog(GROUP, groupName);
this->logGroupMessage(groupName);
_groupDetails.push_back(groupName);
}
void ConsoleScriptingInterface::groupCollapsed(QString groupName) {
_selectedGroup = groupName;
consoleGroupLog(GROUPCOLLAPSED, groupName);
this->logGroupMessage(groupName);
_groupDetails.push_back(groupName);
}
void ConsoleScriptingInterface::groupEnd() {
consoleGroupLog(_selectedGroup, GROUPEND);
_groupDetails.removeLast();
}
void ConsoleScriptingInterface::consoleGroupLog(QString currentGroup, QString groupName) {
QPair<QString, QString> groupData;
QString groupKeyName;
groupData = qMakePair(currentGroup, groupName);
groupKeyName = groupData.first;
if (groupData.first == GROUP || groupData.first == GROUPCOLLAPSED) {
groupKeyName = groupData.second;
if (_isSameLevel) {
_addSpace = _addSpace.mid(0, _addSpace.length() - 3);
}
if (_isGroupEnd) {
_addSpace = "";
_isGroupEnd = false;
} else {
_addSpace += " ";
}
debug(_addSpace.mid(0, _addSpace.length() - 1) + groupData.second);
_isSameLevel = false;
} else {
if (groupData.first == groupKeyName && groupData.first != GROUPCOLLAPSED && groupData.first != GROUP && groupData.second != GROUPEND) {
if (!_isSameLevel) {
_addSpace += " "; //space inner element
_isSameLevel = true; //same level log entry
}
debug(_addSpace + groupData.second);
}
}
if (groupData.second == GROUPEND) {
_addSpace = _addSpace.mid(0, _addSpace.length() - 3);
if (_addSpace.length() == 0) {
_isGroupEnd = true;
}
void ConsoleScriptingInterface::logGroupMessage(QString message) {
int _appendIndentation = _groupDetails.count() * INDENTATION;
QString logMessage;
for (int count = 0; count < _appendIndentation; count++) {
logMessage.append(" ");
}
logMessage.append(message);
this->debug(logMessage);
}

View file

@ -2,12 +2,14 @@
// ConsoleScriptingInterface.h
// libraries/script-engine/src
//
// Created by volansystech on 6/1/17.
// Created by NeetBhagat on 6/1/17.
// Copyright 2014 High Fidelity, Inc.
//
// ConsoleScriptingInterface is responsible to print log.
// using "console" object on debug Window and Logs/log file with proper tags.
// Used in Javascripts file.
// ConsoleScriptingInterface is responsible for following functionality
// Printing logs with various tags and grouping on debug Window and Logs/log file.
// Debugging functionalities like Timer start-end, assertion, trace.
// To use these functionalities, use "console" object in Javascript files.
// For examples please refer "scripts/developer/tests/consoleObjectTest.js"
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -17,17 +19,13 @@
#ifndef hifi_ConsoleScriptingInterface_h
#define hifi_ConsoleScriptingInterface_h
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtScript/QScriptable>
#include <QHash>
#include "EntityEditFilters.h"
#include <QUrl>
#include <QList>
#include <QHash>
/// Scriptable interface of console object. Used exclusively in the JavaScript API
// Scriptable interface of "console" object. Used exclusively in the JavaScript API
class ConsoleScriptingInterface : public QObject, protected QScriptable {
Q_OBJECT
public slots:
@ -38,29 +36,17 @@ public slots:
void error(QString message);
void exception(QString message);
void time(QString labelName);
void timeEnd(QString labelName);
void timeEnd(QString labelName);
void asserts(bool condition, QString message = "");
void trace();
void clear();
void clear();
void group(QString groupName);
void groupCollapsed(QString groupName);
void groupEnd();
void consoleGroupLog(QString currentGroup, QString groupName);
public:
QString secondsToString(qint64 seconds);
bool hasCorrectSyntax(QScriptProgram& program);
QString hadUncaughtExceptions(QScriptEngine& engine, QString& fileName);
void groupCollapsed(QString groupName);
void groupEnd();
private:
QHash<QString, QDateTime> _listOfTimeValues;
const qint64 DAY = 86400;
const QString TIME_FORMAT = "yyyy-MM-dd HH:mm";
QString _selectedGroup;
const QString GROUP = "group";
const QString GROUPCOLLAPSED = "groupCollapse";
const QString GROUPEND = "groupEnd";
bool _isSameLevel = false;
QString _addSpace = "";
bool _isGroupEnd = true;
QHash<QString, QDateTime> _timerDetails;
QList<QString> _groupDetails;
void logGroupMessage(QString msg);
};
#endif // hifi_ConsoleScriptingInterface_h

View file

@ -187,15 +187,6 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
}
logException(output);
});
// this is where all unhandled exceptions end up getting logged
/* connect(this, &BaseScriptEngine::unhandledException, this, [this](const QScriptValue& err) {
auto output = err.engine() == this ? err : makeError(err);
if (!output.property("detail").isValid()) {
output.setProperty("detail", "UnhandledException");
}
logException(output);
});*/
}
QString ScriptEngine::getContext() const {
@ -481,21 +472,21 @@ void ScriptEngine::scriptErrorMessage(const QString& message) {
}
void ScriptEngine::scriptWarningMessage(const QString& message) {
qCWarning(scriptengine) << message;
qCWarning(scriptengine) << qPrintable(message);
emit warningMessage(message, getFilename());
}
void ScriptEngine::scriptInfoMessage(const QString& message) {
qCInfo(scriptengine) << message;
qCInfo(scriptengine) << qPrintable(message);
emit infoMessage(message, getFilename());
}
void ScriptEngine::scriptPrintedMessage(const QString& message) {
qCDebug(scriptengine) << message;
qCDebug(scriptengine) << qPrintable(message);
emit printedMessage(message, getFilename());
}
void ScriptEngine::clearConsole() {
void ScriptEngine::clearDebugLogWindow() {
emit clearDebugWindow();
}
@ -678,11 +669,10 @@ void ScriptEngine::init() {
registerGlobalObject("Entities", entityScriptingInterface.data());
registerGlobalObject("Quat", &_quatLibrary);
registerGlobalObject("Vec3", &_vec3Library);
registerGlobalObject("console", &_consoleScriptingInterface);
registerGlobalObject("Mat4", &_mat4Library);
registerGlobalObject("Uuid", &_uuidLibrary);
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
registerGlobalObject("console", &_consoleScriptingInterface);
registerGlobalObject("File", new FileScriptingInterface(this));
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);

View file

@ -147,7 +147,8 @@ public:
/// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed
/// to scripts. we may not need this to be invokable
void loadURL(const QUrl& scriptURL, bool reload);
bool hasValidScriptSuffix(const QString& scriptFileName);
bool hasValidScriptSuffix(const QString& scriptFileName);
Q_INVOKABLE QString getContext() const;
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; }
@ -225,8 +226,7 @@ public:
void scriptWarningMessage(const QString& message);
void scriptInfoMessage(const QString& message);
void scriptPrintedMessage(const QString& message);
void clearConsole();
void clearDebugLogWindow();
int getNumRunningEntityScripts() const;
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;

View file

@ -54,7 +54,7 @@ void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptN
emit infoMessage(message, scriptName);
}
void ScriptEngines::OnClearConsole() {
void ScriptEngines::onClearDebugWindow() {
emit clearDebugWindow();
}

View file

@ -87,7 +87,7 @@ public slots:
void onWarningMessage(const QString& message, const QString& scriptName);
void onInfoMessage(const QString& message, const QString& scriptName);
void onErrorLoadingScript(const QString& url);
void OnClearConsole();
void onClearDebugWindow();
protected slots:
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);

View file

@ -20,7 +20,7 @@
class BaseScriptEngine : public QScriptEngine, public QEnableSharedFromThis<BaseScriptEngine> {
Q_OBJECT
public:
static const QString SCRIPT_EXCEPTION_FORMAT;
static const QString SCRIPT_EXCEPTION_FORMAT;
static const QString SCRIPT_BACKTRACE_SEP;
// threadsafe "unbound" version of QScriptEngine::nullValue()
@ -29,9 +29,10 @@ public:
BaseScriptEngine() {}
Q_INVOKABLE QScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1);
Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error");
Q_INVOKABLE QString formatException(const QScriptValue& exception, bool includeExtendedDetails);
QScriptValue cloneUncaughtException(const QString& detail = QString());
Q_INVOKABLE QScriptValue makeError(const QScriptValue& other = QScriptValue(), const QString& type = "Error");
Q_INVOKABLE QString formatException(const QScriptValue& exception, bool includeExtendedDetails);
QScriptValue cloneUncaughtException(const QString& detail = QString());
QScriptValue evaluateInClosure(const QScriptValue& locals, const QScriptProgram& program);
// if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it
@ -39,7 +40,8 @@ public:
// if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it.
// note: this is used in cases where C++ code might call into JS API methods directly
bool raiseException(const QScriptValue& exception);
bool raiseException(const QScriptValue& exception);
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
static bool IS_THREADSAFE_INVOCATION(const QThread *thread, const QString& method);
signals:

View file

@ -153,8 +153,8 @@ void QmlWindowClass::sendToQml(const QVariant& message) {
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
}
void QmlWindowClass::clearConsole() {
QMetaObject::invokeMethod(asQuickItem(), "fromConsole", Qt::QueuedConnection);
void QmlWindowClass::clearDebugWindow() {
QMetaObject::invokeMethod(asQuickItem(), "clearDebugWindow", Qt::QueuedConnection);
}
void QmlWindowClass::emitScriptEvent(const QVariant& scriptMessage) {

View file

@ -53,7 +53,7 @@ public slots:
// Scripts can use this to send a message to the QML object
void sendToQml(const QVariant& message);
void clearConsole();
void clearDebugWindow();
// QmlWindow content may include WebView requiring EventBridge.
void emitScriptEvent(const QVariant& scriptMessage);

View file

@ -49,8 +49,8 @@ ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) {
sendToLogWindow("INFO", message, scriptFileName);
});
ScriptDiscoveryService.clearDebugWindow.connect(function () {
window.clearConsole();
ScriptDiscoveryService.clearDebugWindow.connect(function() {
window.clearDebugWindow();
});
}()); // END LOCAL_SCOPE
}());

View file

@ -40,10 +40,10 @@ Rectangle {
}
textArea.append(message);
}
function fromConsole(){
textArea.remove(0,textArea.length);
}
function clearWindow() {
textArea.remove(0,textArea.length);
}
}

View file

@ -0,0 +1,109 @@
// Examples and understanding of console object. Include console methods like
// info, log, debug, warn, error, exception, trace, clear, asserts, group, groupCollapsed, groupEnd, time, timeEnd.
// Useful in debugging and exclusively made for JavaScript files.
// To view the logs click on Developer -> script logs [logs on debug window] and for text file logs go to Logs/log file.
main();
function main() {
var someObject = { str: "Some text", id: 5 };
var someValue = 5;
// console.info examples
console.info("[console.info] Hello World.");
console.info(5 + 6);
console.info(someObject.str);
console.info(a = 2 * 6);
console.info(someValue);
console.info('someObject id ' + someObject.id);
// console.log examples
console.log("[console.log] Hello World");
console.log(5 + 6);
console.log(someObject.str);
console.log(a = 2 * 6);
console.log(someValue);
console.log('someObject id ' + someObject.id);
// console.debug examples
console.debug("[console.debug] Hello World.");
console.debug(5 + 6);
console.debug(someObject.str);
console.debug(a = 2 * 6);
console.debug(someValue);
console.debug('someObject id ' + someObject.id);
// console.warn examples
console.warn("[console.warn] This is warning message.");
console.warn(5 + 6);
console.warn(someObject.str);
console.warn(a = 2 * 6);
console.warn(someValue);
console.warn('someObject id ' + someObject.id);
// console.error examples
console.error('An error occurred!');
console.error('An error occurred! ', 'Value = ', someValue);
console.error('An error occurred! ' + 'Value = ' + someValue);
// console.exception examples
console.exception('An exception occurred!');
console.exception('An exception occurred! ', 'Value = ', someValue);
console.exception('An exception occurred! ' + 'Value = ' + someValue);
// console.trace examples
function fooA() {
function fooB() {
function fooC() {
console.trace();
}
fooC();
}
fooB();
}
fooA();
//console.asserts() examples
var valA = 1, valB = "1";
console.asserts(valA === valB, "Value A doesn't equal to B");
console.asserts(valA === valB);
console.asserts(5 === 5, "5 equals to 5");
console.asserts(5 === 5);
console.asserts(5 > 6, "5 is not greater than 6");
//console.group() examples.
console.group("Group 1");
console.log("Sentence 1");
console.log("Sentence 2");
console.group("Group 2");
console.log("Sentence 3");
console.log("Sentence 4");
console.groupCollapsed("Group 3");
console.log("Sentence 5");
console.log("Sentence 6");
console.groupEnd();
console.log("Sentence 7");
console.groupEnd();
console.log("Sentence 8");
console.groupEnd();
console.log("Sentence 9");
//console.time(),console.timeEnd() examples
console.time('Timer1');
// Do some process
sleep(1000);
console.timeEnd('Timer1');
// use console.clear() to clean Debug Window logs
// console.clear();
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}

View file

@ -36,10 +36,4 @@ function main() {
Uuid.print('[Uuid.print]', Uuid.fromString(Uuid.toString(0)));
}
console.info('[console.info] hello world');
console.log('[console.log] hello world');
console.debug('[console.Debug] hello world');
console.warn('[console.Warn] hello world');
console.error('[console.error] hello world');
console.exception('[console.exception] hello world');
}