diff --git a/HACKING b/HACKING index 1bfaea07..6b4ca69f 100644 --- a/HACKING +++ b/HACKING @@ -133,12 +133,20 @@ In general: Just look at the existing source code! Wrong: function splitStr() -== Testing/Optimization == +== Testing == -TODO: Add some information here about testing/validation/etc. -Information about how/when to use :regressions might be nice. -Additionally, maybe there should be some benchmark information here -- -something to let a developer know what's "too" slow...? Or general -guidelines about optimization? +Functional tests are implemented using the Mozmill automated testing framework +-- https://developer.mozilla.org/en/Mozmill_Tests. -// vim: set ft=asciidoc fdm=marker sw=4 ts=4 et ai: +A fresh profile is created for the duration of the test run, however, passing +arguments to the host application won't be supported until Mozmill 1.5.2, the +next release, so any user RC and plugin files should be temporarily disabled. +This can be done by adding the following to the head of the RC file: +set loadplugins= +finish + +The host application binary tested can be overridden via the HOSTAPP_PATH +makefile variable. E.g., +$ HOSTAPP_PATH=/path/to/firefox make -e -C pentadactyl test + +// vim: fdm=marker sw=4 ts=4 et ai: diff --git a/common/Makefile b/common/Makefile index 64441a35..f2460f4e 100644 --- a/common/Makefile +++ b/common/Makefile @@ -9,6 +9,9 @@ GOOGLE = https://$(GOOGLE_PROJ).googlecode.com/files VERSION ?= $(shell sed -n 's/.*em:version\(>\|="\)\(.*\)["<].*/\2/p' $(TOP)/install.rdf | sed 1q) UUID := $(shell sed -n 's/.*em:id\(>\|="\)\(.*\)["<].*/\2/p' $(TOP)/install.rdf | sed 1q) MANGLE := $(shell date '+%s' | awk '{ printf "%x", $$1 }') +MOZMILL = mozmill +HOSTAPP_PATH = $(shell which $(HOSTAPP)) +TEST_DIR = $(BASE)/tests/functional LOCALEDIR = locale DOC_FILES = $(wildcard $(LOCALEDIR)/*/*.xml) @@ -48,7 +51,7 @@ CURL ?= curl #### rules -TARGETS = all help info jar xpi install clean distclean install installxpi $(CHROME) $(JAR) +TARGETS = all help info jar xpi install clean distclean install installxpi test $(CHROME) $(JAR) $(TARGETS:%=\%.%): echo MAKE $* $(@:$*.%=%) $(MAKE) -C $* $(@:$*.%=%) @@ -71,6 +74,7 @@ help: @echo " make dist - uploads to Google Code (this is not for you)" @echo " make clean - clean up" @echo " make distclean - clean up more" + @echo " make test - run functional tests" @echo @echo "running some commands with V=1 will show more build details" @@ -158,6 +162,11 @@ distclean: @echo "More $(NAME) cleanup..." rm -rf $(BUILD_DIR) +# TODO: generalize log path +test: $(XPI) + @echo "Running functional tests..." + $(MOZMILL) --show-all -l /tmp/dactyl-test.log -b $(HOSTAPP_PATH) --addons $(XPI) -t $(TEST_DIR) + #### xpi $(XPI): $(CHROME) diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js index 7fe61ab0..8b741d9a 100644 --- a/common/content/bookmarks.js +++ b/common/content/bookmarks.js @@ -105,7 +105,7 @@ var Bookmarks = Module("bookmarks", { */ addSearchKeyword: function (elem) { if (elem instanceof HTMLFormElement || elem.form) - var [url, post,, charset] = util.parseForm(elem); + var [url, post, charset] = util.parseForm(elem); else var [url, post, charset] = [elem.href || elem.src, null, elem.ownerDocument.characterSet]; diff --git a/common/content/statusline.js b/common/content/statusline.js index ccc3b454..f76eac28 100644 --- a/common/content/statusline.js +++ b/common/content/statusline.js @@ -12,7 +12,7 @@ var StatusLine = Module("statusline", { init: function () { this._statusLine = document.getElementById("status-bar"); this.statusBar = document.getElementById("addon-bar") || this._statusLine; - this.statusBar.collapsed = true; // it is later restored unless the user sets laststatus=0 + this.statusBar.collapsed = true; this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : ""; if (this.statusBar.localName == "toolbar") { diff --git a/common/locale/en-US/gui.xml b/common/locale/en-US/gui.xml index e7054858..ed6e13e9 100644 --- a/common/locale/en-US/gui.xml +++ b/common/locale/en-US/gui.xml @@ -211,8 +211,9 @@

Status line

- The status line appears at the bottom of each window. The laststatus - option can be used to specify when the status line appears. + The status line appears at the bottom of each window. You can use + guioptions to specify if and when the status line appears, as well + as its relation to the command line and messages.

diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 2ffc9586..1a5e5b7a 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -169,8 +169,12 @@ function endModule() { function require(obj, name, from) { try { + if (arguments.length === 1) + [obj, name] = [{}, obj]; + defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + obj.NAME); JSMLoader.load(name + ".jsm", obj); + return obj; } catch (e) { defineModule.dump("loading " + String.quote(name + ".jsm") + "\n"); diff --git a/common/modules/config.jsm b/common/modules/config.jsm index ca65a065..0e818d3f 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -8,6 +8,7 @@ try { +let global = this; Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("config", { exports: ["ConfigBase", "Config", "config"], @@ -54,7 +55,7 @@ var ConfigBase = Class("ConfigBase", { addon: Class.memoize(function () { let addon = services.fuel.storage.get("dactyl.bootstrap", {}).addon; if (!addon) - addon = AddonManager.getAddonByID(this.addonID); + addon = require("addons").AddonManager.getAddonByID(this.addonID); return addon; }), diff --git a/common/modules/highlight.jsm b/common/modules/highlight.jsm index ac9496f0..9065a44f 100644 --- a/common/modules/highlight.jsm +++ b/common/modules/highlight.jsm @@ -145,10 +145,10 @@ var Highlights = Module("Highlight", { let highlight = this.highlight[key] || this._create(false, [key]); - let extends = extend || highlight.extend; + let bases = extend || highlight.extend; if (append) { newStyle = Styles.append(highlight.value || "", newStyle); - extends = highlight.extends.concat(extends); + bases = highlight.extends.concat(bases); } if (/^\s*$/.test(newStyle)) @@ -161,11 +161,11 @@ var Highlights = Module("Highlight", { return null; } newStyle = highlight.defaultValue; - extends = highlight.defaultExtends; + bases = highlight.defaultExtends; } highlight.set("value", newStyle || ""); - highlight.extends = array.uniq(extends, true); + highlight.extends = array.uniq(bases, true); if (force) highlight.style.enabled = true; this.highlight[highlight.class] = highlight; diff --git a/common/modules/io.jsm b/common/modules/io.jsm index 0aaa3c9e..a60c4ea5 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -836,7 +836,7 @@ unlet s:cpo_save let result = io.system(arg); if (result.returnValue != 0) - result.output += "\nshell returned " + res; + result.output += "\nshell returned " + result.returnValue; modules.commandline.command = "!" + arg; modules.commandline.commandOutput({result.output}); diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 3f521db7..f66fac87 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -1179,7 +1179,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } } if (post) - return [url, elems.join('&'), elems, charset]; + return [url, elems.join('&'), charset, elems]; return [url + "?" + elems.join('&'), null, charset]; }, diff --git a/pentadactyl/regressions.js b/pentadactyl/regressions.js deleted file mode 100644 index bea1a864..00000000 --- a/pentadactyl/regressions.js +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2009 by Martin Stubenschrott -// -// This work is licensed for reuse under an MIT license. Details are -// given in the LICENSE.txt file included with this file. - - -// Script to find regressions -// -// It should use as few dactyl methods as possible, but fall back to standard mozilla/DOM methods -// The reason is, we don't want to find regressions in the regressions script, and it should survive -// massive changes in the internal dactyl API, but just test for functionality of -// user-visible commands/mappings -// -// NOTE: It is preferable to run this script in a clean profile or at least do NOT use -// :mkpentadactylrc afterwards, as it can remove commands/mappings, etc. -// -// Usage: :[count]regr[essions] -// When [count] is given, just run this test. TODO: move to :regressions [spec]? - -// all tests -var skipTests = [":bmarks", "gg"]; - -///////////////////////////////////////////////////////////////////////////////////////// -// Put definitions here which might change due to internal dactyl refactoring -///////////////////////////////////////////////////////////////////////////////////////// - -var doc; // document where we output status messages -var multilineOutput = document.getElementById("dactyl-multiline-output"); -var singlelineOutput = document.getElementById("dactyl-message"); - -///////////////////////////////////////////////////////////////////////////////////////// -// TESTS -// -// They are run in order, so you can specify commands which expect side effects of a -// previous command -///////////////////////////////////////////////////////////////////////////////////////// - -// A series of Ex commands or mappings, each with a -// function checking whether the command succeeded -// If the string starts with a ":" it is executed as an Ex command, otherwise as a mapping -// You can also mix commands and mappings -let tests = [ - { cmds: [":!dir"], - verify: function () getMultilineOutput().length > 10 }, - { cmds: [":abbr VIMP pentadactyl labs", ":abbr"], - verify: function () getOutput().indexOf("pentadactyl labs") >= 0 }, - { cmds: [":unabbr VIMP", ":abbr"], - verify: function () getOutput().indexOf("pentadactyl labs") == -1 }, - { cmds: [":bmarks"], - verify: function () getMultilineOutput().length > 100 }, - { cmds: [":echo \"test\""], - verify: function () getSinglelineOutput() == "test" }, - { cmds: [":qmark V http://test.vimperator.org", ":qmarks"], - verify: function () getMultilineOutput().indexOf("test.vimperator.org") >= 0 }, - { cmds: [":javascript dactyl.echo('test', commandline.FORCE_MULTILINE)"], - verify: function () getMultilineOutput() == "test" }, - // { cmds: [":echomsg \"testmsg\""], - // verify: function () getOutput() == "testmsg" }, - // { cmds: [":echoerr \"testerr\""], - // verify: function () getOutput() == "testerr" }, - { cmds: ["gg", ""], // NOTE: does not work when there is no page to scroll, we should load a large page before doing these tests - verify: function () this._initialPos.y != getBufferPosition().y, - init: function () this._initialPos = getBufferPosition() } - - // testing tab behavior -]; - -// these functions highly depend on the dactyl API, so use Ex command tests whenever possible -let functions = [ - function () { return bookmarks.get("").length > 0 }, // will fail for people without bookmarks :( Might want to add one before - function () { return history.get("").length > 0 } -]; - -///////////////////////////////////////////////////////////////////////////////////////// -// functions below should be as generic as possible, and not require being rewritten -// even after doing major Pentadactyl refactoring -///////////////////////////////////////////////////////////////////////////////////////// - -function resetEnvironment() { - multilineOutput.contentDocument.body.innerHTML = ""; - singlelineOutput.value = ""; - commandline.close(); - modes.reset(); -} - -function getOutput() multilineOutput.contentDocument.body.textContent || singlelineOutput.value; -function getMultilineOutput() multilineOutput.contentDocument.body.textContent; -function getSinglelineOutput() singlelineOutput.value; - -function getTabIndex() getBrowser().mTabContainer.selectedIndex; -function getTabCount() getBrowser().mTabs.length; - -function getBufferPosition() { - let win = window.content; - return { x: win.scrollMaxX ? win.pageXOffset / win.scrollMaxX : 0, - y: win.scrollMaxY ? win.pageYOffset / win.scrollMaxY : 0 } -}; - -function getLocation() window.content.document.location.href; - -function echoLine(str, group) { - if (!doc) - return; - - doc.body.appendChild(util.xmlToDom( -

{str}
, - doc)); -} -function echoMulti(str, group) { - if (!doc) - return; - - doc.body.appendChild(util.xmlToDom(
{template.maybeXML(str)}
, - doc)); -} - -commands.addUserCommand(["regr[essions]"], - "Run regression tests", - function (args) { - // TODO: might need to increase the 'messages' option temporarily - // TODO: count (better even range) support to just run test 34 of 102 - // TODO: bang support to either: a) run commands like deleting bookmarks which - // should only be done in a clean profile or b) run functions and not - // just Ex command tests; Yet to be decided - - let updateOutputHeight = null; - function init() { - dactyl.registerObserver("echoLine", echoLine); - dactyl.registerObserver("echoMultiline", echoMulti); - dactyl.open("chrome://dactyl/content/buffer.xhtml", dactyl.NEW_TAB); - events.waitForPageLoad(); - doc = content.document; - doc.body.setAttributeNS(NS.uri, "highlight", "CmdLine"); - - updateOutputHeight = commandline.updateOutputHeight; - commandline.updateOutputHeight = function (open) { - let elem = document.getElementById("dactyl-multiline-output"); - if (open) - elem.collapsed = false; - elem.height = 0; - }; - } - function cleanup() { - dactyl.unregisterObserver("echoLine", echoLine); - dactyl.unregisterObserver("echoMultiline", echoMulti); - commandline.updateOutputHeight = updateOutputHeight; - } - - function run() { - let now = Date.now(); - let totalTests = tests.length + functions.length; - let successfulTests = 0; - let skippedTests = 0; - let currentTest = 0; - - init(); - - // TODO: might want to unify 'tests' and 'functions' handling - // 1.) run commands and mappings tests - outer: - for (let [, test] in Iterator(tests)) { - currentTest++; - if (args.count >= 1 && currentTest != args.count) - continue; - - let testDescription = util.clip(test.cmds.join(" -> "), 80); - for (let [, cmd] in Iterator(test.cmds)) { - if (skipTests.indexOf(cmd) != -1) { - skippedTests++; - dactyl.echomsg("Skipping test " + currentTest + " of " + totalTests + ": " + testDescription, 0); - continue outer; - } - }; - - commandline.echo("Running test " + currentTest + " of " + totalTests + ": " + testDescription, "Filter", commandline.APPEND_TO_MESSAGES); - resetEnvironment(); - if ("init" in test) - test.init(); - - test.cmds.forEach(function (cmd) { - if (cmd[0] == ":") - dactyl.execute(cmd); - else - events.feedkeys(cmd); - }); - - if (!test.verify()) - dactyl.echoerr("Test " + currentTest + " failed: " + testDescription); - else - successfulTests++; - } - - // 2.) Run function tests - for (let [, func] in Iterator(functions)) { - currentTest++; - if (args.count >= 1 && currentTest != args.count) - continue; - - commandline.echo("Running test " + currentTest + " of " + totalTests + ": " + util.clip(func.toString().replace(/[\s\n]+/gm, " "), 80), "Filter", commandline.APPEND_TO_MESSAGES); - resetEnvironment(); - - if (!func()) - dactyl.echoerr("Test " + currentTest + " failed!"); - else - successfulTests++; - } - - cleanup(); - - let runTests = (args.count >= 1 ? 1 : totalTests) - skippedTests; - XML.ignoreWhitespace = false; - dactyl.echomsg( - {successfulTests} of {runTests} - tests successfully completed ({skippedTests} tests skipped) in - {((Date.now() - now) / 1000.0)} msec - .*); - dactyl.execute(":messages"); - } - - if (!args.bang) { - dactyl.echo( - Running tests should always be done in a new profile.
- - It should not do any harm to your profile, but your current settings like options, - abbreviations or mappings might not be in the same state as before running the tests. - Just make sure, you don't :mkpentadactylrc, after running the tests.

- - - Use :regressions! to skip this prompt. -
.*); - commandline.input("Type 'yes' to run the tests: ", function (res) { if (res == "yes") run(); } ); - return; - } - run(); - }, - { - bang: true, - argCount: "0", - count: true - }); - -// vim: set et sts=4 sw=4 :