testing/xpcshell/selftest.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/testing/xpcshell/selftest.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,786 @@
     1.4 +#!/usr/bin/env python
     1.5 +#
     1.6 +# Any copyright is dedicated to the Public Domain.
     1.7 +# http://creativecommons.org/publicdomain/zero/1.0/
     1.8 +#
     1.9 +
    1.10 +from __future__ import with_statement
    1.11 +import sys, os, unittest, tempfile, shutil
    1.12 +import mozinfo
    1.13 +
    1.14 +from StringIO import StringIO
    1.15 +from xml.etree.ElementTree import ElementTree
    1.16 +
    1.17 +from mozbuild.base import MozbuildObject
    1.18 +build_obj = MozbuildObject.from_environment()
    1.19 +
    1.20 +from runxpcshelltests import XPCShellTests
    1.21 +
    1.22 +mozinfo.find_and_update_from_json()
    1.23 +
    1.24 +objdir = build_obj.topobjdir.encode("utf-8")
    1.25 +xpcshellBin = os.path.join(objdir, "dist", "bin", "xpcshell")
    1.26 +if sys.platform == "win32":
    1.27 +    xpcshellBin += ".exe"
    1.28 +
    1.29 +SIMPLE_PASSING_TEST = "function run_test() { do_check_true(true); }"
    1.30 +SIMPLE_FAILING_TEST = "function run_test() { do_check_true(false); }"
    1.31 +
    1.32 +ADD_TEST_SIMPLE = '''
    1.33 +function run_test() { run_next_test(); }
    1.34 +
    1.35 +add_test(function test_simple() {
    1.36 +  do_check_true(true);
    1.37 +  run_next_test();
    1.38 +});
    1.39 +'''
    1.40 +
    1.41 +ADD_TEST_FAILING = '''
    1.42 +function run_test() { run_next_test(); }
    1.43 +
    1.44 +add_test(function test_failing() {
    1.45 +  do_check_true(false);
    1.46 +  run_next_test();
    1.47 +});
    1.48 +'''
    1.49 +
    1.50 +CHILD_TEST_PASSING = '''
    1.51 +function run_test () { run_next_test(); }
    1.52 +
    1.53 +add_test(function test_child_simple () {
    1.54 +  run_test_in_child("test_pass.js");
    1.55 +  run_next_test();
    1.56 +});
    1.57 +'''
    1.58 +
    1.59 +CHILD_TEST_FAILING = '''
    1.60 +function run_test () { run_next_test(); }
    1.61 +
    1.62 +add_test(function test_child_simple () {
    1.63 +  run_test_in_child("test_fail.js");
    1.64 +  run_next_test();
    1.65 +});
    1.66 +'''
    1.67 +
    1.68 +CHILD_TEST_HANG = '''
    1.69 +function run_test () { run_next_test(); }
    1.70 +
    1.71 +add_test(function test_child_simple () {
    1.72 +  do_test_pending("hang test");
    1.73 +  do_load_child_test_harness();
    1.74 +  sendCommand("_log('child_test_start', {_message: 'CHILD-TEST-STARTED'}); " +
    1.75 +              + "const _TEST_FILE=['test_pass.js']; _execute_test(); ",
    1.76 +              do_test_finished);
    1.77 +  run_next_test();
    1.78 +});
    1.79 +'''
    1.80 +
    1.81 +ADD_TASK_SINGLE = '''
    1.82 +Components.utils.import("resource://gre/modules/Promise.jsm");
    1.83 +
    1.84 +function run_test() { run_next_test(); }
    1.85 +
    1.86 +add_task(function test_task() {
    1.87 +  yield Promise.resolve(true);
    1.88 +  yield Promise.resolve(false);
    1.89 +});
    1.90 +'''
    1.91 +
    1.92 +ADD_TASK_MULTIPLE = '''
    1.93 +Components.utils.import("resource://gre/modules/Promise.jsm");
    1.94 +
    1.95 +function run_test() { run_next_test(); }
    1.96 +
    1.97 +add_task(function test_task() {
    1.98 +  yield Promise.resolve(true);
    1.99 +});
   1.100 +
   1.101 +add_task(function test_2() {
   1.102 +  yield Promise.resolve(true);
   1.103 +});
   1.104 +'''
   1.105 +
   1.106 +ADD_TASK_REJECTED = '''
   1.107 +Components.utils.import("resource://gre/modules/Promise.jsm");
   1.108 +
   1.109 +function run_test() { run_next_test(); }
   1.110 +
   1.111 +add_task(function test_failing() {
   1.112 +  yield Promise.reject(new Error("I fail."));
   1.113 +});
   1.114 +'''
   1.115 +
   1.116 +ADD_TASK_FAILURE_INSIDE = '''
   1.117 +Components.utils.import("resource://gre/modules/Promise.jsm");
   1.118 +
   1.119 +function run_test() { run_next_test(); }
   1.120 +
   1.121 +add_task(function test() {
   1.122 +  let result = yield Promise.resolve(false);
   1.123 +
   1.124 +  do_check_true(result);
   1.125 +});
   1.126 +'''
   1.127 +
   1.128 +ADD_TASK_RUN_NEXT_TEST = '''
   1.129 +function run_test() { run_next_test(); }
   1.130 +
   1.131 +add_task(function () {
   1.132 +  Assert.ok(true);
   1.133 +
   1.134 +  run_next_test();
   1.135 +});
   1.136 +'''
   1.137 +
   1.138 +ADD_TEST_THROW_STRING = '''
   1.139 +function run_test() {do_throw("Passing a string to do_throw")};
   1.140 +'''
   1.141 +
   1.142 +ADD_TEST_THROW_OBJECT = '''
   1.143 +let error = {
   1.144 +  message: "Error object",
   1.145 +  fileName: "failure.js",
   1.146 +  stack: "ERROR STACK",
   1.147 +  toString: function() {return this.message;}
   1.148 +};
   1.149 +function run_test() {do_throw(error)};
   1.150 +'''
   1.151 +
   1.152 +ADD_TEST_REPORT_OBJECT = '''
   1.153 +let error = {
   1.154 +  message: "Error object",
   1.155 +  fileName: "failure.js",
   1.156 +  stack: "ERROR STACK",
   1.157 +  toString: function() {return this.message;}
   1.158 +};
   1.159 +function run_test() {do_report_unexpected_exception(error)};
   1.160 +'''
   1.161 +
   1.162 +# A test for genuine JS-generated Error objects
   1.163 +ADD_TEST_REPORT_REF_ERROR = '''
   1.164 +function run_test() {
   1.165 +  let obj = {blah: 0};
   1.166 +  try {
   1.167 +    obj.noSuchFunction();
   1.168 +  }
   1.169 +  catch (error) {
   1.170 +    do_report_unexpected_exception(error);
   1.171 +  }
   1.172 +};
   1.173 +'''
   1.174 +
   1.175 +# A test for failure to load a test due to a syntax error
   1.176 +LOAD_ERROR_SYNTAX_ERROR = '''
   1.177 +function run_test(
   1.178 +'''
   1.179 +
   1.180 +# A test for failure to load a test due to an error other than a syntax error
   1.181 +LOAD_ERROR_OTHER_ERROR = '''
   1.182 +function run_test() {
   1.183 +    yield "foo";
   1.184 +    return "foo"; // can't use return in a generator!
   1.185 +};
   1.186 +'''
   1.187 +
   1.188 +# A test for asynchronous cleanup functions
   1.189 +ASYNC_CLEANUP = '''
   1.190 +function run_test() {
   1.191 +  Components.utils.import("resource://gre/modules/Promise.jsm", this);
   1.192 +
   1.193 +  // The list of checkpoints in the order we encounter them.
   1.194 +  let checkpoints = [];
   1.195 +
   1.196 +  // Cleanup tasks, in reverse order
   1.197 +  do_register_cleanup(function cleanup_checkout() {
   1.198 +    do_check_eq(checkpoints.join(""), "1234");
   1.199 +    do_print("At this stage, the test has succeeded");
   1.200 +    do_throw("Throwing an error to force displaying the log");
   1.201 +  });
   1.202 +
   1.203 +  do_register_cleanup(function sync_cleanup_2() {
   1.204 +    checkpoints.push(4);
   1.205 +  });
   1.206 +
   1.207 +  do_register_cleanup(function async_cleanup_2() {
   1.208 +    let deferred = Promise.defer();
   1.209 +    do_execute_soon(deferred.resolve);
   1.210 +    return deferred.promise.then(function() {
   1.211 +      checkpoints.push(3);
   1.212 +    });
   1.213 +  });
   1.214 +
   1.215 +  do_register_cleanup(function sync_cleanup() {
   1.216 +    checkpoints.push(2);
   1.217 +  });
   1.218 +
   1.219 +  do_register_cleanup(function async_cleanup() {
   1.220 +    let deferred = Promise.defer();
   1.221 +    do_execute_soon(deferred.resolve);
   1.222 +    return deferred.promise.then(function() {
   1.223 +      checkpoints.push(1);
   1.224 +    });
   1.225 +  });
   1.226 +
   1.227 +}
   1.228 +'''
   1.229 +
   1.230 +
   1.231 +class XPCShellTestsTests(unittest.TestCase):
   1.232 +    """
   1.233 +    Yes, these are unit tests for a unit test harness.
   1.234 +    """
   1.235 +    def setUp(self):
   1.236 +        self.log = StringIO()
   1.237 +        self.tempdir = tempfile.mkdtemp()
   1.238 +        self.x = XPCShellTests(log=self.log)
   1.239 +
   1.240 +    def tearDown(self):
   1.241 +        shutil.rmtree(self.tempdir)
   1.242 +
   1.243 +    def writeFile(self, name, contents):
   1.244 +        """
   1.245 +        Write |contents| to a file named |name| in the temp directory,
   1.246 +        and return the full path to the file.
   1.247 +        """
   1.248 +        fullpath = os.path.join(self.tempdir, name)
   1.249 +        with open(fullpath, "w") as f:
   1.250 +            f.write(contents)
   1.251 +        return fullpath
   1.252 +
   1.253 +    def writeManifest(self, tests):
   1.254 +        """
   1.255 +        Write an xpcshell.ini in the temp directory and set
   1.256 +        self.manifest to its pathname. |tests| is a list containing
   1.257 +        either strings (for test names), or tuples with a test name
   1.258 +        as the first element and manifest conditions as the following
   1.259 +        elements.
   1.260 +        """
   1.261 +        testlines = []
   1.262 +        for t in tests:
   1.263 +            testlines.append("[%s]" % (t if isinstance(t, basestring)
   1.264 +                                       else t[0]))
   1.265 +            if isinstance(t, tuple):
   1.266 +                testlines.extend(t[1:])
   1.267 +        self.manifest = self.writeFile("xpcshell.ini", """
   1.268 +[DEFAULT]
   1.269 +head =
   1.270 +tail =
   1.271 +
   1.272 +""" + "\n".join(testlines))
   1.273 +
   1.274 +    def assertTestResult(self, expected, shuffle=False, xunitFilename=None, verbose=False):
   1.275 +        """
   1.276 +        Assert that self.x.runTests with manifest=self.manifest
   1.277 +        returns |expected|.
   1.278 +        """
   1.279 +        self.assertEquals(expected,
   1.280 +                          self.x.runTests(xpcshellBin,
   1.281 +                                          manifest=self.manifest,
   1.282 +                                          mozInfo=mozinfo.info,
   1.283 +                                          shuffle=shuffle,
   1.284 +                                          testsRootDir=self.tempdir,
   1.285 +                                          verbose=verbose,
   1.286 +                                          xunitFilename=xunitFilename,
   1.287 +                                          sequential=True),
   1.288 +                          msg="""Tests should have %s, log:
   1.289 +========
   1.290 +%s
   1.291 +========
   1.292 +""" % ("passed" if expected else "failed", self.log.getvalue()))
   1.293 +
   1.294 +    def _assertLog(self, s, expected):
   1.295 +        l = self.log.getvalue()
   1.296 +        self.assertEqual(expected, s in l,
   1.297 +                         msg="""Value %s %s in log:
   1.298 +========
   1.299 +%s
   1.300 +========""" % (s, "expected" if expected else "not expected", l))
   1.301 +
   1.302 +    def assertInLog(self, s):
   1.303 +        """
   1.304 +        Assert that the string |s| is contained in self.log.
   1.305 +        """
   1.306 +        self._assertLog(s, True)
   1.307 +
   1.308 +    def assertNotInLog(self, s):
   1.309 +        """
   1.310 +        Assert that the string |s| is not contained in self.log.
   1.311 +        """
   1.312 +        self._assertLog(s, False)
   1.313 +
   1.314 +    def testPass(self):
   1.315 +        """
   1.316 +        Check that a simple test without any manifest conditions passes.
   1.317 +        """
   1.318 +        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
   1.319 +        self.writeManifest(["test_basic.js"])
   1.320 +
   1.321 +        self.assertTestResult(True)
   1.322 +        self.assertEquals(1, self.x.testCount)
   1.323 +        self.assertEquals(1, self.x.passCount)
   1.324 +        self.assertEquals(0, self.x.failCount)
   1.325 +        self.assertEquals(0, self.x.todoCount)
   1.326 +        self.assertInLog("TEST-PASS")
   1.327 +        self.assertNotInLog("TEST-UNEXPECTED-FAIL")
   1.328 +
   1.329 +    def testFail(self):
   1.330 +        """
   1.331 +        Check that a simple failing test without any manifest conditions fails.
   1.332 +        """
   1.333 +        self.writeFile("test_basic.js", SIMPLE_FAILING_TEST)
   1.334 +        self.writeManifest(["test_basic.js"])
   1.335 +
   1.336 +        self.assertTestResult(False)
   1.337 +        self.assertEquals(1, self.x.testCount)
   1.338 +        self.assertEquals(0, self.x.passCount)
   1.339 +        self.assertEquals(1, self.x.failCount)
   1.340 +        self.assertEquals(0, self.x.todoCount)
   1.341 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.342 +        self.assertNotInLog("TEST-PASS")
   1.343 +
   1.344 +    @unittest.skipIf(build_obj.defines.get('MOZ_B2G'),
   1.345 +                     'selftests with child processes fail on b2g desktop builds')
   1.346 +    def testChildPass(self):
   1.347 +        """
   1.348 +        Check that a simple test running in a child process passes.
   1.349 +        """
   1.350 +        self.writeFile("test_pass.js", SIMPLE_PASSING_TEST)
   1.351 +        self.writeFile("test_child_pass.js", CHILD_TEST_PASSING)
   1.352 +        self.writeManifest(["test_child_pass.js"])
   1.353 +
   1.354 +        self.assertTestResult(True, verbose=True)
   1.355 +        self.assertEquals(1, self.x.testCount)
   1.356 +        self.assertEquals(1, self.x.passCount)
   1.357 +        self.assertEquals(0, self.x.failCount)
   1.358 +        self.assertEquals(0, self.x.todoCount)
   1.359 +        self.assertInLog("TEST-PASS")
   1.360 +        self.assertInLog("CHILD-TEST-STARTED")
   1.361 +        self.assertInLog("CHILD-TEST-COMPLETED")
   1.362 +        self.assertNotInLog("TEST-UNEXPECTED-FAIL")
   1.363 +
   1.364 +
   1.365 +    @unittest.skipIf(build_obj.defines.get('MOZ_B2G'),
   1.366 +                     'selftests with child processes fail on b2g desktop builds')
   1.367 +    def testChildFail(self):
   1.368 +        """
   1.369 +        Check that a simple failing test running in a child process fails.
   1.370 +        """
   1.371 +        self.writeFile("test_fail.js", SIMPLE_FAILING_TEST)
   1.372 +        self.writeFile("test_child_fail.js", CHILD_TEST_FAILING)
   1.373 +        self.writeManifest(["test_child_fail.js"])
   1.374 +
   1.375 +        self.assertTestResult(False)
   1.376 +        self.assertEquals(1, self.x.testCount)
   1.377 +        self.assertEquals(0, self.x.passCount)
   1.378 +        self.assertEquals(1, self.x.failCount)
   1.379 +        self.assertEquals(0, self.x.todoCount)
   1.380 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.381 +        self.assertInLog("CHILD-TEST-STARTED")
   1.382 +        self.assertInLog("CHILD-TEST-COMPLETED")
   1.383 +        self.assertNotInLog("TEST-PASS")
   1.384 +
   1.385 +    @unittest.skipIf(build_obj.defines.get('MOZ_B2G'),
   1.386 +                     'selftests with child processes fail on b2g desktop builds')
   1.387 +    def testChildHang(self):
   1.388 +        """
   1.389 +        Check that incomplete output from a child process results in a
   1.390 +        test failure.
   1.391 +        """
   1.392 +        self.writeFile("test_pass.js", SIMPLE_PASSING_TEST)
   1.393 +        self.writeFile("test_child_hang.js", CHILD_TEST_HANG)
   1.394 +        self.writeManifest(["test_child_hang.js"])
   1.395 +
   1.396 +        self.assertTestResult(False)
   1.397 +        self.assertEquals(1, self.x.testCount)
   1.398 +        self.assertEquals(0, self.x.passCount)
   1.399 +        self.assertEquals(1, self.x.failCount)
   1.400 +        self.assertEquals(0, self.x.todoCount)
   1.401 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.402 +        self.assertInLog("CHILD-TEST-STARTED")
   1.403 +        self.assertNotInLog("CHILD-TEST-COMPLETED")
   1.404 +        self.assertNotInLog("TEST-PASS")
   1.405 +
   1.406 +    def testSyntaxError(self):
   1.407 +        """
   1.408 +        Check that running a test file containing a syntax error produces
   1.409 +        a test failure and expected output.
   1.410 +        """
   1.411 +        self.writeFile("test_syntax_error.js", '"')
   1.412 +        self.writeManifest(["test_syntax_error.js"])
   1.413 +
   1.414 +        self.assertTestResult(False, verbose=True)
   1.415 +        self.assertEquals(1, self.x.testCount)
   1.416 +        self.assertEquals(0, self.x.passCount)
   1.417 +        self.assertEquals(1, self.x.failCount)
   1.418 +        self.assertEquals(0, self.x.todoCount)
   1.419 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.420 +        self.assertNotInLog("TEST-PASS")
   1.421 +
   1.422 +    def testPassFail(self):
   1.423 +        """
   1.424 +        Check that running more than one test works.
   1.425 +        """
   1.426 +        self.writeFile("test_pass.js", SIMPLE_PASSING_TEST)
   1.427 +        self.writeFile("test_fail.js", SIMPLE_FAILING_TEST)
   1.428 +        self.writeManifest(["test_pass.js", "test_fail.js"])
   1.429 +
   1.430 +        self.assertTestResult(False)
   1.431 +        self.assertEquals(2, self.x.testCount)
   1.432 +        self.assertEquals(1, self.x.passCount)
   1.433 +        self.assertEquals(1, self.x.failCount)
   1.434 +        self.assertEquals(0, self.x.todoCount)
   1.435 +        self.assertInLog("TEST-PASS")
   1.436 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.437 +
   1.438 +    def testSkip(self):
   1.439 +        """
   1.440 +        Check that a simple failing test skipped in the manifest does
   1.441 +        not cause failure.
   1.442 +        """
   1.443 +        self.writeFile("test_basic.js", SIMPLE_FAILING_TEST)
   1.444 +        self.writeManifest([("test_basic.js", "skip-if = true")])
   1.445 +        self.assertTestResult(True)
   1.446 +        self.assertEquals(1, self.x.testCount)
   1.447 +        self.assertEquals(0, self.x.passCount)
   1.448 +        self.assertEquals(0, self.x.failCount)
   1.449 +        self.assertEquals(0, self.x.todoCount)
   1.450 +        self.assertNotInLog("TEST-UNEXPECTED-FAIL")
   1.451 +        self.assertNotInLog("TEST-PASS")
   1.452 +
   1.453 +    def testKnownFail(self):
   1.454 +        """
   1.455 +        Check that a simple failing test marked as known-fail in the manifest
   1.456 +        does not cause failure.
   1.457 +        """
   1.458 +        self.writeFile("test_basic.js", SIMPLE_FAILING_TEST)
   1.459 +        self.writeManifest([("test_basic.js", "fail-if = true")])
   1.460 +        self.assertTestResult(True)
   1.461 +        self.assertEquals(1, self.x.testCount)
   1.462 +        self.assertEquals(0, self.x.passCount)
   1.463 +        self.assertEquals(0, self.x.failCount)
   1.464 +        self.assertEquals(1, self.x.todoCount)
   1.465 +        self.assertInLog("TEST-KNOWN-FAIL")
   1.466 +        # This should be suppressed because the harness doesn't include
   1.467 +        # the full log from the xpcshell run when things pass.
   1.468 +        self.assertNotInLog("TEST-UNEXPECTED-FAIL")
   1.469 +        self.assertNotInLog("TEST-PASS")
   1.470 +
   1.471 +    def testUnexpectedPass(self):
   1.472 +        """
   1.473 +        Check that a simple failing test marked as known-fail in the manifest
   1.474 +        that passes causes an unexpected pass.
   1.475 +        """
   1.476 +        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
   1.477 +        self.writeManifest([("test_basic.js", "fail-if = true")])
   1.478 +        self.assertTestResult(False)
   1.479 +        self.assertEquals(1, self.x.testCount)
   1.480 +        self.assertEquals(0, self.x.passCount)
   1.481 +        self.assertEquals(1, self.x.failCount)
   1.482 +        self.assertEquals(0, self.x.todoCount)
   1.483 +        # From the outer (Python) harness
   1.484 +        self.assertInLog("TEST-UNEXPECTED-PASS")
   1.485 +        self.assertNotInLog("TEST-KNOWN-FAIL")
   1.486 +        # From the inner (JS) harness
   1.487 +        self.assertInLog("TEST-PASS")
   1.488 +
   1.489 +    def testReturnNonzero(self):
   1.490 +        """
   1.491 +        Check that a test where xpcshell returns nonzero fails.
   1.492 +        """
   1.493 +        self.writeFile("test_error.js", "throw 'foo'")
   1.494 +        self.writeManifest(["test_error.js"])
   1.495 +
   1.496 +        self.assertTestResult(False)
   1.497 +        self.assertEquals(1, self.x.testCount)
   1.498 +        self.assertEquals(0, self.x.passCount)
   1.499 +        self.assertEquals(1, self.x.failCount)
   1.500 +        self.assertEquals(0, self.x.todoCount)
   1.501 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.502 +        self.assertNotInLog("TEST-PASS")
   1.503 +
   1.504 +    def testAddTestSimple(self):
   1.505 +        """
   1.506 +        Ensure simple add_test() works.
   1.507 +        """
   1.508 +        self.writeFile("test_add_test_simple.js", ADD_TEST_SIMPLE)
   1.509 +        self.writeManifest(["test_add_test_simple.js"])
   1.510 +
   1.511 +        self.assertTestResult(True)
   1.512 +        self.assertEquals(1, self.x.testCount)
   1.513 +        self.assertEquals(1, self.x.passCount)
   1.514 +        self.assertEquals(0, self.x.failCount)
   1.515 +
   1.516 +    def testAddTestFailing(self):
   1.517 +        """
   1.518 +        Ensure add_test() with a failing test is reported.
   1.519 +        """
   1.520 +        self.writeFile("test_add_test_failing.js", ADD_TEST_FAILING)
   1.521 +        self.writeManifest(["test_add_test_failing.js"])
   1.522 +
   1.523 +        self.assertTestResult(False)
   1.524 +        self.assertEquals(1, self.x.testCount)
   1.525 +        self.assertEquals(0, self.x.passCount)
   1.526 +        self.assertEquals(1, self.x.failCount)
   1.527 +
   1.528 +    def testAddTaskTestSingle(self):
   1.529 +        """
   1.530 +        Ensure add_test_task() with a single passing test works.
   1.531 +        """
   1.532 +        self.writeFile("test_add_task_simple.js", ADD_TASK_SINGLE)
   1.533 +        self.writeManifest(["test_add_task_simple.js"])
   1.534 +
   1.535 +        self.assertTestResult(True)
   1.536 +        self.assertEquals(1, self.x.testCount)
   1.537 +        self.assertEquals(1, self.x.passCount)
   1.538 +        self.assertEquals(0, self.x.failCount)
   1.539 +
   1.540 +    def testAddTaskTestMultiple(self):
   1.541 +        """
   1.542 +        Ensure multiple calls to add_test_task() work as expected.
   1.543 +        """
   1.544 +        self.writeFile("test_add_task_multiple.js",
   1.545 +            ADD_TASK_MULTIPLE)
   1.546 +        self.writeManifest(["test_add_task_multiple.js"])
   1.547 +
   1.548 +        self.assertTestResult(True)
   1.549 +        self.assertEquals(1, self.x.testCount)
   1.550 +        self.assertEquals(1, self.x.passCount)
   1.551 +        self.assertEquals(0, self.x.failCount)
   1.552 +
   1.553 +    def testAddTaskTestRejected(self):
   1.554 +        """
   1.555 +        Ensure rejected task reports as failure.
   1.556 +        """
   1.557 +        self.writeFile("test_add_task_rejected.js",
   1.558 +            ADD_TASK_REJECTED)
   1.559 +        self.writeManifest(["test_add_task_rejected.js"])
   1.560 +
   1.561 +        self.assertTestResult(False)
   1.562 +        self.assertEquals(1, self.x.testCount)
   1.563 +        self.assertEquals(0, self.x.passCount)
   1.564 +        self.assertEquals(1, self.x.failCount)
   1.565 +
   1.566 +    def testAddTaskTestFailureInside(self):
   1.567 +        """
   1.568 +        Ensure tests inside task are reported as failures.
   1.569 +        """
   1.570 +        self.writeFile("test_add_task_failure_inside.js",
   1.571 +            ADD_TASK_FAILURE_INSIDE)
   1.572 +        self.writeManifest(["test_add_task_failure_inside.js"])
   1.573 +
   1.574 +        self.assertTestResult(False)
   1.575 +        self.assertEquals(1, self.x.testCount)
   1.576 +        self.assertEquals(0, self.x.passCount)
   1.577 +        self.assertEquals(1, self.x.failCount)
   1.578 +
   1.579 +    def testAddTaskRunNextTest(self):
   1.580 +        """
   1.581 +        Calling run_next_test() from inside add_task() results in failure.
   1.582 +        """
   1.583 +        self.writeFile("test_add_task_run_next_test.js",
   1.584 +            ADD_TASK_RUN_NEXT_TEST)
   1.585 +        self.writeManifest(["test_add_task_run_next_test.js"])
   1.586 +
   1.587 +        self.assertTestResult(False)
   1.588 +        self.assertEquals(1, self.x.testCount)
   1.589 +        self.assertEquals(0, self.x.passCount)
   1.590 +        self.assertEquals(1, self.x.failCount)
   1.591 +
   1.592 +    def testMissingHeadFile(self):
   1.593 +        """
   1.594 +        Ensure that missing head file results in fatal error.
   1.595 +        """
   1.596 +        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
   1.597 +        self.writeManifest([("test_basic.js", "head = missing.js")])
   1.598 +
   1.599 +        raised = False
   1.600 +
   1.601 +        try:
   1.602 +            # The actual return value is never checked because we raise.
   1.603 +            self.assertTestResult(True)
   1.604 +        except Exception, ex:
   1.605 +            raised = True
   1.606 +            self.assertEquals(ex.message[0:9], "head file")
   1.607 +
   1.608 +        self.assertTrue(raised)
   1.609 +
   1.610 +    def testMissingTailFile(self):
   1.611 +        """
   1.612 +        Ensure that missing tail file results in fatal error.
   1.613 +        """
   1.614 +        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
   1.615 +        self.writeManifest([("test_basic.js", "tail = missing.js")])
   1.616 +
   1.617 +        raised = False
   1.618 +
   1.619 +        try:
   1.620 +            self.assertTestResult(True)
   1.621 +        except Exception, ex:
   1.622 +            raised = True
   1.623 +            self.assertEquals(ex.message[0:9], "tail file")
   1.624 +
   1.625 +        self.assertTrue(raised)
   1.626 +
   1.627 +    def testRandomExecution(self):
   1.628 +        """
   1.629 +        Check that random execution doesn't break.
   1.630 +        """
   1.631 +        manifest = []
   1.632 +        for i in range(0, 10):
   1.633 +            filename = "test_pass_%d.js" % i
   1.634 +            self.writeFile(filename, SIMPLE_PASSING_TEST)
   1.635 +            manifest.append(filename)
   1.636 +
   1.637 +        self.writeManifest(manifest)
   1.638 +        self.assertTestResult(True, shuffle=True)
   1.639 +        self.assertEquals(10, self.x.testCount)
   1.640 +        self.assertEquals(10, self.x.passCount)
   1.641 +
   1.642 +    def testXunitOutput(self):
   1.643 +        """
   1.644 +        Check that Xunit XML files are written.
   1.645 +        """
   1.646 +        self.writeFile("test_00.js", SIMPLE_PASSING_TEST)
   1.647 +        self.writeFile("test_01.js", SIMPLE_FAILING_TEST)
   1.648 +        self.writeFile("test_02.js", SIMPLE_PASSING_TEST)
   1.649 +
   1.650 +        manifest = [
   1.651 +            "test_00.js",
   1.652 +            "test_01.js",
   1.653 +            ("test_02.js", "skip-if = true")
   1.654 +        ]
   1.655 +
   1.656 +        self.writeManifest(manifest)
   1.657 +
   1.658 +        filename = os.path.join(self.tempdir, "xunit.xml")
   1.659 +
   1.660 +        self.assertTestResult(False, xunitFilename=filename)
   1.661 +
   1.662 +        self.assertTrue(os.path.exists(filename))
   1.663 +        self.assertTrue(os.path.getsize(filename) > 0)
   1.664 +
   1.665 +        tree = ElementTree()
   1.666 +        tree.parse(filename)
   1.667 +        suite = tree.getroot()
   1.668 +
   1.669 +        self.assertTrue(suite is not None)
   1.670 +        self.assertEqual(suite.get("tests"), "3")
   1.671 +        self.assertEqual(suite.get("failures"), "1")
   1.672 +        self.assertEqual(suite.get("skip"), "1")
   1.673 +
   1.674 +        testcases = suite.findall("testcase")
   1.675 +        self.assertEqual(len(testcases), 3)
   1.676 +
   1.677 +        for testcase in testcases:
   1.678 +            attributes = testcase.keys()
   1.679 +            self.assertTrue("classname" in attributes)
   1.680 +            self.assertTrue("name" in attributes)
   1.681 +            self.assertTrue("time" in attributes)
   1.682 +
   1.683 +        self.assertTrue(testcases[1].find("failure") is not None)
   1.684 +        self.assertTrue(testcases[2].find("skipped") is not None)
   1.685 +
   1.686 +    def testDoThrowString(self):
   1.687 +        """
   1.688 +        Check that do_throw produces reasonable messages when the
   1.689 +        input is a string instead of an object
   1.690 +        """
   1.691 +        self.writeFile("test_error.js", ADD_TEST_THROW_STRING)
   1.692 +        self.writeManifest(["test_error.js"])
   1.693 +
   1.694 +        self.assertTestResult(False)
   1.695 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.696 +        self.assertInLog("Passing a string to do_throw")
   1.697 +        self.assertNotInLog("TEST-PASS")
   1.698 +
   1.699 +    def testDoThrowForeignObject(self):
   1.700 +        """
   1.701 +        Check that do_throw produces reasonable messages when the
   1.702 +        input is a generic object with 'filename', 'message' and 'stack' attributes
   1.703 +        but 'object instanceof Error' returns false
   1.704 +        """
   1.705 +        self.writeFile("test_error.js", ADD_TEST_THROW_OBJECT)
   1.706 +        self.writeManifest(["test_error.js"])
   1.707 +
   1.708 +        self.assertTestResult(False)
   1.709 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.710 +        self.assertInLog("failure.js")
   1.711 +        self.assertInLog("Error object")
   1.712 +        self.assertInLog("ERROR STACK")
   1.713 +        self.assertNotInLog("TEST-PASS")
   1.714 +
   1.715 +    def testDoReportForeignObject(self):
   1.716 +        """
   1.717 +        Check that do_report_unexpected_exception produces reasonable messages when the
   1.718 +        input is a generic object with 'filename', 'message' and 'stack' attributes
   1.719 +        but 'object instanceof Error' returns false
   1.720 +        """
   1.721 +        self.writeFile("test_error.js", ADD_TEST_REPORT_OBJECT)
   1.722 +        self.writeManifest(["test_error.js"])
   1.723 +
   1.724 +        self.assertTestResult(False)
   1.725 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.726 +        self.assertInLog("failure.js")
   1.727 +        self.assertInLog("Error object")
   1.728 +        self.assertInLog("ERROR STACK")
   1.729 +        self.assertNotInLog("TEST-PASS")
   1.730 +
   1.731 +    def testDoReportRefError(self):
   1.732 +        """
   1.733 +        Check that do_report_unexpected_exception produces reasonable messages when the
   1.734 +        input is a JS-generated Error
   1.735 +        """
   1.736 +        self.writeFile("test_error.js", ADD_TEST_REPORT_REF_ERROR)
   1.737 +        self.writeManifest(["test_error.js"])
   1.738 +
   1.739 +        self.assertTestResult(False)
   1.740 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.741 +        self.assertInLog("test_error.js")
   1.742 +        self.assertInLog("obj.noSuchFunction is not a function")
   1.743 +        self.assertInLog("run_test@")
   1.744 +        self.assertNotInLog("TEST-PASS")
   1.745 +
   1.746 +    def testDoReportSyntaxError(self):
   1.747 +        """
   1.748 +        Check that attempting to load a test file containing a syntax error
   1.749 +        generates details of the error in the log
   1.750 +        """
   1.751 +        self.writeFile("test_error.js", LOAD_ERROR_SYNTAX_ERROR)
   1.752 +        self.writeManifest(["test_error.js"])
   1.753 +
   1.754 +        self.assertTestResult(False)
   1.755 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.756 +        self.assertInLog("test_error.js")
   1.757 +        self.assertInLog("test_error.js contains SyntaxError")
   1.758 +        self.assertInLog("Diagnostic: SyntaxError: missing formal parameter at")
   1.759 +        self.assertInLog("test_error.js:3")
   1.760 +        self.assertNotInLog("TEST-PASS")
   1.761 +
   1.762 +    def testDoReportNonSyntaxError(self):
   1.763 +        """
   1.764 +        Check that attempting to load a test file containing an error other
   1.765 +        than a syntax error generates details of the error in the log
   1.766 +        """
   1.767 +        self.writeFile("test_error.js", LOAD_ERROR_OTHER_ERROR)
   1.768 +        self.writeManifest(["test_error.js"])
   1.769 +
   1.770 +        self.assertTestResult(False)
   1.771 +        self.assertInLog("TEST-UNEXPECTED-FAIL")
   1.772 +        self.assertInLog("Diagnostic: TypeError: generator function run_test returns a value at")
   1.773 +        self.assertInLog("test_error.js:4")
   1.774 +        self.assertNotInLog("TEST-PASS")
   1.775 +
   1.776 +    def testAsyncCleanup(self):
   1.777 +        """
   1.778 +        Check that do_register_cleanup handles nicely cleanup tasks that
   1.779 +        return a promise
   1.780 +        """
   1.781 +        self.writeFile("test_asyncCleanup.js", ASYNC_CLEANUP)
   1.782 +        self.writeManifest(["test_asyncCleanup.js"])
   1.783 +        self.assertTestResult(False)
   1.784 +        self.assertInLog("\"1234\" == \"1234\"")
   1.785 +        self.assertInLog("At this stage, the test has succeeded")
   1.786 +        self.assertInLog("Throwing an error to force displaying the log")
   1.787 +
   1.788 +if __name__ == "__main__":
   1.789 +    unittest.main()

mercurial