diff options
author | David Clark <david.a.clark@nokia.com> | 2010-11-18 16:20:48 +1000 |
---|---|---|
committer | David Clark <david.a.clark@nokia.com> | 2010-11-18 16:20:48 +1000 |
commit | c223232bc15106750da632598047a35ad3762723 (patch) | |
tree | 403f7aa2c3a5a912edce6feae869046c89d29178 /old/interpreter | |
parent | b984b0b62076067f1f75db5a7eda5aaa2cdaad2a (diff) |
Diffstat (limited to 'old/interpreter')
-rw-r--r-- | old/interpreter/DESCRIPTION | 1 | ||||
-rw-r--r-- | old/interpreter/builtins.js | 596 | ||||
-rw-r--r-- | old/interpreter/config.js | 72 | ||||
-rw-r--r-- | old/interpreter/interpreter.pro | 60 | ||||
-rw-r--r-- | old/interpreter/main.cpp | 59 | ||||
-rw-r--r-- | old/interpreter/qscriptsystemtest.cpp | 838 | ||||
-rw-r--r-- | old/interpreter/qscriptsystemtest.h | 114 | ||||
-rw-r--r-- | old/interpreter/qtscript_bindings.cpp | 313 | ||||
-rw-r--r-- | old/interpreter/qtscript_bindings.h | 83 | ||||
-rw-r--r-- | old/interpreter/qtuitestengineagent.cpp | 70 | ||||
-rw-r--r-- | old/interpreter/qtuitestengineagent.h | 63 | ||||
-rw-r--r-- | old/interpreter/scriptpreprocessor.cpp | 256 | ||||
-rw-r--r-- | old/interpreter/scriptpreprocessor.h | 58 | ||||
-rw-r--r-- | old/interpreter/scripts.qrc | 6 |
14 files changed, 2589 insertions, 0 deletions
diff --git a/old/interpreter/DESCRIPTION b/old/interpreter/DESCRIPTION new file mode 100644 index 0000000..560c597 --- /dev/null +++ b/old/interpreter/DESCRIPTION @@ -0,0 +1 @@ +Interpreter for QtUitest scripts. diff --git a/old/interpreter/builtins.js b/old/interpreter/builtins.js new file mode 100644 index 0000000..e8f6e10 --- /dev/null +++ b/old/interpreter/builtins.js @@ -0,0 +1,596 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + "Builtin" functions available to all tests. + This file gets evaluated once at the beginning of every test. + + Documentation for these functions should be placed in qsystemtest.cpp. + + Note, if a function exists with the same name in both here and QSystemTest, + the QtScript version takes precedence. To force a call to the QSystemTest + function, use ParentTestObject[signature], e.g. see expectMessageBox. + + Note that the exported function needs to be a public slot, AND have at least one + parameter. See: runAsManualTest(void) +*/ + +/* called before every init() */ +function init_global() +{ +} + +/* called after every cleanup() */ +function cleanup_global() +{ +} + +/* called before every initTestCase() */ +function initTestCase_global() +{ +} + +/* called after every cleanupTestCase() */ +function cleanupTestCase_global() +{ + killApplication(); +} + +function verify() { + if (arguments.length == 0) { + fail( "You need to specify at least a boolean expression" ); + return false; + } + var msg = ""; + if (arguments.length > 1) { msg = arguments[1] }; + + if (ParentTestObject['runAsManualTest(void)']()) { + var description = "verify that MAGIC_DATA"; + if (msg.length > 0) description = "MAGIC_COMMAND:" + msg; + ParentTestObject['manualTest(QString)'](description); + return true; + } + + return ParentTestObject['verify(bool,QString)'](arguments[0],msg); +} + +function print() { + var str = ""; + for (var i = 0; i < arguments.length; ++i) { + if (arguments[i] != undefined) str = str + arguments[i]; + } + ParentTestObject['print(QVariant)'](str); +} + +/* Expect conditionFunction to return true after executing code, or fail with message */ +function _expect(conditionFunction, message) { + if (message == undefined) { + message = "Expected condition did not become true"; + } + this.__defineSetter__("code", function(code){ + try { + code.apply(this); + } catch (e) { + if (this.onFail != undefined) { + this.onFail.apply(this); + } + throw e; + } + if (!conditionFunction.call()) { + fail(message); + } + }); +} + +function expect(a,b) { + return new _expect(a,b); +} + +function select( item, queryPath ) { + if (item != undefined) { + if (queryPath == undefined) queryPath = ""; + ParentTestObject['select(QString,QString)'](item, queryPath); + } + return true; +} + +function selectIndex( idx, queryPath ) { + if (idx instanceof Array) { + ParentTestObject['selectIndex(QVariantList,QString)'](idx, queryPath); + } else { + ParentTestObject['selectIndex(QVariantList,QString)']([idx, 0], queryPath); + } +} + +function skip(text, mode) { + ParentTestObject['skip(QString,QSystemTest::SkipMode)'](text, mode); +} + +/* + Wait for code to return true for up to timeout ms, with given number of intervals, + and message on failure +*/ +function _waitFor(timeout, intervals, message) { + if (timeout == undefined) { + timeout = 10000; + } + if (intervals == undefined) { + intervals = 20; + } + if (message == undefined) { + message = "Waited-for condition did not become true after " + timeout + " milliseconds"; + } + this.__defineSetter__("code", function(code){ + ok = code.apply(this); + i = 0; + while (!ok && i < intervals) { + wait(timeout/intervals); + ok = code.apply(this); + i = i + 1; + } + if (!ok) { + fail(message); + } + }); +} +function waitFor(a,b,c) { + return new _waitFor(a,b,c); +} + +Array.prototype.isEmpty = function() +{ + return (this.length <= 0); +} + +Array.prototype.contains = function() +{ + obj = arguments[0]; + + if (ParentTestObject['runAsManualTest(void)']()) { + var description = " contains " + obj; + ParentTestObject['manualTestData(QString)'](description); + return true; + } + + for (var i=0; i<this.length; i++) { + if (this[i] == obj) { + return true; + } + } + return false; +} + +// Ensure we don't get the 'contains' function when using for-in on arrays +setFlags(Array.prototype, "contains", QScriptValue.SkipInEnumeration); + +Array.prototype.containsOne = function() +{ + obj = arguments[0]; + var count = 0; + for (var i=0; i<this.length; i++) { + if (this[i] == obj) { + count++; + } + } + return count == 1; +} +// Ensure we don't get the 'containsOne' function when using for-in on arrays +setFlags(Array.prototype, "containsOne", QScriptValue.SkipInEnumeration); + +String.prototype.contains = function() +{ + var obj = arguments[0]; + if (arguments[1] != undefined) { + obj = arguments[1]; + } + + if (obj instanceof Date) { + var strs = []; + strs.push(obj.toString("ddd, M/d/yy")); + strs.push(obj.toString("dddd, MMMM d, yyyy")); + strs.push(obj.toString("ddd, d MMM")); + strs.push(obj.toString("dd/MM/yyyy")); + + if (ParentTestObject['runAsManualTest(void)']()) { + var description = " contains date " + strs[i]; + ParentTestObject['manualTestData(QString)'](description); + return true; + } + + for (var i = 0; i < strs.length; ++i) { + if (this.toString().contains(strs[i])) return true; + } + return false; + } else if (obj instanceof RegExp) { + if (ParentTestObject['runAsManualTest(void)']()) { + var description = " contains " + obj; + ParentTestObject['manualTestData(QString)'](description); + return true; + } + + return obj.test(this.toString()); + } else { + if (ParentTestObject['runAsManualTest(void)']()) { + var description = " contains " + obj; + ParentTestObject['manualTestData(QString)'](description); + return true; + } + + var ref = this.toString(); + return (-1 != ref.indexOf(obj)); + } +} + +String.prototype.toDate = function() +{ + var obj = this.toString(); + return toDate(obj); +} + +String.prototype.startsWith = function() +{ + var obj = arguments[0]; + var ref = this.toString(); + return ref.indexOf( obj ) == 0; +} + +String.prototype.left = function() +{ + var start = 0; + var end = arguments[0]; + var ref = this.toString(); + return ref.slice( start, end ); +} + +String.prototype.right = function() +{ + var ref = this.toString(); + var start = ref.length - arguments[0]; + return ref.slice( start ); +} + +String.prototype.mid = function() +{ + var start = arguments[0]; + var end = arguments[1]+1; + var ref = this.toString(); + return ref.slice( start, end ); +} + +String.prototype.toValue = function() +{ + var ret = 0; + var ref = this.toString(); + for (var i=0; i<ref.length; i++) { + var ch = ref.charAt(i); + if (ch == '-') { + ret = ret * -1; + } else { + ret = ret * 10; + if (ch == '0') { + } else if (ch == '1') { + ret = ret + 1; + } else if (ch == '2') { + ret = ret + 2; + } else if (ch == '3') { + ret = ret + 3; + } else if (ch == '4') { + ret = ret + 4; + } else if (ch == '5') { + ret = ret + 5; + } else if (ch == '6') { + ret = ret + 6; + } else if (ch == '7') { + ret = ret + 7; + } else if (ch == '8') { + ret = ret + 8; + } else if (ch == '9') { + ret = ret + 9; + } else { + fail( "ERROR: Couldn't convert '" + ref + "' into a value." ); + return undefined; + } + } + } + return ret; +} + +function anyDefined() +{ + for (var i = 0; i < arguments.length; ++i) { + if (arguments[i] != undefined) return true; + } + return false; +} + +function tabBar(index) +{ + if (index == undefined) { + index = 0; + } + + if (ParentTestObject['runAsManualTest(void)']()) { + if (index == 0) { + // In 99% of the cases there will be only one tabbar in an app + // so we can get something like: + // select 'foobar' from 'tabBar' + return "tabBar"; + } else { + // In the remaining 1% we get something like: + // select 'foobar' from '2nd tabBar' + return (index+1) + "nd tabBar"; + } + } + + var tabBars = findByProperty( { inherits: "QTabBar" } ); + if (tabBars[index] == undefined) { + fail( "ERROR: Tab widget not found." ); + return undefined; + } + return tabBars[index]; +} + +function menuBar(index) +{ + if (index == undefined) { + index = 0; + } + + if (ParentTestObject['runAsManualTest(void)']()) { + if (index == 0) { + // In 99% of the cases there will be only one menubar in an app + // so we can get something like: + // select 'foobar' from 'menuBar' + return "menuBar"; + } else { + // In the remaining 1% we get something like: + // select 'foobar' from '2nd menuBar' + return (index+1) + "nd menuBar"; + } + } + + + var menuBars = findByProperty( { inherits: "QMenuBar" } ); + if (menuBars[index] == undefined) { + fail( "ERROR: MenuBar widget not found." ); + return undefined; + } + return menuBars[index]; +} + +// Convert twiki markup to HTML for display in prompts +function twiki(text) { + var lines = text.split("\n"); + var result = []; + var listType = []; + var currLevel = 0; + var rowNumber = 0; + var inListItem = false; + var inTable = false; + var inVerbatim = false; + var liRegExp = /((?: )+)(\*|(?:[1AaIi]).)\s*(.*)/; + var liContRegExp = /(?: )\s*(.*)/; + var tableRegExp = /\s*\|(\s*)(.*)(\s*)\|\s*$/; + + for (var i=0; i < lines.length; i++) { + var line = lines[i]; + + // Verbatim + if (inVerbatim) { + if (line.match(/\<\s*\/\s*verbatim\s*\>/)) { + inVerbatim = false; + result.push("</pre>"); + } else { + result.push(line); + } + continue; + } else if (line.match(/\<\s*verbatim\s*\>/)) { + inVerbatim = true; + result.push("<pre>"); + continue; + } + + // Simple replacements + // Headings + line = line.replace(/---\+(?:!!)? +(.*)/, "<h1>$1</h1>"); + line = line.replace(/---\+\+(?:!!)? +(.*)/, "<h2>$1</h2>"); + line = line.replace(/---\+\+\+(?:!!)? +(.*)/, "<h3>$1</h3>"); + line = line.replace(/---\+\+\+\+(?:!!)? +(.*)/, "<h4>$1</h4>"); + line = line.replace(/---\+\+\+\+\+(?:!!)? +(.*)/, "<h5>$1</h5>"); + line = line.replace(/---\+\+\+\+\+\+(?:!!)? +(.*)/, "<h6>$1</h6>"); + + // Misc + line = line.replace(/---[-]*/g, "<hr />"); + line = line.replace(/^$/, "<p />"); + line = line.replace(/!([A-Z]\w*)/g, "$1"); + line = line.replace(/%TOC%/g, ""); + line = line.replace(/%BB%/g, "<br />•"); + + // Links and anchors + line = line.replace(/^\#([A-Z]\w*)/, "<a name=\"$1\"></a><p />"); + line = line.replace(/\[\[\#([A-Z]\w*)\]\[(.*)\]\]/g, "<a href=\"#$1\">$2</a>"); + line = line.replace(/\[\[\#([A-Z]\w*)\]\]/g, "<a href=\"#$1\">#$1</a>"); + +// line = line.replace(/\[\[(.*?)\]\[(.*?)\]\]/g, "<a href=\"$1\">$2</a>"); +// line = line.replace(/\[\[(.*?)\]\]/g, "<a href=\"$1\">$1</a>"); + line = line.replace(/\[\[(.*?)\]\[(.*?)\]\]/g, "$2"); + line = line.replace(/\[\[(.*?)\]\]/g, "$1"); + + // Text style + line = line.replace(/^\*(\S.*?\S)\*(?!\w)/, "<b>$1</b>"); + line = line.replace(/([^\w])\*(\S.*?\S)\*(?!\w)/g, "$1<b>$2</b>"); + line = line.replace(/^__(.*?\S)__(?!\w)/g, "<b><i>$2</i></b>"); + line = line.replace(/([^\w])__(.*?\S)__(?!\w)/g, "$1<b><i>$2</i></b>"); + line = line.replace(/^_([^_]*?\S)_(?!\w)/, "<i>$1</i>"); + line = line.replace(/([^\w])_([^_]*?\S)_(?!\w)/g, "$1<i>$2</i>"); + line = line.replace(/^==(.*?\S)==(?!\w)/g, "<b><code>$1</code></b>"); + line = line.replace(/([^\w])==(.*?\S)==(?!\w)/g, "$1<b><code>$2</code></b>"); + line = line.replace(/^=(.*?\S)=(?!\w)/, "<code>$1</code>"); + line = line.replace(/([^\w])=(.*?\S)=(?!\w)/g, "$1<code>$2</code>"); + line = line.replace(/%(.*)%(.*)%ENDCOLOR%/g, "<font color=\"$1\">$2<\/font>"); + + // Tables + tableMatch = tableRegExp.exec(line); + if (tableMatch) { + if (inTable) { + rowNumber++; + if (rowNumber%2) { + result.push("</tr>\n<tr bgcolor=\"#f2f2f2\">"); + } else { + result.push("</tr>\n<tr bgcolor=\"#e0e0e0\">"); + } + } else { + inTable = true; + rowNumber = 0; + result.push("<table>\n<tr bgcolor=\"#e0e0e0\">"); + } + + var cellRegExp = /\|(\s*)(\<b\>)?(.*?)(\<\/b\>)?(\s*)(?=\|)/g; + var trLine = ""; + while (cell = cellRegExp.exec(line)) { + var left = cell[1].length; + var right = cell[5].length; + var align = "left"; + if (left > 1 && left == right) { + align = "center"; + } else if (left > right) { + align = "right"; + } + if (cell[2] && cell[4]) { + trLine += "<th bgcolor=\"#404040\"><font color=\"white\">" + cell[3] + "</font></th>"; + } else { + trLine += "<td align=\"" + align + "\">" + cell[3] + "</td>"; + } + } + line = trLine; + result.push(line); + continue; + } else { + if (inTable) { + result.push("</tr></table>"); + inTable = false; + } + } + + // List Items + liMatch = liRegExp.exec(line); + level = (liMatch ? liMatch[1].length/3 : 0); + liType = (level ? (liMatch[2] == "*" ? "ul" : "ol") : null); + + // List item continuation + if (inListItem) { + if (!liMatch) { + liContMatch = liContRegExp.exec(line); + if (!liContMatch) { + inListItem = false; + result.push("</li>"); + } else { + line = liContMatch[1]; + level = currLevel; + } + } else { + inListItem = false; + result.push("</li>"); + } + } + + // Change of list indentation level + if (level > currLevel) { + for (var j = currLevel; j < level; j++) { + result.push("<" + liType + (liType == "ol" ? " type=\"" + liMatch[2][0] + "\"" : "") + ">"); + listType.push(liType); + } + } else { + for (var j = level; j < currLevel; j++) { + prevLiType = listType.pop(); + result.push("</" + prevLiType + ">"); + } + if (liMatch && listType.length && listType[listType.length - 1] != liType) { + // Change of list item type (from unordered to ordered, or vice-versa) + result.push("</" + listType.pop() + ">"); + result.push("<" + liType + (liType == "ol" ? " type=\"" + liMatch[2][0] + "\"" : "") + ">"); + listType.push(liType); + } + } + currLevel = level; + + if (liMatch) { + line = line.replace(liRegExp, "<li" + (liType == "ol" ? " type=\"" + liMatch[2][0] + "\"" : "") + ">" + liMatch[3]); + inListItem = true; + } + + result.push(line); + } + + return(result.join("\n")); +} + +// Support object-oriented syntax for widget interaction +String.prototype.focusWidget = function() { return focusWidget(this); } +String.prototype.getSelectedText = function() { return getSelectedText(this); } +String.prototype.getText = function() { return getText(this); } +String.prototype.getSelectedValue = function() { return getSelectedValue(this); } +String.prototype.getValue = function() { return getValue(this); } +String.prototype.getList = function() { return getList(this); } +String.prototype.getLabels = function() { return getLabels(this); } +String.prototype.currentTitle = function() { return currentTitle(this); } +String.prototype.getGeometry = function() { return getGeometry(this); } +String.prototype.setProperty = function(name, value) { setProperty(this, name, value); } +String.prototype.getProperty = function(name) { return getProperty(this, name); } +String.prototype.keyPress = function(key) { keyPress(key, this); } +String.prototype.keyRelease = function(key) { keyRelease(key, this); } +String.prototype.keyClick = function(key) { keyClick(key, this); } +String.prototype.keyClickHold = function(key, duration) { keyClickHold(key, duration, this); } +String.prototype.mouseClick = function() { mouseClick(this); } +String.prototype.mouseClickHold = function(duration) { mouseClickHold(this, duration); } +String.prototype.mousePress = function() { mousePress(this); } +String.prototype.mouseRelease = function() { mouseRelease(this); } +String.prototype.enter = function(value, mode) { enter(value, this, mode) } +String.prototype.select = function(item) { select(item, this); } +String.prototype.selectIndex = function(idx) { selectIndex(idx, this); } +String.prototype.activate = function() { activate(this); } +String.prototype.isVisible = function() { return isVisible(this); } +String.prototype.isEnabled = function() { return isEnabled(this); } +String.prototype.isChecked = function() { return isChecked(this); } +String.prototype.setChecked = function(doCheck) { setChecked(doCheck, this); } +String.prototype.checkState = function() { return checkState(this); } +String.prototype.setCheckState = function(state) { setCheckState(state, this); } +String.prototype.saveScreen = function(name) { saveScreen(name, this); } diff --git a/old/interpreter/config.js b/old/interpreter/config.js new file mode 100644 index 0000000..8679467 --- /dev/null +++ b/old/interpreter/config.js @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* Internal configuration for QtUiTest. */ + +/* Preprocessor configuration. */ +preprocess = { + /* A list of mappings from function names, to strings which should + * be appended after the function. */ + functionAppends: {} +} + +/* A list of files containing "builtin" functions to be made accessible + * to all tests. */ +builtin_files = [ "builtins.js" ]; + +/* code_setters: a list of special functions which have syntax like: + * myFunction(a,b,c) { + * some code; + * } + * This is not valid ecmascript. In reality, the code expands to: + * myFunction,a,b,c).code = function() { + * some code; + * } + * Where a setter is defined for property "code" which makes the magic + * happen. The functions are implemented in builtins.js. + */ +code_setters = [ + "waitFor", + "expect" +] +for (var i = 0; i < code_setters.length; ++i) + preprocess.functionAppends[code_setters[i]] = ".code = function() "; + diff --git a/old/interpreter/interpreter.pro b/old/interpreter/interpreter.pro new file mode 100644 index 0000000..0c9705f --- /dev/null +++ b/old/interpreter/interpreter.pro @@ -0,0 +1,60 @@ +SOURCES +=\ + main.cpp \ + qscriptsystemtest.cpp \ + qtscript_bindings.cpp \ + qtuitestengineagent.cpp \ + scriptpreprocessor.cpp + +HEADERS +=\ + qscriptsystemtest.h \ + scriptpreprocessor.h \ + qtuitestengineagent.h \ + qtscript_bindings.h + +RESOURCES += scripts.qrc +DEFINES += QTUITESTRUNNER_TARGET +TEMPLATE=app +VPATH+=$$PWD +INCLUDEPATH+=$$PWD +INCLUDEPATH+=$$SRCROOT +INCLUDEPATH+=$$SRCROOT/libqsystemtest +QT+=script network +CONFIG+=qtestlib +TARGET=qtuitestrunner + +!symbian { + MOC_DIR=$$OUT_PWD/.moc + OBJECTS_DIR=$$OUT_PWD/.obj + DESTDIR=$$BUILDROOT/bin + target.path=$$[QT_INSTALL_BINS] + INSTALLS+=target +} + +#symbian { +# TARGET.EPOCALLOWDLLDATA=1 +# TARGET.CAPABILITY += AllFiles ReadDeviceData ReadUserData SwEvent WriteUserData +# MOC_DIR=$$OUT_PWD/moc +# OBJECTS_DIR=$$OUT_PWD/obj +# LIBS += -L$$OUT_PWD -lqsystemtest +#} + +win32 { + CONFIG(debug,debug|release): LIBS+=-L$$BUILDROOT/libqsystemtest/debug -lqsystemtestd + CONFIG(release,debug|release):LIBS+=-L$$BUILDROOT/libqsystemtest/release -lqsystemtest + target.path=$$[QT_INSTALL_BINS] + INSTALLS+=target + !equals(QMAKE_CXX, "g++") { + DEFINES+=strcasecmp=_stricmp + } +} + +!win32:!symbian:!mac { + LIBS += -L$$BUILDROOT/lib -lqsystemtest -lBotan -lCore +} + +mac { + CONFIG-=app_bundle + CONFIG(debug,debug|release): LIBS += -L$$BUILDROOT/lib -lqsystemtest_debug + CONFIG(release,debug|release): LIBS += -L$$BUILDROOT/lib -lqsystemtest +} + diff --git a/old/interpreter/main.cpp b/old/interpreter/main.cpp new file mode 100644 index 0000000..3867727 --- /dev/null +++ b/old/interpreter/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptsystemtest.h" +#include <QApplication> + +int main(int argc, char *argv[]) +{ + bool guiEnabled = true; + + // If in auto mode, don't need a GUI connection. + for (int i = 1; i < argc; ++i) { + if (!strcasecmp(argv[i], "-auto")) { + guiEnabled = false; + } + } + + QApplication app(argc, argv, guiEnabled); + QScriptSystemTest tc; + return tc.exec(argc, argv); +} diff --git a/old/interpreter/qscriptsystemtest.cpp b/old/interpreter/qscriptsystemtest.cpp new file mode 100644 index 0000000..641bfb4 --- /dev/null +++ b/old/interpreter/qscriptsystemtest.cpp @@ -0,0 +1,838 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptsystemtest.h" +#include "scriptpreprocessor.h" + +#include <QScriptEngine> +#include <QScriptValue> +#include <QScriptValueIterator> +#include <QScriptContextInfo> + +//#include <qtestprotocol_p.h> +#include <qtestide.h> +#include <QtTest/QtTest> + +#include "qtscript_bindings.h" +#include "qtuitestengineagent.h" + +#include <QDebug> + +QStringList builtins; + +Q_DECLARE_METATYPE(QVariant) +Q_DECLARE_METATYPE(QList<qint64>) + +template <typename Tp> +QScriptValue qScriptValueFromQObject(QScriptEngine *engine, Tp const &qobject) +{ + return engine->newQObject(qobject, QScriptEngine::AutoOwnership); +} + +template <typename Tp> +void qScriptValueToQObject(const QScriptValue &value, Tp &qobject) +{ qobject = qobject_cast<Tp>(value.toQObject()); +} + +template <typename Tp> +int qScriptRegisterQObjectMetaType( + QScriptEngine *engine, + const QScriptValue &prototype = QScriptValue(), + Tp * /* dummy */ = 0 + ) +{ + return qScriptRegisterMetaType<Tp>(engine, qScriptValueFromQObject, + qScriptValueToQObject, prototype); +} + +void setupEnums(QScriptEngine *engine) { + QScriptValue qsvObject = engine->newObject(); +#define S(val) qsvObject.setProperty(#val, engine->toScriptValue((int)QScriptValue::val)); + S(PropertySetter);S(ReadOnly);S(Undeletable);S(SkipInEnumeration); + S(PropertyGetter);S(QObjectMember);S(KeepExistingFlags);S(UserRange); +#undef S + engine->globalObject().setProperty("QScriptValue", qsvObject ); +} + +static QScriptValue dateToString(QScriptContext *ctx, QScriptEngine *eng) +{ + QDateTime dt = ctx->thisObject().toDateTime(); + QString fmt = ctx->argument(0).toString(); + + return eng->toScriptValue(dt.toString(fmt)); +} + +static QString findIncludeScript(const QString& name) +{ + QList<QDir> includeDirs; + foreach (QByteArray const& split, qgetenv("QTUITEST_INCLUDE_PATH").split(':')) { + if (split.isEmpty()) continue; + QDir dir(split); + if (dir.exists()) includeDirs << dir; + } + + foreach (QDir const& dir, includeDirs) { + QString file = dir.canonicalPath() + "/" + name; + if (QFile::exists(file)) { + return file; + } + } + + return QString(); +} + +// Include a single script into an engine. +static QScriptValue includeScriptFunction + (QScriptEngine *engine, const QString& name) +{ + // Find the script, relative to the entry script. + QString script; + QtScript::getLocation(engine->currentContext(), &script, 0); + QString directory = QFileInfo(QFileInfo(script).canonicalFilePath()).dir().canonicalPath(); + QDir dir(directory); + QFileInfo file(dir, name); + QString filename = name; + if (file.exists()) filename = file.canonicalFilePath(); + + // Check if the script has already been loaded. + static const char includesProperty[] = "_qtuitest_includes"; + QStringList included = qscriptvalue_cast<QStringList>(engine->globalObject().property(includesProperty)); + if (included.contains(filename)) { + return QScriptValue(); + } + + // Try to load the script into memory. + QFile scriptFile(filename); + if (!scriptFile.exists() || !scriptFile.open(QIODevice::ReadOnly)) { + int pos = directory.indexOf("/interpreter"); + if (pos > 0) { + directory = directory.left(pos) + "/tests/shared"; + QDir dir(directory); + QFileInfo file(dir, name); + filename = file.filePath(); + scriptFile.setFileName(filename); + } else { + scriptFile.setFileName(findIncludeScript(filename)); + } + } + + if (!scriptFile.exists() || (!scriptFile.isOpen() && !scriptFile.open(QIODevice::ReadOnly))) { + return engine->currentContext()->throwError("Could not find " + name + " in either the shared or testcase directory"); + } + + QString contents = QTextStream(&scriptFile).readAll(); + scriptFile.close(); + + contents.prepend("with(ParentTestMetaObject) {"); + contents.append("\n}"); + + ScriptPreprocessor().preprocess(contents); + + // Evaluate the contents of the script. + engine->pushContext()->setActivationObject(engine->globalObject()); + // Note that we have included this script. + if (!engine->globalObject().property(includesProperty).isValid()) { + engine->globalObject().setProperty(includesProperty, engine->newArray()); + } + engine->globalObject().property(includesProperty).setProperty( + engine->globalObject().property(includesProperty).property("length").toUInt32(), + qScriptValueFromValue(engine, filename)); + QScriptValue ret = engine->evaluate(contents, filename); + engine->popContext(); + + return ret; +} + +// Implementation of the "include()" function in scripts, which imports scripts. +static QScriptValue includeFunction + (QScriptContext *context, QScriptEngine *engine) +{ + QScriptValue value; + if (context->argumentCount() == 0) { + return context->throwError + ("script name must be supplied to include()"); + } + value = context->argument(0); + if (!value.isString()) { + return context->throwError + ("invalid script name supplied to include()"); + } + + QString name = value.toString(); + if (name.contains(QChar('*'))) { + //FIXME: This path doesn't look in the tests/shared/ directory. + // Expand the wildcard and include all matching scripts. + QString script; + QtScript::getLocation(context, &script, 0); + QDir dir(QFileInfo(script).dir()); + QStringList files = dir.entryList(QStringList(name)); + foreach (QString filename, files) { + value = includeScriptFunction(engine, dir.filePath(filename)); + if (engine->hasUncaughtException()) + return value; + } + return engine->undefinedValue(); + } else { + // Include a single script file. + return includeScriptFunction(engine, name); + } +} + +static QScriptValue setFlags + (QScriptContext *context, QScriptEngine* /*engine*/) +{ + Q_ASSERT(context); + if (context->argumentCount() != 3) { + return context->throwError + ("setFlags() needs three arguments"); + } + QScriptValue o = context->argument(0); + if (!o.isObject()) return QScriptValue(); + QString name = context->argument(1).toString(); + int flags = context->argument(2).toInt32(); + + o.setProperty(name, o.property(name), QFlag(flags)); + return QScriptValue(); +} + + +QScriptSystemTest::QScriptSystemTest() + : m_engine(0), + m_agent(0), + m_contextDepth(0), + testObject(0), + m_checkOnly(false) +{ +} + +QScriptSystemTest::~QScriptSystemTest() +{ +} + +void QScriptSystemTest::initEngine(bool createNewEngine) +{ + if (m_engine) { + if (createNewEngine) { + delete m_engine; + } else { + return; + } + } + + m_engine = new QScriptEngine(); + m_agent = new QtUiTestEngineAgent(m_engine, this); + + // Ensure we process events periodically. + m_engine->setProcessEventsInterval(100); + + m_engine->importExtension("qt.core"); + setupEnums(m_engine); + + // include() imports scripts directly into the parent script. + m_engine->globalObject().setProperty + ("include", m_engine->newFunction(includeFunction, 1)); + m_engine->globalObject().setProperty + ("setFlags", m_engine->newFunction(setFlags, 3)); + + + m_engine->globalObject().setProperty("_dateToString", m_engine->newFunction(dateToString)); + m_engine->evaluate("_old_date_toString = Date.prototype.toString;" + "Date.prototype.toString = function() {" + " if (arguments[0] == undefined)" + " return _old_date_toString.apply(this, arguments);" + " return _dateToString.apply(this, arguments);" + "}"); + + m_engine->globalObject().setProperty("ParentTestObject", m_engine->newQObject(this)); + m_engine->globalObject().setProperty("ParentTestMetaObject", m_engine->newQMetaObject(metaObject())); + + loadBuiltins(m_engine); + importIntoGlobalNamespace(m_engine, "ParentTestObject"); +} + +QString QScriptSystemTest::testCaseName() const +{ + if (testObject) + return testObject->metaObject()->className(); + return QAbstractTest::testCaseName(); +} + +void QScriptSystemTest::loadBuiltins(QScriptEngine *engine) +{ + QScriptEngine configEngine; + QScriptSystemTest::loadInternalScript("config.js", &configEngine); + for (int i = 0; i < configEngine.globalObject().property("builtin_files").property("length").toInt32(); ++i) { + QString file = configEngine.globalObject().property("builtin_files").property(i).toString(); + QtScript::addInternalFile( QScriptSystemTest::loadInternalScript(file, engine, true) ); + } +} + +void QScriptSystemTest::importIntoGlobalNamespace(QScriptEngine *engine, QString const& object) +{ + QScriptValue obj = engine->globalObject().property(object); + + QScriptValueIterator it(obj); + while (it.hasNext()) { + it.next(); + QString name = it.name(); + + // Transform name of enter(QString,QString) to enter + if (it.value().isFunction() && name.contains('(')) { + name = name.left(name.indexOf('(')); + } + + // Import this property into the global object iff one doesn't already + // exist with this name + if (engine->globalObject().property(name).isValid()) continue; + + // For functions, to keep the QObject slot resolution working right, we + // must wrap the property instead of simply copying it. + if (it.value().isFunction()) { + engine->evaluate(QString("%1 = function(){ return %2.%1.apply(this, arguments); };") + .arg(name) + .arg(object)); + } else { + engine->globalObject().setProperty(name, it.value()); + } + } +} + +QString QScriptSystemTest::loadInternalScript(QString const &name, QScriptEngine *engine, bool withParentObject) +{ + QString filename = QFileInfo(QString::fromAscii(__FILE__)).absolutePath() + "/" + name; + if (!QFileInfo(filename).exists()) filename = ":/" + name; + QFile f(filename); + QString data; + if (!f.open(QIODevice::ReadOnly) || (data = QTextStream(&f).readAll()).isEmpty()) { + qWarning("QtUiTest: Couldn't load config file '%s' (using '%s')", qPrintable(name), qPrintable(filename)); + return QString(); + } + + if (withParentObject) { + data.prepend("with(ParentTestMetaObject) {"); + data.append("\n}"); + } + + QScriptValue e = engine->evaluate(data, filename); + if (e.isError()) { + QString backtrace = engine->uncaughtExceptionBacktrace().join("\n"); + qWarning("In QtUiTest config file %s:\n%s\n%s", qPrintable(filename), qPrintable(e.toString()), + qPrintable(backtrace)); + } + builtins << filename; + return filename; +} + +QScriptValue variantToScriptValue(QScriptEngine *engine, const QVariant &v) +{ + QScriptValue ret; + if (v.isNull()) { + ret = QScriptValue( engine, QScriptValue::NullValue ); + } else if (v.canConvert<QStringList>()) { + ret = engine->toScriptValue(v.value<QStringList>()); + } else if (v.canConvert<qreal>()) { + ret = engine->toScriptValue(v.value<qreal>()); + } else if (v.canConvert<int>()) { + ret = engine->toScriptValue(v.value<int>()); + } else if (v.canConvert<QString>()) { + ret = engine->toScriptValue(v.value<QString>()); + } else { + ret = engine->newVariant(v); + } + return ret; +} + +void variantFromScriptValue(const QScriptValue &obj, QVariant &v) +{ + v = obj.toVariant(); +} + +QString QScriptSystemTest::currentFile() +{ + QString fileName = QString(); + int lineNumber = 0; + + QtScript::getLocation(m_engine->currentContext(), &fileName, &lineNumber); + + return fileName; +} + +int QScriptSystemTest::currentLine() +{ + QString fileName = QString(); + int lineNumber = 0; + + QtScript::getLocation(m_engine->currentContext(), &fileName, &lineNumber); + + return lineNumber; +} + +void QScriptSystemTest::outputBacktrace() +{ + QScriptContext *ctx = m_engine->currentContext(); + QString bt("Backtrace:"); + while (ctx) { + QScriptContextInfo ctxInfo(ctx); + QString fn = ctxInfo.fileName(); + int ln = ctxInfo.lineNumber(); + if (!fn.isEmpty() && ln != -1) + bt += "\n " + fn + "(" + QString::number(ln) + ")"; + ctx = ctx->parentContext(); + } + QDebug(QtDebugMsg) << qPrintable(bt); +} + +void QScriptSystemTest::skip(QString const &message, QSystemTest::SkipMode mode) +{ + QSystemTest::skip(message, mode); + m_engine->evaluate("throw new QTestFailure('QSKIP');"); +} + +bool QScriptSystemTest::fail(QString const &message) +{ + bool ret = QSystemTest::fail( message ); + if (!ret) { + outputBacktrace(); + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + } + return ret; +} + +void QScriptSystemTest::verify(bool statement, QString const &message) +{ + if (!QTest::qVerify(statement, "<statement>", qPrintable(message), qPrintable(currentFile()), currentLine() )) { + outputBacktrace(); + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + } +} + +void QScriptSystemTest::compare(const QString &actual, const QString &expected) +{ + if (QSystemTest::runAsManualTest()) { + QString act; + if (actual == "MAGIC_DATA" || actual.contains("'")) act = actual; + else act = "'" + actual + "'"; + QString exp; + if (expected == "MAGIC_DATA" || expected.contains("'")) exp = expected; + else exp = "'" + expected + "'"; + manualTest( QString("verify that %1 is equal to %2").arg(act).arg(exp)); + return; + } + + if (!QTest::qCompare( actual, expected, qPrintable(actual), qPrintable(expected), qPrintable(currentFile()), currentLine() )) { + outputBacktrace(); + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + } +} + +void QScriptSystemTest::compare(const QStringList &actual, const QStringList &expected) +{ + if (QSystemTest::runAsManualTest()) { + QString act = actual.count() > 0 ? actual[0] : ""; + QString exp = expected.count() > 0 ? expected[0] : ""; + manualTest( QString("verify that %1 is equal to %2").arg(act).arg(exp)); + return; + } + + if (!QTest::qCompare( actual.count(), expected.count(), qPrintable(actual.join("\n")), qPrintable(expected.join("\n")), qPrintable(currentFile()), currentLine() )) { + outputBacktrace(); + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + return; + } + + for (int i=0; i<actual.count(); i++) { + if (!QTest::qCompare( actual[i], expected[i], qPrintable(actual[i]), qPrintable(expected[i]), qPrintable(currentFile()), currentLine() )) { + outputBacktrace(); + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + } + } +} + +bool QScriptSystemTest::isFailExpected() +{ + return (expect_fail_function == currentTestFunction() && expect_fail_datatag == currentDataTag()); +} + +void QScriptSystemTest::expectFail( const QString &reason ) +{ + expect_fail_function = currentTestFunction(); + expect_fail_datatag = currentDataTag(); + expect_fail_reason = reason; + int line = currentLine(); + + if (testObject) { + testObject->setProperty("expectFailLineNumber", line); + } + + bool ok = QTest::qExpectFail(currentDataTag().toLatin1(), reason.toLatin1(), + QTest::TestFailMode(1),//mode), + qPrintable(currentFile()), line); + if (!ok) + m_engine->evaluate("throw new QTestFailure('QFAIL');"); +} + +bool QScriptSystemTest::setQueryError( const QTestMessage &message ) +{ + QString errorString = message["status"].toString(); + QVariant response = message["_q_inResponseTo"]; + if (response.isValid()) { + errorString += QString("\nIn response to message: %2").arg(response.toString()); + } + return setQueryError( errorString ); +} + +bool QScriptSystemTest::setQueryError( const QString &errString ) +{ + if (queryFailed()) return false; + QSystemTest::setQueryError(errString); + bool ret = fail(errString); + if (!ret) { + m_engine->evaluate("throw new QTestFailure('QFAIL');"); + } + return ret; +} + +int QScriptSystemTest::runTest(const QString &fname, const QStringList ¶meters, + const QStringList &environment) +{ + m_env = environment; + bool createNewEngine = filename == fname; + filename = fname; + + QFile file(filename); + if (!file.open(QFile::ReadOnly)) { + qDebug() << "Can't open " << filename; + return -1; + } + + QTextStream stream(&file); + QString script = stream.readAll(); + + ScriptPreprocessor().preprocess(script); + + initEngine(createNewEngine); + + // Allow shebangs without giving syntax errors. + if (script.startsWith("#!")) script.prepend("//"); + script.prepend("with(ParentTestMetaObject){"); + script.append("\n}"); + + QtScriptTest tc(filename, script, m_engine); + + if (tc.status() != QtScriptTest::StatusNormal) { + return -1; + } + + testObject = &tc; + + qScriptRegisterMetaType(m_engine, variantToScriptValue, variantFromScriptValue); + qScriptRegisterSequenceMetaType<QList<qint64> >(m_engine); + + // Only set up the test data path if not explicitly set by user + if (!QCoreApplication::arguments().contains("-data")) { + setupTestDataPath(qPrintable(filename)); + } + + enableQueryWarnings(false); + + // If we get here, the syntax of the script is definitely OK + // (a syntax error causes a qFatal in the QtScriptTest ctor). + if (m_checkOnly) + return 0; + + // If an IDE is connected, set the agent to enable script debugging + if (QTestIDE::instance()->isConnected()) { + m_engine->setAgent(m_agent); + } + + if (QTest::testObject()) { + qDebug() << "Test already in progress?"; + return -1; + } + int retval = QTest::qExec(&tc, parameters); + + testObject = 0; + + // After a full test run, QTestLib sometimes returns 0 or sometimes returns + // the number of test failures, depending on how it was compiled. In both + // cases, a negative number denotes an error. + // We don't want test failures to affect the exit code. + return (retval < 0) ? retval : 0; +} + +/*! + \internal + Send a raw message from within script to the connected system. + + This can be used to extend the API for new messages on the target device + without modifying QSystemTest. + + If the message doesn't elicit a response of "OK", the current test fails. +*/ +QVariantMap QScriptSystemTest::sendRaw(const QString& event, const QScriptValue& object) +{ + QVariantMap ret; + if (object.isValid() && !object.isObject()) { + setQueryError("Second argument to sendRaw must be an object."); + return ret; + } + if (event.isEmpty()) { + setQueryError("First argument to sendRaw cannot be an empty string."); + return ret; + } + + QTestMessage message(event); + + // Treat object like a variant map and load it into message. + QScriptValueIterator it(object); + while (it.hasNext()) { + it.next(); + QScriptValue v = it.value(); + // Map must be flat; we don't handle objects within objects. + if (v.isObject() && !v.isArray()) { + setQueryError("Object passed to sendRaw must not have any child objects."); + return ret; + } + // toVariant appears to flatten stringlists into strings, which we don't want. + if (v.isArray()) { + QVariantList list; + for (int i = 0, max = qscriptvalue_cast<int>(v.property("length")); i < max; ++i) + list << v.property(i).toVariant(); + message[it.name()] = list; + } else { + message[it.name()] = v.toVariant(); + } + } + + QTestMessage reply; + if (!doQuery(message, QString(), &reply)) { + setQueryError("Raw " + event + " message failed: " + reply["status"].toString()); + return ret; + } + + foreach (QString const& key, reply.keys()) + ret[key] = reply[key]; + return ret; +} + +//#ifndef QTCREATOR_QTEST +/*! + \internal + Print any special usage information which should be shown when test is launched + with -help. +*/ +void QScriptSystemTest::printUsage() const +{ + QSystemTest::printUsage(); + qWarning( + " Script options:\n" + " -c : Check the syntax of the test only. Returns a non-zero exit code if the test\n" + " contains any syntax errors.\n" + ); +} +//#endif + +/*! + \internal + If \a func is a function, install it as a handler for all messages received from the test + system. + + Whenever a new message is received, \a func will be called with two arguments. + The first is the message event as a string. + The second is an object containing one property for each parameter of the message. + + If \a func returns true, processing of the message ends. +*/ +void QScriptSystemTest::installMessageHandler(const QScriptValue& func) +{ + if (!func.isFunction()) { + setQueryError("Argument to installMessageHandler must be a function."); + return; + } + m_messageHandlers << func; +} + + +QString qDumpScriptValue(QString const& name, QScriptValue const& v, int indent = 0) +{ + const QString spc = QString().fill(' ', indent); + + QString ret; + + ret += spc + name + ": "; + if (name != "global" && v.engine()->globalObject().strictlyEquals(v)) + ret += "global"; + else if (v.isBoolean()) + ret += "Boolean:" + v.toString(); + else if (v.isDate()) + ret += "Date:" + v.toString(); + else if (v.isFunction()) + ret += "Function"; + else if (v.isNull()) + ret += "null"; + else if (v.isNumber()) + ret += "Number:" + v.toString(); + else if (v.isString()) + ret += "String:" + v.toString(); + else if (v.isUndefined()) + ret += "undef"; + else { + QString inner; + QScriptValueIterator it(v); + QString sep; + while (it.hasNext()) { + it.next(); + inner += sep + qDumpScriptValue(it.name(), it.value(), indent+2); + sep = ",\n"; + } + if (inner.isEmpty()) { + ret += "{}"; + } else { + ret += "{\n" + inner + "\n" + spc + "} /* " + name + " */"; + } + } + + return ret; +} + +/*! + \internal + Write out most of the state of the script engine to stderr. +*/ +void QScriptSystemTest::dumpEngine() +{ + QString state; + + { + QScriptContext* ctx = m_engine->currentContext(); + state += "context: {"; + int i = 0; + QString sep; + while (ctx) { + state += QString("%1\n %2: {\n").arg(sep).arg(i++); + state += " toString: " + ctx->toString() + "\n"; + state += qDumpScriptValue("activationObject", ctx->activationObject(), 4) + ",\n"; + state += qDumpScriptValue("thisObject", ctx->thisObject(), 4) + "\n"; + state += " }"; + sep = ","; + ctx = ctx->parentContext(); + } + state += "\n};\n"; + } + + state += qDumpScriptValue("global", m_engine->globalObject()); + state += ";"; + + fprintf(stderr, "%s\n", qPrintable(state)); +} + +/*! + \internal + Passes the test message through any installed QtScript message handlers. + If none of them handle the message, it will be passed to the superclass. +*/ +void QScriptSystemTest::processMessage(const QTestMessage& message) +{ + if (m_messageHandlers.count()) { + QVariantMap map; + foreach (QString const& key, message.keys()) + map[key] = message[key]; + + QScriptValueList args; + args << m_engine->toScriptValue(message.event()); + args << m_engine->toScriptValue(map); + + for (int i = 0; i < m_messageHandlers.count(); ++i) { + QScriptValue out = m_messageHandlers[i].call(QScriptValue(), args); + if (out.isBoolean() && out.toBoolean()) + return; + } + } + + QSystemTest::processMessage(message); +} + +/*! + \internal + Processes the command line parameters. +*/ +void QScriptSystemTest::processCommandLine( QStringList &args ) +{ + QMutableStringListIterator it(args); + + while (it.hasNext()) { + QString arg = it.next(); + if (!arg.compare("-c", Qt::CaseInsensitive)) { + m_checkOnly = true; + it.remove(); + } + } + + QSystemTest::processCommandLine(args); +} + +void QScriptSystemTest::scriptPositionChange(qint64 scriptId, int line, int column) +{ + Q_UNUSED(scriptId); + Q_UNUSED(line); + Q_UNUSED(column); + + QScriptContextInfo ctxInfo(m_engine->currentContext()); + if (!ctxInfo.fileName().isEmpty() && !builtins.contains(ctxInfo.fileName())) { + QString functionName; + if (ctxInfo.functionName().isEmpty()) { + functionName = currentTestFunction(); + } else { + functionName = ctxInfo.functionName(); + } + if (QTestIDE::instance()->queryBreakpoint(ctxInfo.fileName(), ctxInfo.lineNumber(), functionName, m_contextDepth)) { + QTestIDE::instance()->breakpointContext(m_engine->currentContext()); + } + } +} + +void QScriptSystemTest::scriptContextChange(bool isNew) +{ + if (isNew) + ++m_contextDepth; + else + --m_contextDepth; +}
\ No newline at end of file diff --git a/old/interpreter/qscriptsystemtest.h b/old/interpreter/qscriptsystemtest.h new file mode 100644 index 0000000..bc270af --- /dev/null +++ b/old/interpreter/qscriptsystemtest.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTSYSTEMTEST_H +#define QSCRIPTSYSTEMTEST_H + +#include <qsystemtest.h> +#include <QScriptEngine> +#include <QScriptEngineAgent> +#include <QVariantMap> + +class QTestMessage; + +class QTUITESTRUNNER_EXPORT QScriptSystemTest + : public QSystemTest +{ +Q_OBJECT +public: + QScriptSystemTest(); + virtual ~QScriptSystemTest(); + + static QString loadInternalScript(QString const &name, QScriptEngine *engine, bool withParentObject = false); + static void loadBuiltins(QScriptEngine *engine); + static void importIntoGlobalNamespace(QScriptEngine*, QString const&); + + virtual QString testCaseName() const; + void scriptPositionChange(qint64, int, int); + void scriptContextChange(bool); + + virtual int runTest(const QString &fname, const QStringList ¶meters, + const QStringList &environment); + +public slots: + virtual bool fail(QString const &message); + virtual void expectFail( const QString &reason ); + virtual void skip(QString const &message, QSystemTest::SkipMode mode = QSystemTest::SkipSingle); + virtual void verify(bool statement, QString const &message = QString()); + virtual void compare(const QString &actual, const QString &expected); + virtual void compare(const QStringList &actual, const QStringList &expected); + + QVariantMap sendRaw(const QString&, const QScriptValue& = QScriptValue()); + void installMessageHandler(const QScriptValue&); + + void dumpEngine(); + +protected: + virtual void initEngine(bool); + virtual QString currentFile(); + virtual int currentLine(); + virtual void outputBacktrace(); + + virtual bool setQueryError( const QTestMessage &message ); + virtual bool setQueryError( const QString &errString ); + + virtual void printUsage() const; + virtual void processCommandLine(QStringList&); + + virtual bool isFailExpected(); + + virtual void processMessage(const QTestMessage& message); + +private: + QString filename; + QScriptEngine *m_engine; + QScriptEngineAgent *m_agent; + int m_contextDepth; + QList<QScriptValue> m_messageHandlers; + QObject* testObject; + QString expect_fail_function; + QString expect_fail_datatag; + QString expect_fail_reason; + bool m_checkOnly; + QStringList m_builtins; +}; + +#endif diff --git a/old/interpreter/qtscript_bindings.cpp b/old/interpreter/qtscript_bindings.cpp new file mode 100644 index 0000000..dc66659 --- /dev/null +++ b/old/interpreter/qtscript_bindings.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <qtestide.h> +#include "qscriptengine.h" +#include "qscriptvalue.h" +#include "qscriptvalueiterator.h" +#include "qscriptcontext.h" +#include "qscriptcontextinfo.h" + +#include "qtscript_bindings.h" + +QStringList builtinFiles; + +void QtScript::addInternalFile(QString const &filename) +{ + builtinFiles << filename; +} + +static QString readFile(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::ReadOnly)) + return QString(); + QTextStream stream(&file); + return stream.readAll(); +} + +static void appendCString(QVector<char> *v, const char *s) +{ + char c; + do { + c = *(s++); + *v << c; + } while (c != '\0'); +} + +void QtScript::getLocation(QScriptContext *ctx, QString *fileName, int *lineNumber) +{ + Q_ASSERT(ctx); + if (fileName) *fileName = QString(); + if (lineNumber) *lineNumber = 0; + + while (ctx) { + QScriptContextInfo ctxInfo(ctx); + QString fn = ctxInfo.fileName(); + if (!fn.isEmpty() && !builtinFiles.contains(fn)) { + if (fileName) *fileName = fn; + if (lineNumber) *lineNumber = ctxInfo.lineNumber(); + return; + } + ctx = ctx->parentContext(); + } +} + +QMetaObject QtScriptTest::staticMetaObject; + +Q_GLOBAL_STATIC(QVector<uint>, qt_meta_data_QtScriptTest) +Q_GLOBAL_STATIC(QVector<char>, qt_meta_stringdata_QtScriptTest) + +const QMetaObject *QtScriptTest::metaObject() const +{ + return &staticMetaObject; +} + +void *QtScriptTest::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QtScriptTest()->constData())) + return static_cast<void*>(const_cast<QtScriptTest*>(this)); + return QObject::qt_metacast(_clname); +} + +int QtScriptTest::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + const QMetaObject *mo = metaObject(); + QMetaMethod method = mo->method(mo->methodOffset() + _id); + QByteArray sig = method.signature(); + QString name = sig.left(sig.lastIndexOf('(')); + QScriptValue testcase = m_engine->globalObject().property("testcase"); + QScriptValue val = testcase.property(name); + if (name.endsWith("_data")) { + QTest::addColumn<int>("dummy"); + QScriptValueIterator it(val); + while (it.hasNext()) { + it.next(); + QString tag = it.name(); + QTest::newRow(tag.toLatin1()); + } + } else { + QScriptValue args; + QScriptValue data = testcase.property(name + "_data"); + if (data.isObject()) { + QString tag = QTest::currentDataTag(); + QScriptValue v = data.property(tag); + if (v.isValid()) { + if (v.isArray()) { + args = v; + } else { + args = m_engine->newArray(); + args.setProperty(0, v); + } + } + } + if (!args.isArray()) + args = m_engine->newArray(); + QScriptValue ret; + if (name == "init") { + ret = m_engine->evaluate("init_global();"); + } + else if (name == "initTestCase") { + ret = m_engine->evaluate("initTestCase_global();"); + } + else if (name != "cleanup" && name != "cleanupTestCase") { + m_engine->evaluate("qtuitest_pre_test_function();"); + } + if (!ret.isError() || ret.property("name").toString() == "QTestFailure") { + ret = val.call(m_engine->globalObject(), args); + QTest::compare_helper( true, "expectFail() not followed by a test statement", + m_testFilePath.toAscii(), property("expectFailLineNumber").toInt() ); + } + if (name != "init" && name != "initTestCase" && name != "cleanup" && name != "cleanupTestCase") { + m_engine->evaluate("qtuitest_post_test_function();"); + } + if (name == "cleanup") { + m_engine->evaluate("cleanup_global();"); + } + else if (name == "cleanupTestCase") { + m_engine->evaluate("cleanupTestCase_global();"); + } + if (ret.isError() && (ret.property("name").toString() != "QTestFailure")) { + QString backtrace = m_engine->uncaughtExceptionBacktrace().join("\n"); + QString message = ret.toString() + // xxx makes the failure message look cluttered; correct fix + // xxx is to implement proper generic saving of backtrace info + // xxx in test results + // + "\n" + backtrace + ; + QString fileName = "unknown"; + int lineNumber = -1; + QRegExp locationRe("@([^:]+):([0-9]+)"); + if (-1 != locationRe.indexIn(backtrace)) { + fileName = locationRe.cap(1); + lineNumber = locationRe.cap(2).toInt(); + } + QTest::qFail(qPrintable(message), qPrintable(fileName), lineNumber); + } + } + + _id = -1; + } + return _id; +} + +QtScriptTest::QtScriptTest(QString const &testFilePath, QString const &scriptData, QScriptEngine *engine) + : QObject(), m_testFilePath(testFilePath), m_engine(engine), m_status(StatusNotStarted) +{ + if (m_testFilePath.isEmpty()) + m_testFilePath = qgetenv("Q_TEST_FILE"); + if (m_testFilePath.isEmpty()) + m_testFilePath="testcase.js"; + +// m_engine->importExtension("qt.core"); + + QScriptValue qtestObject = m_engine->newObject(); + qtestObject.setProperty("SkipSingle", QScriptValue(m_engine, QTest::SkipSingle)); + qtestObject.setProperty("SkipAll", QScriptValue(m_engine, QTest::SkipAll)); + qtestObject.setProperty("Abort", QScriptValue(m_engine, QTest::Abort)); + qtestObject.setProperty("Continue", QScriptValue(m_engine, QTest::Continue)); + m_engine->globalObject().setProperty("QTest", qtestObject); + + m_engine->evaluate("function QTestFailure() { Error.apply(this, arguments); }" + "QTestFailure.prototype = new Error();" + "QTestFailure.prototype.name = 'QTestFailure';"); + + QStringList slotNames; + QString script = scriptData; + if (script.isEmpty()) script = readFile(m_testFilePath); + if (!script.isEmpty()) { + QScriptSyntaxCheckResult synChk = m_engine->checkSyntax(script); + if (synChk.state() != QScriptSyntaxCheckResult::Valid) { + QTestIDE::instance()->scriptSyntaxError(synChk.errorMessage(), m_testFilePath, synChk.errorLineNumber()); + m_status = StatusSyntaxError; + return; + } + + QScriptValue ret = m_engine->evaluate(script, m_testFilePath); + QString error; + if (m_engine->hasUncaughtException()) { + error = m_engine->uncaughtException().toString(); + } else if (ret.isError()) { + error = ret.toString(); + } + if (!error.isEmpty()) { + QString backtrace = m_engine->uncaughtExceptionBacktrace().join("\n"); + qWarning("%s\n%s", qPrintable(error), qPrintable(backtrace)); + m_status = StatusException; + return; + } + QScriptValue testcase = m_engine->globalObject().property("testcase"); + QScriptValueIterator it(testcase); + while (it.hasNext()) { + it.next(); + QScriptValue val = it.value(); + if (val.isFunction() || (val.isObject() && it.name().endsWith("_data"))) + slotNames << it.name(); + } + + QStringList requiredSlots; + requiredSlots + << "init" + << "cleanup" + << "initTestCase" + << "cleanupTestCase"; + + foreach (QString required, requiredSlots) { + if (!slotNames.contains(required)) { + m_engine->evaluate("testcase." + required + " = function() {}"); + slotNames << required; + } + } + } else { + qWarning("*** Failed to read testcase!"); + } + + QVector<uint> *data = qt_meta_data_QtScriptTest(); + data->clear(); + // content: + *data << 1 // revision + << 0 // classname + << 0 << 0 // classinfo + << slotNames.count() << 10 // methods + << 0 << 0 // properties + << 0 << 0 // enums/sets + ; + + QString testcaseName = QFileInfo(m_testFilePath).baseName(); + + QVector<char> *stringdata = qt_meta_stringdata_QtScriptTest(); + stringdata->clear(); + appendCString(stringdata, testcaseName.toLocal8Bit() ); + int namelen = stringdata->size(); + appendCString(stringdata, ""); + + // slots: signature, parameters, type, tag, flags + foreach (QString slotName, slotNames) { + QString slot = slotName + QLatin1String("()"); + *data << stringdata->size() << namelen << namelen << namelen << 0x08; + appendCString(stringdata, slot.toLatin1()); + } + *data << 0; // eod + + // initialize staticMetaObject + staticMetaObject.d.superdata = &QObject::staticMetaObject; + staticMetaObject.d.stringdata = stringdata->constData(); + staticMetaObject.d.data = data->constData(); + staticMetaObject.d.extradata = 0; + + m_status = StatusNormal; +} + +QtScriptTest::~QtScriptTest() +{ +} + +QtScriptTest::Status QtScriptTest::status() +{ + return m_status; +}
\ No newline at end of file diff --git a/old/interpreter/qtscript_bindings.h b/old/interpreter/qtscript_bindings.h new file mode 100644 index 0000000..b60f746 --- /dev/null +++ b/old/interpreter/qtscript_bindings.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSCRIPT_BINDINGS_H +#define QTSCRIPT_BINDINGS_H + +namespace QtScript { + void getLocation(QScriptContext*,QString*,int*); + void addInternalFile(QString const&); +}; + +class QtScriptTest : public QObject +{ + +public: + + enum Status + { + StatusNotStarted, + StatusNormal, + StatusSyntaxError, + StatusException + }; + + QtScriptTest(QString const &testFilePath = QString(), QString const &scriptData = QString(), QScriptEngine *engine = 0); + virtual ~QtScriptTest(); + + static QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + + QString testFilePath() const { return m_testFilePath; } + + QScriptEngine* engine() { return m_engine; } + Status status(); + +private: + QString m_testFilePath; + QScriptEngine* m_engine; + Status m_status; +}; + +#endif + diff --git a/old/interpreter/qtuitestengineagent.cpp b/old/interpreter/qtuitestengineagent.cpp new file mode 100644 index 0000000..44dade0 --- /dev/null +++ b/old/interpreter/qtuitestengineagent.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtuitestengineagent.h" +#include <QDebug> +#include <QScriptEngine> +#include <QScriptContextInfo> +#include <QScriptValueIterator> + +QtUiTestEngineAgent::QtUiTestEngineAgent(QScriptEngine *engine, QScriptSystemTest *sysTest) + : QScriptEngineAgent(engine), m_systemTest(sysTest) +{ +} + +QtUiTestEngineAgent::~QtUiTestEngineAgent() +{ +} + +void QtUiTestEngineAgent::positionChange(qint64 scriptId, int line, int column) +{ + m_systemTest->scriptPositionChange(scriptId, line, column); +} + +void QtUiTestEngineAgent::contextPush() +{ + m_systemTest->scriptContextChange(true); +} + +void QtUiTestEngineAgent::contextPop() +{ + m_systemTest->scriptContextChange(false); +}
\ No newline at end of file diff --git a/old/interpreter/qtuitestengineagent.h b/old/interpreter/qtuitestengineagent.h new file mode 100644 index 0000000..e610ffc --- /dev/null +++ b/old/interpreter/qtuitestengineagent.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTUITESTENGINEAGENT_H +#define QTUITESTENGINEAGENT_H + +#include <QScriptEngineAgent> +#include "qscriptsystemtest.h" + +class QtUiTestEngineAgent : public QScriptEngineAgent +{ + +public: + QtUiTestEngineAgent(QScriptEngine*,QScriptSystemTest*); + ~QtUiTestEngineAgent(); + virtual void positionChange(qint64, int, int); + virtual void contextPush(); + virtual void contextPop(); + +private: + QScriptSystemTest *m_systemTest; + +}; + +#endif diff --git a/old/interpreter/scriptpreprocessor.cpp b/old/interpreter/scriptpreprocessor.cpp new file mode 100644 index 0000000..42a413b --- /dev/null +++ b/old/interpreter/scriptpreprocessor.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptpreprocessor.h" +#include "qscriptsystemtest.h" + +#include <QScriptEngine> +#include <QScriptValue> +#include <QScriptValueIterator> +#include <QByteArray> + +ScriptPreprocessor::ScriptPreprocessor() +{ + QScriptEngine engine; + QScriptSystemTest::loadInternalScript("config.js", &engine); + + QScriptValue settings = engine.globalObject().property("preprocess"); + if (!settings.isObject()) return; + + /* The documentation for the following settings objects is in config.js */ + + { + QScriptValueIterator it(settings.property("functionAppends")); + while (it.hasNext()) { + it.next(); + functionAppends[it.name()] = it.value().toString(); + } + } +} + +void ScriptPreprocessor::preprocess(QString &script) +{ + QString out; + out.reserve(script.size()); + + bool in_singleline_comment = false; + bool in_multiline_comment = false; + bool in_singlequote_literal = false; + bool in_doublequote_literal = false; + + const char singlequote = '\''; + const char doublequote = '"'; + const char brace_open = '('; + const char brace_close = ')'; + const char curlybrace_open = '{'; + const char curlybrace_close = '}'; + const char newline = '\n'; + const char cr = '\r'; + const char backslash = '\\'; + const char forwardslash = '/'; + const char asterisk = '*'; + + QString identifier_chars = "_"; + for (char c = '0'; c <= '9'; ++c) identifier_chars.append(c); + for (char c = 'a'; c <= 'z'; ++c) identifier_chars.append(c); + for (char c = 'A'; c <= 'Z'; ++c) identifier_chars.append(c); + + int braces = 0; + int curlybraces = 0; + + QString function_append; + int function_append_braces = 0; + + for (int i = 0; i < script.count(); ++i) { + QChar c1 = script[i];//.toLatin1(); + QChar c2 = script[i+1];//.toLatin1(); // OK; QByteArray is always null-terminated. + + if (in_singleline_comment) { + if (newline == c1) { + in_singleline_comment = false; + out.append(c1); + continue; + } + out.append(c1); + continue; + } + + if (in_multiline_comment) { + if (asterisk == c1 && forwardslash == c2) { + in_multiline_comment = false; + out.append(c1); + out.append(c2); + ++i; + continue; + } + out.append(c1); + continue; + } + + if (in_singlequote_literal) { + if (backslash == c1) { + out.append(c1); + out.append(c2); + ++i; + continue; + } + if (singlequote == c1) { + in_singlequote_literal = false; + out.append(c1); + continue; + } + if (cr == c1 && newline == c2) { + out.append("\\n\'+\r\n\'"); + ++i; + continue; + } + if (newline == c1) { + out.append("\\n\'+\n\'"); + continue; + } + out.append(c1); + continue; + } + + if (in_doublequote_literal) { + if (backslash == c1) { + out.append(c1); + out.append(c2); + ++i; + continue; + } + if (doublequote == c1) { + in_doublequote_literal = false; + out.append(c1); + continue; + } + if (cr == c1 && newline == c2) { + out.append("\\n\"+\r\n\""); + ++i; + continue; + } + if (newline == c1) { + out.append("\\n\"+\n\""); + continue; + } + out.append(c1); + continue; + } + + switch(c1.toLatin1()) { + + case singlequote: + in_singlequote_literal = true; + out.append(c1); + continue; + + case doublequote: + in_doublequote_literal = true; + out.append(c1); + continue; + + case forwardslash: + out.append(c1); + if (c2 == forwardslash) { + in_singleline_comment = true; + out.append(c2); + ++i; + } + if (c2 == asterisk) { + in_multiline_comment = true; + out.append(c2); + ++i; + } + continue; + + case brace_open: + ++braces; + out.append(c1); + continue; + + case brace_close: + --braces; + out.append(c1); + if (!function_append.isEmpty() && function_append_braces == braces) { + out.append(function_append); + function_append = QString(); + } + continue; + + case curlybrace_open: + ++curlybraces; + out.append(c1); + continue; + + case curlybrace_close: + --curlybraces; + out.append(c1); + continue; + + default: + // Look ahead to next non-identifier character + int tok_len; + for (tok_len = 0; i + tok_len < script.count() && identifier_chars.contains(script[i+tok_len]); ++tok_len) {} + if (tok_len < 2) { + out.append(c1); + continue; + } + QString tok = script.mid(i, tok_len); + + // Apply preprocessing rules + + // 1. Function appends - text placed immediately after the closing + // bracket of a function invocation. + if (functionAppends.contains(tok)) { + function_append = functionAppends[tok]; + function_append_braces = braces; + } + + out.append(tok); + i += tok_len - 1; + continue; + } + } + + out.squeeze(); + + script = out; +} diff --git a/old/interpreter/scriptpreprocessor.h b/old/interpreter/scriptpreprocessor.h new file mode 100644 index 0000000..31046f9 --- /dev/null +++ b/old/interpreter/scriptpreprocessor.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SCRIPTPREPROCESSOR_H +#define SCRIPTPREPROCESSOR_H + +#include <QMap> +#include <QString> + +class ScriptPreprocessor +{ +public: + ScriptPreprocessor(); + void preprocess(QString &script); + +private: + QMap<QString, QString> functionAppends; +}; + +#endif diff --git a/old/interpreter/scripts.qrc b/old/interpreter/scripts.qrc new file mode 100644 index 0000000..3baa62e --- /dev/null +++ b/old/interpreter/scripts.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>builtins.js</file> + <file>config.js</file> +</qresource> +</RCC> |