Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | # |
michael@0 | 3 | # Any copyright is dedicated to the Public Domain. |
michael@0 | 4 | # http://creativecommons.org/publicdomain/zero/1.0/ |
michael@0 | 5 | # |
michael@0 | 6 | |
michael@0 | 7 | from __future__ import with_statement |
michael@0 | 8 | import sys, os, unittest, tempfile, shutil |
michael@0 | 9 | import mozinfo |
michael@0 | 10 | |
michael@0 | 11 | from StringIO import StringIO |
michael@0 | 12 | from xml.etree.ElementTree import ElementTree |
michael@0 | 13 | |
michael@0 | 14 | from mozbuild.base import MozbuildObject |
michael@0 | 15 | build_obj = MozbuildObject.from_environment() |
michael@0 | 16 | |
michael@0 | 17 | from runxpcshelltests import XPCShellTests |
michael@0 | 18 | |
michael@0 | 19 | mozinfo.find_and_update_from_json() |
michael@0 | 20 | |
michael@0 | 21 | objdir = build_obj.topobjdir.encode("utf-8") |
michael@0 | 22 | xpcshellBin = os.path.join(objdir, "dist", "bin", "xpcshell") |
michael@0 | 23 | if sys.platform == "win32": |
michael@0 | 24 | xpcshellBin += ".exe" |
michael@0 | 25 | |
michael@0 | 26 | SIMPLE_PASSING_TEST = "function run_test() { do_check_true(true); }" |
michael@0 | 27 | SIMPLE_FAILING_TEST = "function run_test() { do_check_true(false); }" |
michael@0 | 28 | |
michael@0 | 29 | ADD_TEST_SIMPLE = ''' |
michael@0 | 30 | function run_test() { run_next_test(); } |
michael@0 | 31 | |
michael@0 | 32 | add_test(function test_simple() { |
michael@0 | 33 | do_check_true(true); |
michael@0 | 34 | run_next_test(); |
michael@0 | 35 | }); |
michael@0 | 36 | ''' |
michael@0 | 37 | |
michael@0 | 38 | ADD_TEST_FAILING = ''' |
michael@0 | 39 | function run_test() { run_next_test(); } |
michael@0 | 40 | |
michael@0 | 41 | add_test(function test_failing() { |
michael@0 | 42 | do_check_true(false); |
michael@0 | 43 | run_next_test(); |
michael@0 | 44 | }); |
michael@0 | 45 | ''' |
michael@0 | 46 | |
michael@0 | 47 | CHILD_TEST_PASSING = ''' |
michael@0 | 48 | function run_test () { run_next_test(); } |
michael@0 | 49 | |
michael@0 | 50 | add_test(function test_child_simple () { |
michael@0 | 51 | run_test_in_child("test_pass.js"); |
michael@0 | 52 | run_next_test(); |
michael@0 | 53 | }); |
michael@0 | 54 | ''' |
michael@0 | 55 | |
michael@0 | 56 | CHILD_TEST_FAILING = ''' |
michael@0 | 57 | function run_test () { run_next_test(); } |
michael@0 | 58 | |
michael@0 | 59 | add_test(function test_child_simple () { |
michael@0 | 60 | run_test_in_child("test_fail.js"); |
michael@0 | 61 | run_next_test(); |
michael@0 | 62 | }); |
michael@0 | 63 | ''' |
michael@0 | 64 | |
michael@0 | 65 | CHILD_TEST_HANG = ''' |
michael@0 | 66 | function run_test () { run_next_test(); } |
michael@0 | 67 | |
michael@0 | 68 | add_test(function test_child_simple () { |
michael@0 | 69 | do_test_pending("hang test"); |
michael@0 | 70 | do_load_child_test_harness(); |
michael@0 | 71 | sendCommand("_log('child_test_start', {_message: 'CHILD-TEST-STARTED'}); " + |
michael@0 | 72 | + "const _TEST_FILE=['test_pass.js']; _execute_test(); ", |
michael@0 | 73 | do_test_finished); |
michael@0 | 74 | run_next_test(); |
michael@0 | 75 | }); |
michael@0 | 76 | ''' |
michael@0 | 77 | |
michael@0 | 78 | ADD_TASK_SINGLE = ''' |
michael@0 | 79 | Components.utils.import("resource://gre/modules/Promise.jsm"); |
michael@0 | 80 | |
michael@0 | 81 | function run_test() { run_next_test(); } |
michael@0 | 82 | |
michael@0 | 83 | add_task(function test_task() { |
michael@0 | 84 | yield Promise.resolve(true); |
michael@0 | 85 | yield Promise.resolve(false); |
michael@0 | 86 | }); |
michael@0 | 87 | ''' |
michael@0 | 88 | |
michael@0 | 89 | ADD_TASK_MULTIPLE = ''' |
michael@0 | 90 | Components.utils.import("resource://gre/modules/Promise.jsm"); |
michael@0 | 91 | |
michael@0 | 92 | function run_test() { run_next_test(); } |
michael@0 | 93 | |
michael@0 | 94 | add_task(function test_task() { |
michael@0 | 95 | yield Promise.resolve(true); |
michael@0 | 96 | }); |
michael@0 | 97 | |
michael@0 | 98 | add_task(function test_2() { |
michael@0 | 99 | yield Promise.resolve(true); |
michael@0 | 100 | }); |
michael@0 | 101 | ''' |
michael@0 | 102 | |
michael@0 | 103 | ADD_TASK_REJECTED = ''' |
michael@0 | 104 | Components.utils.import("resource://gre/modules/Promise.jsm"); |
michael@0 | 105 | |
michael@0 | 106 | function run_test() { run_next_test(); } |
michael@0 | 107 | |
michael@0 | 108 | add_task(function test_failing() { |
michael@0 | 109 | yield Promise.reject(new Error("I fail.")); |
michael@0 | 110 | }); |
michael@0 | 111 | ''' |
michael@0 | 112 | |
michael@0 | 113 | ADD_TASK_FAILURE_INSIDE = ''' |
michael@0 | 114 | Components.utils.import("resource://gre/modules/Promise.jsm"); |
michael@0 | 115 | |
michael@0 | 116 | function run_test() { run_next_test(); } |
michael@0 | 117 | |
michael@0 | 118 | add_task(function test() { |
michael@0 | 119 | let result = yield Promise.resolve(false); |
michael@0 | 120 | |
michael@0 | 121 | do_check_true(result); |
michael@0 | 122 | }); |
michael@0 | 123 | ''' |
michael@0 | 124 | |
michael@0 | 125 | ADD_TASK_RUN_NEXT_TEST = ''' |
michael@0 | 126 | function run_test() { run_next_test(); } |
michael@0 | 127 | |
michael@0 | 128 | add_task(function () { |
michael@0 | 129 | Assert.ok(true); |
michael@0 | 130 | |
michael@0 | 131 | run_next_test(); |
michael@0 | 132 | }); |
michael@0 | 133 | ''' |
michael@0 | 134 | |
michael@0 | 135 | ADD_TEST_THROW_STRING = ''' |
michael@0 | 136 | function run_test() {do_throw("Passing a string to do_throw")}; |
michael@0 | 137 | ''' |
michael@0 | 138 | |
michael@0 | 139 | ADD_TEST_THROW_OBJECT = ''' |
michael@0 | 140 | let error = { |
michael@0 | 141 | message: "Error object", |
michael@0 | 142 | fileName: "failure.js", |
michael@0 | 143 | stack: "ERROR STACK", |
michael@0 | 144 | toString: function() {return this.message;} |
michael@0 | 145 | }; |
michael@0 | 146 | function run_test() {do_throw(error)}; |
michael@0 | 147 | ''' |
michael@0 | 148 | |
michael@0 | 149 | ADD_TEST_REPORT_OBJECT = ''' |
michael@0 | 150 | let error = { |
michael@0 | 151 | message: "Error object", |
michael@0 | 152 | fileName: "failure.js", |
michael@0 | 153 | stack: "ERROR STACK", |
michael@0 | 154 | toString: function() {return this.message;} |
michael@0 | 155 | }; |
michael@0 | 156 | function run_test() {do_report_unexpected_exception(error)}; |
michael@0 | 157 | ''' |
michael@0 | 158 | |
michael@0 | 159 | # A test for genuine JS-generated Error objects |
michael@0 | 160 | ADD_TEST_REPORT_REF_ERROR = ''' |
michael@0 | 161 | function run_test() { |
michael@0 | 162 | let obj = {blah: 0}; |
michael@0 | 163 | try { |
michael@0 | 164 | obj.noSuchFunction(); |
michael@0 | 165 | } |
michael@0 | 166 | catch (error) { |
michael@0 | 167 | do_report_unexpected_exception(error); |
michael@0 | 168 | } |
michael@0 | 169 | }; |
michael@0 | 170 | ''' |
michael@0 | 171 | |
michael@0 | 172 | # A test for failure to load a test due to a syntax error |
michael@0 | 173 | LOAD_ERROR_SYNTAX_ERROR = ''' |
michael@0 | 174 | function run_test( |
michael@0 | 175 | ''' |
michael@0 | 176 | |
michael@0 | 177 | # A test for failure to load a test due to an error other than a syntax error |
michael@0 | 178 | LOAD_ERROR_OTHER_ERROR = ''' |
michael@0 | 179 | function run_test() { |
michael@0 | 180 | yield "foo"; |
michael@0 | 181 | return "foo"; // can't use return in a generator! |
michael@0 | 182 | }; |
michael@0 | 183 | ''' |
michael@0 | 184 | |
michael@0 | 185 | # A test for asynchronous cleanup functions |
michael@0 | 186 | ASYNC_CLEANUP = ''' |
michael@0 | 187 | function run_test() { |
michael@0 | 188 | Components.utils.import("resource://gre/modules/Promise.jsm", this); |
michael@0 | 189 | |
michael@0 | 190 | // The list of checkpoints in the order we encounter them. |
michael@0 | 191 | let checkpoints = []; |
michael@0 | 192 | |
michael@0 | 193 | // Cleanup tasks, in reverse order |
michael@0 | 194 | do_register_cleanup(function cleanup_checkout() { |
michael@0 | 195 | do_check_eq(checkpoints.join(""), "1234"); |
michael@0 | 196 | do_print("At this stage, the test has succeeded"); |
michael@0 | 197 | do_throw("Throwing an error to force displaying the log"); |
michael@0 | 198 | }); |
michael@0 | 199 | |
michael@0 | 200 | do_register_cleanup(function sync_cleanup_2() { |
michael@0 | 201 | checkpoints.push(4); |
michael@0 | 202 | }); |
michael@0 | 203 | |
michael@0 | 204 | do_register_cleanup(function async_cleanup_2() { |
michael@0 | 205 | let deferred = Promise.defer(); |
michael@0 | 206 | do_execute_soon(deferred.resolve); |
michael@0 | 207 | return deferred.promise.then(function() { |
michael@0 | 208 | checkpoints.push(3); |
michael@0 | 209 | }); |
michael@0 | 210 | }); |
michael@0 | 211 | |
michael@0 | 212 | do_register_cleanup(function sync_cleanup() { |
michael@0 | 213 | checkpoints.push(2); |
michael@0 | 214 | }); |
michael@0 | 215 | |
michael@0 | 216 | do_register_cleanup(function async_cleanup() { |
michael@0 | 217 | let deferred = Promise.defer(); |
michael@0 | 218 | do_execute_soon(deferred.resolve); |
michael@0 | 219 | return deferred.promise.then(function() { |
michael@0 | 220 | checkpoints.push(1); |
michael@0 | 221 | }); |
michael@0 | 222 | }); |
michael@0 | 223 | |
michael@0 | 224 | } |
michael@0 | 225 | ''' |
michael@0 | 226 | |
michael@0 | 227 | |
michael@0 | 228 | class XPCShellTestsTests(unittest.TestCase): |
michael@0 | 229 | """ |
michael@0 | 230 | Yes, these are unit tests for a unit test harness. |
michael@0 | 231 | """ |
michael@0 | 232 | def setUp(self): |
michael@0 | 233 | self.log = StringIO() |
michael@0 | 234 | self.tempdir = tempfile.mkdtemp() |
michael@0 | 235 | self.x = XPCShellTests(log=self.log) |
michael@0 | 236 | |
michael@0 | 237 | def tearDown(self): |
michael@0 | 238 | shutil.rmtree(self.tempdir) |
michael@0 | 239 | |
michael@0 | 240 | def writeFile(self, name, contents): |
michael@0 | 241 | """ |
michael@0 | 242 | Write |contents| to a file named |name| in the temp directory, |
michael@0 | 243 | and return the full path to the file. |
michael@0 | 244 | """ |
michael@0 | 245 | fullpath = os.path.join(self.tempdir, name) |
michael@0 | 246 | with open(fullpath, "w") as f: |
michael@0 | 247 | f.write(contents) |
michael@0 | 248 | return fullpath |
michael@0 | 249 | |
michael@0 | 250 | def writeManifest(self, tests): |
michael@0 | 251 | """ |
michael@0 | 252 | Write an xpcshell.ini in the temp directory and set |
michael@0 | 253 | self.manifest to its pathname. |tests| is a list containing |
michael@0 | 254 | either strings (for test names), or tuples with a test name |
michael@0 | 255 | as the first element and manifest conditions as the following |
michael@0 | 256 | elements. |
michael@0 | 257 | """ |
michael@0 | 258 | testlines = [] |
michael@0 | 259 | for t in tests: |
michael@0 | 260 | testlines.append("[%s]" % (t if isinstance(t, basestring) |
michael@0 | 261 | else t[0])) |
michael@0 | 262 | if isinstance(t, tuple): |
michael@0 | 263 | testlines.extend(t[1:]) |
michael@0 | 264 | self.manifest = self.writeFile("xpcshell.ini", """ |
michael@0 | 265 | [DEFAULT] |
michael@0 | 266 | head = |
michael@0 | 267 | tail = |
michael@0 | 268 | |
michael@0 | 269 | """ + "\n".join(testlines)) |
michael@0 | 270 | |
michael@0 | 271 | def assertTestResult(self, expected, shuffle=False, xunitFilename=None, verbose=False): |
michael@0 | 272 | """ |
michael@0 | 273 | Assert that self.x.runTests with manifest=self.manifest |
michael@0 | 274 | returns |expected|. |
michael@0 | 275 | """ |
michael@0 | 276 | self.assertEquals(expected, |
michael@0 | 277 | self.x.runTests(xpcshellBin, |
michael@0 | 278 | manifest=self.manifest, |
michael@0 | 279 | mozInfo=mozinfo.info, |
michael@0 | 280 | shuffle=shuffle, |
michael@0 | 281 | testsRootDir=self.tempdir, |
michael@0 | 282 | verbose=verbose, |
michael@0 | 283 | xunitFilename=xunitFilename, |
michael@0 | 284 | sequential=True), |
michael@0 | 285 | msg="""Tests should have %s, log: |
michael@0 | 286 | ======== |
michael@0 | 287 | %s |
michael@0 | 288 | ======== |
michael@0 | 289 | """ % ("passed" if expected else "failed", self.log.getvalue())) |
michael@0 | 290 | |
michael@0 | 291 | def _assertLog(self, s, expected): |
michael@0 | 292 | l = self.log.getvalue() |
michael@0 | 293 | self.assertEqual(expected, s in l, |
michael@0 | 294 | msg="""Value %s %s in log: |
michael@0 | 295 | ======== |
michael@0 | 296 | %s |
michael@0 | 297 | ========""" % (s, "expected" if expected else "not expected", l)) |
michael@0 | 298 | |
michael@0 | 299 | def assertInLog(self, s): |
michael@0 | 300 | """ |
michael@0 | 301 | Assert that the string |s| is contained in self.log. |
michael@0 | 302 | """ |
michael@0 | 303 | self._assertLog(s, True) |
michael@0 | 304 | |
michael@0 | 305 | def assertNotInLog(self, s): |
michael@0 | 306 | """ |
michael@0 | 307 | Assert that the string |s| is not contained in self.log. |
michael@0 | 308 | """ |
michael@0 | 309 | self._assertLog(s, False) |
michael@0 | 310 | |
michael@0 | 311 | def testPass(self): |
michael@0 | 312 | """ |
michael@0 | 313 | Check that a simple test without any manifest conditions passes. |
michael@0 | 314 | """ |
michael@0 | 315 | self.writeFile("test_basic.js", SIMPLE_PASSING_TEST) |
michael@0 | 316 | self.writeManifest(["test_basic.js"]) |
michael@0 | 317 | |
michael@0 | 318 | self.assertTestResult(True) |
michael@0 | 319 | self.assertEquals(1, self.x.testCount) |
michael@0 | 320 | self.assertEquals(1, self.x.passCount) |
michael@0 | 321 | self.assertEquals(0, self.x.failCount) |
michael@0 | 322 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 323 | self.assertInLog("TEST-PASS") |
michael@0 | 324 | self.assertNotInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 325 | |
michael@0 | 326 | def testFail(self): |
michael@0 | 327 | """ |
michael@0 | 328 | Check that a simple failing test without any manifest conditions fails. |
michael@0 | 329 | """ |
michael@0 | 330 | self.writeFile("test_basic.js", SIMPLE_FAILING_TEST) |
michael@0 | 331 | self.writeManifest(["test_basic.js"]) |
michael@0 | 332 | |
michael@0 | 333 | self.assertTestResult(False) |
michael@0 | 334 | self.assertEquals(1, self.x.testCount) |
michael@0 | 335 | self.assertEquals(0, self.x.passCount) |
michael@0 | 336 | self.assertEquals(1, self.x.failCount) |
michael@0 | 337 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 338 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 339 | self.assertNotInLog("TEST-PASS") |
michael@0 | 340 | |
michael@0 | 341 | @unittest.skipIf(build_obj.defines.get('MOZ_B2G'), |
michael@0 | 342 | 'selftests with child processes fail on b2g desktop builds') |
michael@0 | 343 | def testChildPass(self): |
michael@0 | 344 | """ |
michael@0 | 345 | Check that a simple test running in a child process passes. |
michael@0 | 346 | """ |
michael@0 | 347 | self.writeFile("test_pass.js", SIMPLE_PASSING_TEST) |
michael@0 | 348 | self.writeFile("test_child_pass.js", CHILD_TEST_PASSING) |
michael@0 | 349 | self.writeManifest(["test_child_pass.js"]) |
michael@0 | 350 | |
michael@0 | 351 | self.assertTestResult(True, verbose=True) |
michael@0 | 352 | self.assertEquals(1, self.x.testCount) |
michael@0 | 353 | self.assertEquals(1, self.x.passCount) |
michael@0 | 354 | self.assertEquals(0, self.x.failCount) |
michael@0 | 355 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 356 | self.assertInLog("TEST-PASS") |
michael@0 | 357 | self.assertInLog("CHILD-TEST-STARTED") |
michael@0 | 358 | self.assertInLog("CHILD-TEST-COMPLETED") |
michael@0 | 359 | self.assertNotInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 360 | |
michael@0 | 361 | |
michael@0 | 362 | @unittest.skipIf(build_obj.defines.get('MOZ_B2G'), |
michael@0 | 363 | 'selftests with child processes fail on b2g desktop builds') |
michael@0 | 364 | def testChildFail(self): |
michael@0 | 365 | """ |
michael@0 | 366 | Check that a simple failing test running in a child process fails. |
michael@0 | 367 | """ |
michael@0 | 368 | self.writeFile("test_fail.js", SIMPLE_FAILING_TEST) |
michael@0 | 369 | self.writeFile("test_child_fail.js", CHILD_TEST_FAILING) |
michael@0 | 370 | self.writeManifest(["test_child_fail.js"]) |
michael@0 | 371 | |
michael@0 | 372 | self.assertTestResult(False) |
michael@0 | 373 | self.assertEquals(1, self.x.testCount) |
michael@0 | 374 | self.assertEquals(0, self.x.passCount) |
michael@0 | 375 | self.assertEquals(1, self.x.failCount) |
michael@0 | 376 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 377 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 378 | self.assertInLog("CHILD-TEST-STARTED") |
michael@0 | 379 | self.assertInLog("CHILD-TEST-COMPLETED") |
michael@0 | 380 | self.assertNotInLog("TEST-PASS") |
michael@0 | 381 | |
michael@0 | 382 | @unittest.skipIf(build_obj.defines.get('MOZ_B2G'), |
michael@0 | 383 | 'selftests with child processes fail on b2g desktop builds') |
michael@0 | 384 | def testChildHang(self): |
michael@0 | 385 | """ |
michael@0 | 386 | Check that incomplete output from a child process results in a |
michael@0 | 387 | test failure. |
michael@0 | 388 | """ |
michael@0 | 389 | self.writeFile("test_pass.js", SIMPLE_PASSING_TEST) |
michael@0 | 390 | self.writeFile("test_child_hang.js", CHILD_TEST_HANG) |
michael@0 | 391 | self.writeManifest(["test_child_hang.js"]) |
michael@0 | 392 | |
michael@0 | 393 | self.assertTestResult(False) |
michael@0 | 394 | self.assertEquals(1, self.x.testCount) |
michael@0 | 395 | self.assertEquals(0, self.x.passCount) |
michael@0 | 396 | self.assertEquals(1, self.x.failCount) |
michael@0 | 397 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 398 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 399 | self.assertInLog("CHILD-TEST-STARTED") |
michael@0 | 400 | self.assertNotInLog("CHILD-TEST-COMPLETED") |
michael@0 | 401 | self.assertNotInLog("TEST-PASS") |
michael@0 | 402 | |
michael@0 | 403 | def testSyntaxError(self): |
michael@0 | 404 | """ |
michael@0 | 405 | Check that running a test file containing a syntax error produces |
michael@0 | 406 | a test failure and expected output. |
michael@0 | 407 | """ |
michael@0 | 408 | self.writeFile("test_syntax_error.js", '"') |
michael@0 | 409 | self.writeManifest(["test_syntax_error.js"]) |
michael@0 | 410 | |
michael@0 | 411 | self.assertTestResult(False, verbose=True) |
michael@0 | 412 | self.assertEquals(1, self.x.testCount) |
michael@0 | 413 | self.assertEquals(0, self.x.passCount) |
michael@0 | 414 | self.assertEquals(1, self.x.failCount) |
michael@0 | 415 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 416 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 417 | self.assertNotInLog("TEST-PASS") |
michael@0 | 418 | |
michael@0 | 419 | def testPassFail(self): |
michael@0 | 420 | """ |
michael@0 | 421 | Check that running more than one test works. |
michael@0 | 422 | """ |
michael@0 | 423 | self.writeFile("test_pass.js", SIMPLE_PASSING_TEST) |
michael@0 | 424 | self.writeFile("test_fail.js", SIMPLE_FAILING_TEST) |
michael@0 | 425 | self.writeManifest(["test_pass.js", "test_fail.js"]) |
michael@0 | 426 | |
michael@0 | 427 | self.assertTestResult(False) |
michael@0 | 428 | self.assertEquals(2, self.x.testCount) |
michael@0 | 429 | self.assertEquals(1, self.x.passCount) |
michael@0 | 430 | self.assertEquals(1, self.x.failCount) |
michael@0 | 431 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 432 | self.assertInLog("TEST-PASS") |
michael@0 | 433 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 434 | |
michael@0 | 435 | def testSkip(self): |
michael@0 | 436 | """ |
michael@0 | 437 | Check that a simple failing test skipped in the manifest does |
michael@0 | 438 | not cause failure. |
michael@0 | 439 | """ |
michael@0 | 440 | self.writeFile("test_basic.js", SIMPLE_FAILING_TEST) |
michael@0 | 441 | self.writeManifest([("test_basic.js", "skip-if = true")]) |
michael@0 | 442 | self.assertTestResult(True) |
michael@0 | 443 | self.assertEquals(1, self.x.testCount) |
michael@0 | 444 | self.assertEquals(0, self.x.passCount) |
michael@0 | 445 | self.assertEquals(0, self.x.failCount) |
michael@0 | 446 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 447 | self.assertNotInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 448 | self.assertNotInLog("TEST-PASS") |
michael@0 | 449 | |
michael@0 | 450 | def testKnownFail(self): |
michael@0 | 451 | """ |
michael@0 | 452 | Check that a simple failing test marked as known-fail in the manifest |
michael@0 | 453 | does not cause failure. |
michael@0 | 454 | """ |
michael@0 | 455 | self.writeFile("test_basic.js", SIMPLE_FAILING_TEST) |
michael@0 | 456 | self.writeManifest([("test_basic.js", "fail-if = true")]) |
michael@0 | 457 | self.assertTestResult(True) |
michael@0 | 458 | self.assertEquals(1, self.x.testCount) |
michael@0 | 459 | self.assertEquals(0, self.x.passCount) |
michael@0 | 460 | self.assertEquals(0, self.x.failCount) |
michael@0 | 461 | self.assertEquals(1, self.x.todoCount) |
michael@0 | 462 | self.assertInLog("TEST-KNOWN-FAIL") |
michael@0 | 463 | # This should be suppressed because the harness doesn't include |
michael@0 | 464 | # the full log from the xpcshell run when things pass. |
michael@0 | 465 | self.assertNotInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 466 | self.assertNotInLog("TEST-PASS") |
michael@0 | 467 | |
michael@0 | 468 | def testUnexpectedPass(self): |
michael@0 | 469 | """ |
michael@0 | 470 | Check that a simple failing test marked as known-fail in the manifest |
michael@0 | 471 | that passes causes an unexpected pass. |
michael@0 | 472 | """ |
michael@0 | 473 | self.writeFile("test_basic.js", SIMPLE_PASSING_TEST) |
michael@0 | 474 | self.writeManifest([("test_basic.js", "fail-if = true")]) |
michael@0 | 475 | self.assertTestResult(False) |
michael@0 | 476 | self.assertEquals(1, self.x.testCount) |
michael@0 | 477 | self.assertEquals(0, self.x.passCount) |
michael@0 | 478 | self.assertEquals(1, self.x.failCount) |
michael@0 | 479 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 480 | # From the outer (Python) harness |
michael@0 | 481 | self.assertInLog("TEST-UNEXPECTED-PASS") |
michael@0 | 482 | self.assertNotInLog("TEST-KNOWN-FAIL") |
michael@0 | 483 | # From the inner (JS) harness |
michael@0 | 484 | self.assertInLog("TEST-PASS") |
michael@0 | 485 | |
michael@0 | 486 | def testReturnNonzero(self): |
michael@0 | 487 | """ |
michael@0 | 488 | Check that a test where xpcshell returns nonzero fails. |
michael@0 | 489 | """ |
michael@0 | 490 | self.writeFile("test_error.js", "throw 'foo'") |
michael@0 | 491 | self.writeManifest(["test_error.js"]) |
michael@0 | 492 | |
michael@0 | 493 | self.assertTestResult(False) |
michael@0 | 494 | self.assertEquals(1, self.x.testCount) |
michael@0 | 495 | self.assertEquals(0, self.x.passCount) |
michael@0 | 496 | self.assertEquals(1, self.x.failCount) |
michael@0 | 497 | self.assertEquals(0, self.x.todoCount) |
michael@0 | 498 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 499 | self.assertNotInLog("TEST-PASS") |
michael@0 | 500 | |
michael@0 | 501 | def testAddTestSimple(self): |
michael@0 | 502 | """ |
michael@0 | 503 | Ensure simple add_test() works. |
michael@0 | 504 | """ |
michael@0 | 505 | self.writeFile("test_add_test_simple.js", ADD_TEST_SIMPLE) |
michael@0 | 506 | self.writeManifest(["test_add_test_simple.js"]) |
michael@0 | 507 | |
michael@0 | 508 | self.assertTestResult(True) |
michael@0 | 509 | self.assertEquals(1, self.x.testCount) |
michael@0 | 510 | self.assertEquals(1, self.x.passCount) |
michael@0 | 511 | self.assertEquals(0, self.x.failCount) |
michael@0 | 512 | |
michael@0 | 513 | def testAddTestFailing(self): |
michael@0 | 514 | """ |
michael@0 | 515 | Ensure add_test() with a failing test is reported. |
michael@0 | 516 | """ |
michael@0 | 517 | self.writeFile("test_add_test_failing.js", ADD_TEST_FAILING) |
michael@0 | 518 | self.writeManifest(["test_add_test_failing.js"]) |
michael@0 | 519 | |
michael@0 | 520 | self.assertTestResult(False) |
michael@0 | 521 | self.assertEquals(1, self.x.testCount) |
michael@0 | 522 | self.assertEquals(0, self.x.passCount) |
michael@0 | 523 | self.assertEquals(1, self.x.failCount) |
michael@0 | 524 | |
michael@0 | 525 | def testAddTaskTestSingle(self): |
michael@0 | 526 | """ |
michael@0 | 527 | Ensure add_test_task() with a single passing test works. |
michael@0 | 528 | """ |
michael@0 | 529 | self.writeFile("test_add_task_simple.js", ADD_TASK_SINGLE) |
michael@0 | 530 | self.writeManifest(["test_add_task_simple.js"]) |
michael@0 | 531 | |
michael@0 | 532 | self.assertTestResult(True) |
michael@0 | 533 | self.assertEquals(1, self.x.testCount) |
michael@0 | 534 | self.assertEquals(1, self.x.passCount) |
michael@0 | 535 | self.assertEquals(0, self.x.failCount) |
michael@0 | 536 | |
michael@0 | 537 | def testAddTaskTestMultiple(self): |
michael@0 | 538 | """ |
michael@0 | 539 | Ensure multiple calls to add_test_task() work as expected. |
michael@0 | 540 | """ |
michael@0 | 541 | self.writeFile("test_add_task_multiple.js", |
michael@0 | 542 | ADD_TASK_MULTIPLE) |
michael@0 | 543 | self.writeManifest(["test_add_task_multiple.js"]) |
michael@0 | 544 | |
michael@0 | 545 | self.assertTestResult(True) |
michael@0 | 546 | self.assertEquals(1, self.x.testCount) |
michael@0 | 547 | self.assertEquals(1, self.x.passCount) |
michael@0 | 548 | self.assertEquals(0, self.x.failCount) |
michael@0 | 549 | |
michael@0 | 550 | def testAddTaskTestRejected(self): |
michael@0 | 551 | """ |
michael@0 | 552 | Ensure rejected task reports as failure. |
michael@0 | 553 | """ |
michael@0 | 554 | self.writeFile("test_add_task_rejected.js", |
michael@0 | 555 | ADD_TASK_REJECTED) |
michael@0 | 556 | self.writeManifest(["test_add_task_rejected.js"]) |
michael@0 | 557 | |
michael@0 | 558 | self.assertTestResult(False) |
michael@0 | 559 | self.assertEquals(1, self.x.testCount) |
michael@0 | 560 | self.assertEquals(0, self.x.passCount) |
michael@0 | 561 | self.assertEquals(1, self.x.failCount) |
michael@0 | 562 | |
michael@0 | 563 | def testAddTaskTestFailureInside(self): |
michael@0 | 564 | """ |
michael@0 | 565 | Ensure tests inside task are reported as failures. |
michael@0 | 566 | """ |
michael@0 | 567 | self.writeFile("test_add_task_failure_inside.js", |
michael@0 | 568 | ADD_TASK_FAILURE_INSIDE) |
michael@0 | 569 | self.writeManifest(["test_add_task_failure_inside.js"]) |
michael@0 | 570 | |
michael@0 | 571 | self.assertTestResult(False) |
michael@0 | 572 | self.assertEquals(1, self.x.testCount) |
michael@0 | 573 | self.assertEquals(0, self.x.passCount) |
michael@0 | 574 | self.assertEquals(1, self.x.failCount) |
michael@0 | 575 | |
michael@0 | 576 | def testAddTaskRunNextTest(self): |
michael@0 | 577 | """ |
michael@0 | 578 | Calling run_next_test() from inside add_task() results in failure. |
michael@0 | 579 | """ |
michael@0 | 580 | self.writeFile("test_add_task_run_next_test.js", |
michael@0 | 581 | ADD_TASK_RUN_NEXT_TEST) |
michael@0 | 582 | self.writeManifest(["test_add_task_run_next_test.js"]) |
michael@0 | 583 | |
michael@0 | 584 | self.assertTestResult(False) |
michael@0 | 585 | self.assertEquals(1, self.x.testCount) |
michael@0 | 586 | self.assertEquals(0, self.x.passCount) |
michael@0 | 587 | self.assertEquals(1, self.x.failCount) |
michael@0 | 588 | |
michael@0 | 589 | def testMissingHeadFile(self): |
michael@0 | 590 | """ |
michael@0 | 591 | Ensure that missing head file results in fatal error. |
michael@0 | 592 | """ |
michael@0 | 593 | self.writeFile("test_basic.js", SIMPLE_PASSING_TEST) |
michael@0 | 594 | self.writeManifest([("test_basic.js", "head = missing.js")]) |
michael@0 | 595 | |
michael@0 | 596 | raised = False |
michael@0 | 597 | |
michael@0 | 598 | try: |
michael@0 | 599 | # The actual return value is never checked because we raise. |
michael@0 | 600 | self.assertTestResult(True) |
michael@0 | 601 | except Exception, ex: |
michael@0 | 602 | raised = True |
michael@0 | 603 | self.assertEquals(ex.message[0:9], "head file") |
michael@0 | 604 | |
michael@0 | 605 | self.assertTrue(raised) |
michael@0 | 606 | |
michael@0 | 607 | def testMissingTailFile(self): |
michael@0 | 608 | """ |
michael@0 | 609 | Ensure that missing tail file results in fatal error. |
michael@0 | 610 | """ |
michael@0 | 611 | self.writeFile("test_basic.js", SIMPLE_PASSING_TEST) |
michael@0 | 612 | self.writeManifest([("test_basic.js", "tail = missing.js")]) |
michael@0 | 613 | |
michael@0 | 614 | raised = False |
michael@0 | 615 | |
michael@0 | 616 | try: |
michael@0 | 617 | self.assertTestResult(True) |
michael@0 | 618 | except Exception, ex: |
michael@0 | 619 | raised = True |
michael@0 | 620 | self.assertEquals(ex.message[0:9], "tail file") |
michael@0 | 621 | |
michael@0 | 622 | self.assertTrue(raised) |
michael@0 | 623 | |
michael@0 | 624 | def testRandomExecution(self): |
michael@0 | 625 | """ |
michael@0 | 626 | Check that random execution doesn't break. |
michael@0 | 627 | """ |
michael@0 | 628 | manifest = [] |
michael@0 | 629 | for i in range(0, 10): |
michael@0 | 630 | filename = "test_pass_%d.js" % i |
michael@0 | 631 | self.writeFile(filename, SIMPLE_PASSING_TEST) |
michael@0 | 632 | manifest.append(filename) |
michael@0 | 633 | |
michael@0 | 634 | self.writeManifest(manifest) |
michael@0 | 635 | self.assertTestResult(True, shuffle=True) |
michael@0 | 636 | self.assertEquals(10, self.x.testCount) |
michael@0 | 637 | self.assertEquals(10, self.x.passCount) |
michael@0 | 638 | |
michael@0 | 639 | def testXunitOutput(self): |
michael@0 | 640 | """ |
michael@0 | 641 | Check that Xunit XML files are written. |
michael@0 | 642 | """ |
michael@0 | 643 | self.writeFile("test_00.js", SIMPLE_PASSING_TEST) |
michael@0 | 644 | self.writeFile("test_01.js", SIMPLE_FAILING_TEST) |
michael@0 | 645 | self.writeFile("test_02.js", SIMPLE_PASSING_TEST) |
michael@0 | 646 | |
michael@0 | 647 | manifest = [ |
michael@0 | 648 | "test_00.js", |
michael@0 | 649 | "test_01.js", |
michael@0 | 650 | ("test_02.js", "skip-if = true") |
michael@0 | 651 | ] |
michael@0 | 652 | |
michael@0 | 653 | self.writeManifest(manifest) |
michael@0 | 654 | |
michael@0 | 655 | filename = os.path.join(self.tempdir, "xunit.xml") |
michael@0 | 656 | |
michael@0 | 657 | self.assertTestResult(False, xunitFilename=filename) |
michael@0 | 658 | |
michael@0 | 659 | self.assertTrue(os.path.exists(filename)) |
michael@0 | 660 | self.assertTrue(os.path.getsize(filename) > 0) |
michael@0 | 661 | |
michael@0 | 662 | tree = ElementTree() |
michael@0 | 663 | tree.parse(filename) |
michael@0 | 664 | suite = tree.getroot() |
michael@0 | 665 | |
michael@0 | 666 | self.assertTrue(suite is not None) |
michael@0 | 667 | self.assertEqual(suite.get("tests"), "3") |
michael@0 | 668 | self.assertEqual(suite.get("failures"), "1") |
michael@0 | 669 | self.assertEqual(suite.get("skip"), "1") |
michael@0 | 670 | |
michael@0 | 671 | testcases = suite.findall("testcase") |
michael@0 | 672 | self.assertEqual(len(testcases), 3) |
michael@0 | 673 | |
michael@0 | 674 | for testcase in testcases: |
michael@0 | 675 | attributes = testcase.keys() |
michael@0 | 676 | self.assertTrue("classname" in attributes) |
michael@0 | 677 | self.assertTrue("name" in attributes) |
michael@0 | 678 | self.assertTrue("time" in attributes) |
michael@0 | 679 | |
michael@0 | 680 | self.assertTrue(testcases[1].find("failure") is not None) |
michael@0 | 681 | self.assertTrue(testcases[2].find("skipped") is not None) |
michael@0 | 682 | |
michael@0 | 683 | def testDoThrowString(self): |
michael@0 | 684 | """ |
michael@0 | 685 | Check that do_throw produces reasonable messages when the |
michael@0 | 686 | input is a string instead of an object |
michael@0 | 687 | """ |
michael@0 | 688 | self.writeFile("test_error.js", ADD_TEST_THROW_STRING) |
michael@0 | 689 | self.writeManifest(["test_error.js"]) |
michael@0 | 690 | |
michael@0 | 691 | self.assertTestResult(False) |
michael@0 | 692 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 693 | self.assertInLog("Passing a string to do_throw") |
michael@0 | 694 | self.assertNotInLog("TEST-PASS") |
michael@0 | 695 | |
michael@0 | 696 | def testDoThrowForeignObject(self): |
michael@0 | 697 | """ |
michael@0 | 698 | Check that do_throw produces reasonable messages when the |
michael@0 | 699 | input is a generic object with 'filename', 'message' and 'stack' attributes |
michael@0 | 700 | but 'object instanceof Error' returns false |
michael@0 | 701 | """ |
michael@0 | 702 | self.writeFile("test_error.js", ADD_TEST_THROW_OBJECT) |
michael@0 | 703 | self.writeManifest(["test_error.js"]) |
michael@0 | 704 | |
michael@0 | 705 | self.assertTestResult(False) |
michael@0 | 706 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 707 | self.assertInLog("failure.js") |
michael@0 | 708 | self.assertInLog("Error object") |
michael@0 | 709 | self.assertInLog("ERROR STACK") |
michael@0 | 710 | self.assertNotInLog("TEST-PASS") |
michael@0 | 711 | |
michael@0 | 712 | def testDoReportForeignObject(self): |
michael@0 | 713 | """ |
michael@0 | 714 | Check that do_report_unexpected_exception produces reasonable messages when the |
michael@0 | 715 | input is a generic object with 'filename', 'message' and 'stack' attributes |
michael@0 | 716 | but 'object instanceof Error' returns false |
michael@0 | 717 | """ |
michael@0 | 718 | self.writeFile("test_error.js", ADD_TEST_REPORT_OBJECT) |
michael@0 | 719 | self.writeManifest(["test_error.js"]) |
michael@0 | 720 | |
michael@0 | 721 | self.assertTestResult(False) |
michael@0 | 722 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 723 | self.assertInLog("failure.js") |
michael@0 | 724 | self.assertInLog("Error object") |
michael@0 | 725 | self.assertInLog("ERROR STACK") |
michael@0 | 726 | self.assertNotInLog("TEST-PASS") |
michael@0 | 727 | |
michael@0 | 728 | def testDoReportRefError(self): |
michael@0 | 729 | """ |
michael@0 | 730 | Check that do_report_unexpected_exception produces reasonable messages when the |
michael@0 | 731 | input is a JS-generated Error |
michael@0 | 732 | """ |
michael@0 | 733 | self.writeFile("test_error.js", ADD_TEST_REPORT_REF_ERROR) |
michael@0 | 734 | self.writeManifest(["test_error.js"]) |
michael@0 | 735 | |
michael@0 | 736 | self.assertTestResult(False) |
michael@0 | 737 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 738 | self.assertInLog("test_error.js") |
michael@0 | 739 | self.assertInLog("obj.noSuchFunction is not a function") |
michael@0 | 740 | self.assertInLog("run_test@") |
michael@0 | 741 | self.assertNotInLog("TEST-PASS") |
michael@0 | 742 | |
michael@0 | 743 | def testDoReportSyntaxError(self): |
michael@0 | 744 | """ |
michael@0 | 745 | Check that attempting to load a test file containing a syntax error |
michael@0 | 746 | generates details of the error in the log |
michael@0 | 747 | """ |
michael@0 | 748 | self.writeFile("test_error.js", LOAD_ERROR_SYNTAX_ERROR) |
michael@0 | 749 | self.writeManifest(["test_error.js"]) |
michael@0 | 750 | |
michael@0 | 751 | self.assertTestResult(False) |
michael@0 | 752 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 753 | self.assertInLog("test_error.js") |
michael@0 | 754 | self.assertInLog("test_error.js contains SyntaxError") |
michael@0 | 755 | self.assertInLog("Diagnostic: SyntaxError: missing formal parameter at") |
michael@0 | 756 | self.assertInLog("test_error.js:3") |
michael@0 | 757 | self.assertNotInLog("TEST-PASS") |
michael@0 | 758 | |
michael@0 | 759 | def testDoReportNonSyntaxError(self): |
michael@0 | 760 | """ |
michael@0 | 761 | Check that attempting to load a test file containing an error other |
michael@0 | 762 | than a syntax error generates details of the error in the log |
michael@0 | 763 | """ |
michael@0 | 764 | self.writeFile("test_error.js", LOAD_ERROR_OTHER_ERROR) |
michael@0 | 765 | self.writeManifest(["test_error.js"]) |
michael@0 | 766 | |
michael@0 | 767 | self.assertTestResult(False) |
michael@0 | 768 | self.assertInLog("TEST-UNEXPECTED-FAIL") |
michael@0 | 769 | self.assertInLog("Diagnostic: TypeError: generator function run_test returns a value at") |
michael@0 | 770 | self.assertInLog("test_error.js:4") |
michael@0 | 771 | self.assertNotInLog("TEST-PASS") |
michael@0 | 772 | |
michael@0 | 773 | def testAsyncCleanup(self): |
michael@0 | 774 | """ |
michael@0 | 775 | Check that do_register_cleanup handles nicely cleanup tasks that |
michael@0 | 776 | return a promise |
michael@0 | 777 | """ |
michael@0 | 778 | self.writeFile("test_asyncCleanup.js", ASYNC_CLEANUP) |
michael@0 | 779 | self.writeManifest(["test_asyncCleanup.js"]) |
michael@0 | 780 | self.assertTestResult(False) |
michael@0 | 781 | self.assertInLog("\"1234\" == \"1234\"") |
michael@0 | 782 | self.assertInLog("At this stage, the test has succeeded") |
michael@0 | 783 | self.assertInLog("Throwing an error to force displaying the log") |
michael@0 | 784 | |
michael@0 | 785 | if __name__ == "__main__": |
michael@0 | 786 | unittest.main() |