gabrielg-xultestrunner 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) <2009> <Gabriel Gironda>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,6 @@
1
+ XULTestRunner
2
+ =============
3
+
4
+ This is a XUL based test runner for running the quasi-scriptaculous-unittest.js-based tests that Conflagration and WaterTower use.
5
+
6
+ Yes, it is a XUL app in a RubyGem, yes I am in touch with this.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ begin
2
+ require 'jeweler'
3
+ require 'uuidtools'
4
+ require 'pathname'
5
+
6
+ Jeweler::Tasks.new do |s|
7
+ s.name = "xultestrunner"
8
+ s.executables = "xultest"
9
+ s.summary = "XUL based test runner for running your JS unit tests."
10
+ s.email = "contact@gironda.org"
11
+ s.homepage = "http://github.com/gabrielg/xultestrunner"
12
+ s.description = "XUL based test runner for running your JS unit tests."
13
+ s.authors = ["Gabriel Gironda"]
14
+ end
15
+
16
+ application_ini_path = (Pathname(__FILE__).parent + "xulapp/application.ini").expand_path
17
+
18
+ desc "Writes out a random UUID for the Build ID when we release to the XUL application's application.ini"
19
+ task :write_xul_build_id do
20
+ build_id = UUIDTools::UUID.random_create.to_s
21
+ ini_contents = application_ini_path.read
22
+ application_ini_path.open('w') do |f|
23
+ f << ini_contents.sub(/^BuildID=.*$/, "BuildID=#{build_id}")
24
+ end
25
+ end
26
+
27
+ desc "Writes out the gem version to the XUL application's application.ini"
28
+ task :write_xul_version do
29
+ version = (Pathname(__FILE__).parent + "VERSION").read.chomp
30
+ ini_contents = application_ini_path.read
31
+ application_ini_path.open('w') do |f|
32
+ f << ini_contents.sub(/^Version=.*$/, "Version=#{version}")
33
+ end
34
+ end
35
+
36
+ desc "Commits the application ini before a release"
37
+ task :commit_application_ini do
38
+ system("git", "add", application_ini_path.to_s)
39
+ system("git", "commit", "-m", "Bumping application.ini", application_ini_path.to_s)
40
+ end
41
+
42
+ task :release => [:write_xul_build_id, :write_xul_version, :commit_application_ini]
43
+
44
+ rescue LoadError
45
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
46
+ end
47
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/xultest ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+
4
+ firefox_location = [`which firefox-bin`.chomp, `which firefox`.chomp].detect do |loc|
5
+ !loc.length.zero?
6
+ end
7
+
8
+ raise "Could not find `firefox-bin` or `firefox` in your path. Please rectify this and try again." unless firefox_location
9
+
10
+ xulapp_ini_path = (Pathname(__FILE__).parent.parent + "xulapp/application.ini").expand_path
11
+
12
+ exec(firefox_location, *["-app", xulapp_ini_path, *ARGV])
@@ -0,0 +1,9 @@
1
+ [App]
2
+ Name=XULTestRunner
3
+ Version=0.1.0
4
+ BuildID=a8de3f80-a874-4802-9c8f-60c958660398
5
+ ID=XULTestRunner@conflagrationjs.org
6
+ Vendor=Conflagration JS
7
+
8
+ [Gecko]
9
+ MinVersion=1.9.1
@@ -0,0 +1,2 @@
1
+ content xultestrunner file:content/
2
+ resource xultestrunner file:content/
@@ -0,0 +1,15 @@
1
+ Cc = Components.classes;
2
+ Ci = Components.interfaces;
3
+ Cr = Components.results;
4
+ Cu = Components.utils;
5
+ loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
6
+ load = function(fileURI, moduleScope) {
7
+ try {
8
+ loader.loadSubScript(fileURI, moduleScope);
9
+ } catch (e) {
10
+ throw({name: "LoadError", message: "Failed to load " + fileURI + ": " + e.message});
11
+ }
12
+ };
13
+
14
+ print = dump;
15
+ puts = function(str) { print(str + "\n"); };
@@ -0,0 +1,126 @@
1
+ var XULTestCase = Class.create({
2
+ initialize: function(testName, testDef) {
3
+ this.testRunner = new XULTestCase.TestRunner(testDef);
4
+ this.testRunner.name = testName;
5
+ },
6
+
7
+ go: function() {
8
+ this.testRunner.runTests();
9
+ }
10
+
11
+ });
12
+
13
+ Object.extend(XULTestCase, {
14
+ create: function(testName, testDefinitionFunc) {
15
+ var tests = {};
16
+ var setupCollector = function(givenSetup) { tests['setup'] = givenSetup; };
17
+ var teardownCollector = function(givenTeardown) { tests['teardown'] = givenTeardown; };
18
+ var testCollector = function(testName, testDef) { tests["test " + testName] = testDef; };
19
+ testDefinitionFunc(setupCollector, teardownCollector, testCollector);
20
+ var testCase = new XULTestCase(testName, tests);
21
+ this.__LAST_DEFINED_TESTCASE__ = testCase;
22
+ return testCase
23
+ },
24
+
25
+ loadFromFile: function(fileURI) {
26
+ try {
27
+ load(fileURI);
28
+ if (this.__LAST_DEFINED_TESTCASE__) {
29
+ return this.__LAST_DEFINED_TESTCASE__;
30
+ } else {
31
+ throw("wuh oh");
32
+ }
33
+ } catch (e) {
34
+ this._handleTestLoadError(fileURI, e);
35
+ } finally {
36
+ this.__LAST_DEFINED_TESTCASE__ = null;
37
+ }
38
+ },
39
+
40
+ _handleTestLoadError: function(e) {
41
+ puts("There was a problem loading the test case from '" + fileURI + "'");
42
+ }
43
+
44
+ });
45
+
46
+ XULTestCase.Logger = Class.create(Test.Unit.Logger, {
47
+ consoleOutputMapping: {failed: 'F', error: 'E', passed: '.'},
48
+
49
+ initialize: function($super, log) {
50
+ $super(log);
51
+ this.unpassedTests = [];
52
+ },
53
+
54
+ finish: function ($super, status, summary) {
55
+ $super(status, summary);
56
+ print(this.consoleOutputMapping[status]);
57
+ if (status != "passed") {
58
+ this.unpassedTests.push({name: this.testName, status: status, summary: summary});
59
+ }
60
+ }
61
+ });
62
+
63
+ XULTestCase.TestRunner = Class.create(Test.Unit.Runner, {
64
+ // FIXME - ugh. straight rip from unittest.js just so we can ditch the settimeout
65
+ // and instantiate our own logger.
66
+
67
+ initialize: function(testcases) {
68
+ this.options = Object.extend({
69
+ testLog: 'testlog'
70
+ }, arguments[1] || {});
71
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
72
+ this.options.tests = this.parseTestsQueryParameter();
73
+ if (this.options.testLog) {
74
+ this.options.testLog = $(this.options.testLog) || null;
75
+ }
76
+ if(this.options.tests) {
77
+ this.tests = [];
78
+ for(var i = 0; i < this.options.tests.length; i++) {
79
+ if(/^test/.test(this.options.tests[i])) {
80
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
81
+ }
82
+ }
83
+ } else {
84
+ if (this.options.test) {
85
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
86
+ } else {
87
+ this.tests = [];
88
+ for(var testcase in testcases) {
89
+ if(/^test/.test(testcase)) {
90
+ this.tests.push(
91
+ new Test.Unit.Testcase(
92
+ this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
93
+ testcases[testcase], testcases["setup"], testcases["teardown"]
94
+ ));
95
+ }
96
+ }
97
+ }
98
+ }
99
+ this.currentTest = 0;
100
+ this.logger = new XULTestCase.Logger(this.options.testLog);
101
+ },
102
+
103
+ postResults: function($super) {
104
+ $super();
105
+ this._finishUpTestCase();
106
+ },
107
+
108
+ _finishUpTestCase: function() {
109
+ var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
110
+ var data = {name: this.name, unpassedTests: this.logger.unpassedTests,
111
+ summary: this._summaryData(), testLength: this.tests.length};
112
+ // Need to pass between observers as JSON to avoid crashes. Wee.
113
+ observerService.notifyObservers(null, "xultestrunner-testdone", Object.toJSON(data));
114
+ },
115
+
116
+ _summaryData: function() {
117
+ var assertions = 0, failures = 0, errors = 0;
118
+ $A(this.tests).each(function(test) {
119
+ assertions += test.assertions;
120
+ failures += test.failures;
121
+ errors += test.errors;
122
+ });
123
+ return {assertions: assertions, failures: failures, errors: errors};
124
+ }
125
+
126
+ });
@@ -0,0 +1,114 @@
1
+ var XULTestRunner = Class.create({
2
+ initialize: function(options) {
3
+ this.options = options;
4
+ this._testResults = [];
5
+ this.appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
6
+ },
7
+
8
+ handleExit: function (exitWithFailure) {
9
+ if (exitWithFailure) {
10
+ puts("Test Failures!");
11
+ // We kill ourselves this way because we need a non-zero exit status. This is really hacky.
12
+ // Really, really hacky.
13
+ var envFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
14
+ envFile.initWithPath("/usr/bin/env");
15
+ var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
16
+ process.init(envFile);
17
+ process.run(true, ["ruby", "-e", "Process.kill(9, Process.ppid)"], 3);
18
+ }
19
+ this.appStartup.quit(Ci.nsIAppStartup.eForceQuit);
20
+ },
21
+
22
+
23
+ go: function () {
24
+ this._registerObservers();
25
+ var testsToRun = $A(this.options.tests).collect(function(testFile) { return '"' + testFile.path + '"'; });
26
+ puts("Running tests...");
27
+ puts(testsToRun.join(" ") + "\n");
28
+ this._runNextTest();
29
+ },
30
+
31
+ observe: function(subject, topic, data) {
32
+ // Need to pass between observers as JSON to avoid crashes. Wee.
33
+ this._testResults.push(data.evalJSON());
34
+ this._runNextTest();
35
+ },
36
+
37
+ _runNextTest: function() {
38
+ var testFile = this.options.tests.shift();
39
+ if (testFile) {
40
+ this._runTest(testFile);
41
+ } else {
42
+ this._finishTestRun()
43
+ }
44
+ },
45
+
46
+ _runTest: function(testFile) {
47
+ var qs = Object.toQueryString({testFile: "file://" + encodeURI(testFile.path)});
48
+ var harnessWithPath = this.options.harnessURI + "?" + qs;
49
+ this.options.browser.loadURI(harnessWithPath);
50
+ },
51
+
52
+ _finishTestRun: function() {
53
+ print("\n\n");
54
+ var exitWithFailure = this._doSummary();
55
+ this.handleExit(exitWithFailure);
56
+ },
57
+
58
+ _doSummary: function() {
59
+ var testRunners = 0, testCases = 0, unpassedLength = 0, assertions = 0, failures = 0, errors = 0;
60
+ $A(this._testResults).each(function(testResult) {
61
+ $A(testResult.unpassedTests).each(function(unpassedTest) {
62
+ unpassedLength += 1;
63
+ this._showSummaryForTestResult(testResult.name, unpassedTest, unpassedLength);
64
+ }, this);
65
+ testRunners++;
66
+ testCases += testResult.testLength;
67
+ assertions += testResult.summary.assertions;
68
+ failures += testResult.summary.failures;
69
+ errors += testResult.summary.errors;
70
+ }, this);
71
+
72
+ var overallSummary = "\n\n#{runners} runners, #{cases} cases, #{unpassed} unpassed, #{assertions} assertions, #{failures} failures, #{errors} errors.\n";
73
+ puts(overallSummary.interpolate({runners: testRunners, cases: testCases,
74
+ unpassed: unpassedLength, assertions: assertions, failures: failures, errors: errors}));
75
+ return unpassedLength > 0;
76
+ },
77
+
78
+ _showSummaryForTestResult: function(testName, testResult, failureIndex) {
79
+ puts("#{index}) #{testName} - #{name}".interpolate({index: failureIndex, testName: testName, name: testResult.name}));
80
+ puts(" " + testResult.status.toUpperCase() + ":\n");
81
+ puts(testResult.summary.replace(/^/mg, " ") + "\n\n");
82
+ },
83
+
84
+ _registerObservers: function() {
85
+ var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
86
+ observerService.addObserver(this, "xultestrunner-testdone", false);
87
+ }
88
+ });
89
+
90
+ Object.extend(XULTestRunner, {
91
+ testFileMatcher: /_test\.js$/,
92
+ initializeFromCmdArgs: function(cmdArgs, options) {
93
+ if (cmdArgs.testDir) {
94
+ var tests = this._findTestsFromDir(cmdArgs.testDir);
95
+ } else if (cmdArgs.testFile) {
96
+ var tests = [cmdArgs.testFile];
97
+ }
98
+ return new this(Object.extend({tests: $A(tests)}, options));
99
+ },
100
+
101
+ _findTestsFromDir: function(rootDir) {
102
+ var testFiles = [];
103
+ var entries = rootDir.directoryEntries;
104
+ while (entries.hasMoreElements()) {
105
+ var entry = entries.getNext().QueryInterface(Ci.nsIFile);
106
+ if (entry.isDirectory()) {
107
+ testFiles = testFiles.concat(this._findTestsFromDir(entry));
108
+ } else if (this.testFileMatcher.test(entry.path)) {
109
+ testFiles.push(entry);
110
+ }
111
+ }
112
+ return testFiles;
113
+ }
114
+ });
@@ -0,0 +1,16 @@
1
+ Copyright (c) 2005-2008 Sam Stephenson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
16
+ SOFTWARE.