Bug 938019 - Run mochitests from manifests (r=ted,gps)

This commit is contained in:
Bill McCloskey 2014-03-18 08:03:51 -07:00
parent 2dd8e3a0bb
commit 8bc8184542
15 changed files with 173 additions and 38 deletions

View File

@ -566,9 +566,12 @@ class TreeMetadataEmitter(LoggingMixin):
# If there are no tests, look for support-files under DEFAULT. # If there are no tests, look for support-files under DEFAULT.
process_support_files(defaults) process_support_files(defaults)
# We also copy the manifest into the output directory. # We also copy manifests into the output directory,
out_path = mozpath.join(out_dir, mozpath.basename(manifest_path)) # including manifests from [include:foo] directives.
obj.installs[path] = (out_path, False) for mpath in m.manifests():
mpath = mozpath.normpath(mpath)
out_path = mozpath.join(out_dir, mozpath.basename(mpath))
obj.installs[mpath] = (out_path, False)
# Some manifests reference files that are auto generated as # Some manifests reference files that are auto generated as
# part of the build or shouldn't be installed for some # part of the build or shouldn't be installed for some

View File

@ -0,0 +1,4 @@
[DEFAULT]
install-to-subdir = subdir
[include:common.ini]

View File

@ -0,0 +1,4 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
MOCHITEST_MANIFESTS += ['mochitest.ini']

View File

@ -366,6 +366,24 @@ class TestEmitterBasic(unittest.TestCase):
paths = sorted([v[0] for v in o.installs.values()]) paths = sorted([v[0] for v in o.installs.values()])
self.assertEqual(paths, expected) self.assertEqual(paths, expected)
def test_test_manifest_install_includes(self):
"""Ensure that any [include:foo.ini] are copied to the objdir."""
reader = self.reader('test-manifest-install-includes')
objs = self.read_topsrcdir(reader)
self.assertEqual(len(objs), 1)
o = objs[0]
self.assertEqual(len(o.installs), 3)
self.assertEqual(o.manifest_relpath, "mochitest.ini")
self.assertEqual(o.manifest_obj_relpath, "subdir/mochitest.ini")
expected = [
mozpath.normpath(mozpath.join(o.install_prefix, "subdir/common.ini")),
mozpath.normpath(mozpath.join(o.install_prefix, "subdir/mochitest.ini")),
mozpath.normpath(mozpath.join(o.install_prefix, "subdir/test_foo.html")),
]
paths = sorted([v[0] for v in o.installs.values()])
self.assertEqual(paths, expected)
def test_test_manifest_keys_extracted(self): def test_test_manifest_keys_extracted(self):
"""Ensure all metadata from test manifests is extracted.""" """Ensure all metadata from test manifests is extracted."""
reader = self.reader('test-manifest-keys-extracted') reader = self.reader('test-manifest-keys-extracted')

View File

@ -320,7 +320,6 @@ class MochitestRunner(MozbuildObject):
options.dumpAboutMemoryAfterTest = dump_about_memory_after_test options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
options.dumpDMDAfterTest = dump_dmd_after_test options.dumpDMDAfterTest = dump_dmd_after_test
options.dumpOutputDirectory = dump_output_directory options.dumpOutputDirectory = dump_output_directory
mozinfo.update({"e10s": e10s}) # for test manifest parsing.
options.failureFile = failure_file_path options.failureFile = failure_file_path
if install_extension != None: if install_extension != None:

View File

@ -19,7 +19,8 @@ function parseTestManifest(testManifest, params, callback) {
// For mochitest-plain, we define lists as an array of testnames. // For mochitest-plain, we define lists as an array of testnames.
for (var obj of testManifest['tests']) { for (var obj of testManifest['tests']) {
var path = obj['path']; var path = obj['path'];
if (obj.disabled) { // Note that obj.disabled may be "". We still want to skip in that case.
if ("disabled" in obj) {
dump("TEST-SKIPPED | " + path + " | " + obj.disabled + "\n"); dump("TEST-SKIPPED | " + path + " | " + obj.disabled + "\n");
continue; continue;
} }

View File

@ -404,6 +404,8 @@ class MochitestOptions(optparse.OptionParser):
def verifyOptions(self, options, mochitest): def verifyOptions(self, options, mochitest):
""" verify correct options and cleanup paths """ """ verify correct options and cleanup paths """
mozinfo.update({"e10s": options.e10s}) # for test manifest parsing.
if options.app is None: if options.app is None:
if build_obj is not None: if build_obj is not None:
options.app = build_obj.get_binary_path() options.app = build_obj.get_binary_path()

View File

@ -362,24 +362,113 @@ class MochitestUtilsMixin(object):
if options.dumpDMDAfterTest: if options.dumpDMDAfterTest:
self.urlOpts.append("dumpDMDAfterTest=true") self.urlOpts.append("dumpDMDAfterTest=true")
def getTestFlavor(self, options):
if options.browserChrome:
return "browser-chrome"
elif options.chrome:
return "chrome"
elif options.a11y:
return "a11y"
elif options.webapprtChrome:
return "webapprt-chrome"
else:
return "mochitest"
# This check can be removed when bug 983867 is fixed.
def isTest(self, options, filename):
allow_js_css = False
if options.browserChrome:
allow_js_css = True
testPattern = re.compile(r"browser_.+\.js")
elif options.chrome or options.a11y:
testPattern = re.compile(r"(browser|test)_.+\.(xul|html|js|xhtml)")
elif options.webapprtChrome:
testPattern = re.compile(r"webapprt_")
else:
testPattern = re.compile(r"test_")
if not allow_js_css and (".js" in filename or ".css" in filename):
return False
pathPieces = filename.split("/")
return (testPattern.match(pathPieces[-1]) and
not re.search(r'\^headers\^$', filename))
def getTestPath(self, options):
if options.ipcplugins:
return "dom/plugins/test"
else:
return options.testPath
def getTestRoot(self, options):
if options.browserChrome:
if options.immersiveMode:
return 'metro'
return 'browser'
elif options.a11y:
return 'a11y'
elif options.webapprtChrome:
return 'webapprtChrome'
elif options.chrome:
return 'chrome'
return self.TEST_PATH
def buildTestURL(self, options):
testHost = "http://mochi.test:8888"
testPath = self.getTestPath(options)
testURL = "/".join([testHost, self.TEST_PATH, testPath])
if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, testPath)) and options.repeat > 0:
testURL = "/".join([testHost, self.TEST_PATH, os.path.dirname(testPath)])
if options.chrome or options.a11y:
testURL = "/".join([testHost, self.CHROME_PATH])
elif options.browserChrome:
testURL = "about:blank"
return testURL
def buildTestPath(self, options): def buildTestPath(self, options):
""" Build the url path to the specific test harness and test file or directory """ Build the url path to the specific test harness and test file or directory
Build a manifest of tests to run and write out a json file for the harness to read Build a manifest of tests to run and write out a json file for the harness to read
""" """
manifest = None
testRoot = self.getTestRoot(options)
testRootAbs = os.path.abspath(testRoot)
if options.manifestFile and os.path.isfile(options.manifestFile): if options.manifestFile and os.path.isfile(options.manifestFile):
manifest = TestManifest(strict=False) manifestFileAbs = os.path.abspath(options.manifestFile)
manifest.read(options.manifestFile) assert manifestFileAbs.startswith(testRootAbs)
manifest = TestManifest([options.manifestFile], strict=False)
else:
masterName = self.getTestFlavor(options) + '.ini'
masterPath = os.path.join(testRoot, masterName)
if os.path.exists(masterPath):
manifest = TestManifest([masterPath], strict=False)
if manifest:
# Python 2.6 doesn't allow unicode keys to be used for keyword
# arguments. This gross hack works around the problem until we
# rid ourselves of 2.6.
info = {}
for k, v in mozinfo.info.items():
if isinstance(k, unicode):
k = k.encode('ascii')
info[k] = v
# Bug 883858 - return all tests including disabled tests # Bug 883858 - return all tests including disabled tests
tests = manifest.active_tests(disabled=True, **mozinfo.info) tests = manifest.active_tests(disabled=True, **info)
# We need to ensure we match on a complete directory name matching the
# test root, and not a substring somewhere else in the path.
test_root = os.path.sep + self.getTestRoot(options) + os.path.sep
paths = [] paths = []
testPath = self.getTestPath(options)
for test in tests: for test in tests:
tp = test['path'].split(test_root, 1)[1].replace('\\', '/').strip('/') pathAbs = os.path.abspath(test['path'])
assert pathAbs.startswith(testRootAbs)
tp = pathAbs[len(testRootAbs):].replace('\\', '/').strip('/')
# Filter out tests if we are using --test-path # Filter out tests if we are using --test-path
if options.testPath and not tp.startswith(options.testPath): if testPath and not tp.startswith(testPath):
continue
if not self.isTest(options, tp):
print 'Warning: %s from manifest %s is not a valid test' % (test['name'], test['manifest'])
continue continue
testob = {'path': tp} testob = {'path': tp}
@ -387,22 +476,20 @@ class MochitestUtilsMixin(object):
testob['disabled'] = test['disabled'] testob['disabled'] = test['disabled']
paths.append(testob) paths.append(testob)
# Sort tests so they are run in a deterministic order.
def path_sort(ob1, ob2):
path1 = ob1['path'].split('/')
path2 = ob2['path'].split('/')
return cmp(path1, path2)
paths.sort(path_sort)
# Bug 883865 - add this functionality into manifestDestiny # Bug 883865 - add this functionality into manifestDestiny
with open('tests.json', 'w') as manifestFile: with open('tests.json', 'w') as manifestFile:
manifestFile.write(json.dumps({'tests': paths})) manifestFile.write(json.dumps({'tests': paths}))
options.manifestFile = 'tests.json' options.manifestFile = 'tests.json'
testHost = "http://mochi.test:8888" return self.buildTestURL(options)
testURL = ("/").join([testHost, self.TEST_PATH, options.testPath])
if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0:
testURL = ("/").join([testHost, self.TEST_PATH, os.path.dirname(options.testPath)])
if options.chrome or options.a11y:
testURL = ("/").join([testHost, self.CHROME_PATH])
elif options.browserChrome:
testURL = "about:blank"
elif options.ipcplugins:
testURL = ("/").join([testHost, self.TEST_PATH, "dom/plugins/test"])
return testURL
def startWebSocketServer(self, options, debuggerInfo): def startWebSocketServer(self, options, debuggerInfo):
""" Launch the websocket server """ """ Launch the websocket server """
@ -1298,19 +1385,6 @@ class Mochitest(MochitestUtilsMixin):
with open(os.path.join(options.profilePath, "testConfig.js"), "w") as config: with open(os.path.join(options.profilePath, "testConfig.js"), "w") as config:
config.write(content) config.write(content)
def getTestRoot(self, options):
if (options.browserChrome):
if (options.immersiveMode):
return 'metro'
return 'browser'
elif (options.a11y):
return 'a11y'
elif (options.webapprtChrome):
return 'webapprtChrome'
elif (options.chrome):
return 'chrome'
return self.TEST_PATH
def installExtensionFromPath(self, options, path, extensionID = None): def installExtensionFromPath(self, options, path, extensionID = None):
"""install an extension to options.profilePath""" """install an extension to options.profilePath"""

View File

@ -65,6 +65,10 @@ class B2GMochitest(MochitestUtilsMixin):
test_url += "?" + "&".join(self.urlOpts) test_url += "?" + "&".join(self.urlOpts)
self.test_script_args.append(test_url) self.test_script_args.append(test_url)
def buildTestPath(self, options):
# Skip over the manifest building that happens on desktop.
return self.buildTestURL(options)
def build_profile(self, options): def build_profile(self, options):
# preferences # preferences
prefs = {} prefs = {}

View File

@ -379,6 +379,14 @@ class MochiRemote(Mochitest):
options.logFile = self.localLog options.logFile = self.localLog
return retVal return retVal
def buildTestPath(self, options):
if options.robocopIni != "":
# Skip over manifest building if we just want to run
# robocop tests.
return self.buildTestURL(options)
else:
return super(MochiRemote, self).buildTestPath(options)
def installChromeFile(self, filename, options): def installChromeFile(self, filename, options):
parts = options.app.split('/') parts = options.app.split('/')
if (parts[0] == options.app): if (parts[0] == options.app):

View File

@ -603,7 +603,9 @@ class ManifestParser(object):
return manifests in order in which they appear in the tests return manifests in order in which they appear in the tests
""" """
if tests is None: if tests is None:
tests = self.tests # Make sure to return all the manifests, even ones without tests.
return self.manifest_defaults.keys()
manifests = [] manifests = []
for test in tests: for test in tests:
manifest = test.get('manifest') manifest = test.get('manifest')

View File

@ -0,0 +1,2 @@
[DEFAULT]
foo = bar

View File

@ -200,5 +200,17 @@ class TestManifestParser(unittest.TestCase):
self.assertTrue(manifest in parser.manifest_defaults) self.assertTrue(manifest in parser.manifest_defaults)
self.assertEquals(parser.manifest_defaults[manifest]['foo'], 'bar') self.assertEquals(parser.manifest_defaults[manifest]['foo'], 'bar')
def test_manifest_list(self):
"""
Ensure a manifest with just a DEFAULT section still returns
itself from the manifests() method.
"""
parser = ManifestParser()
manifest = os.path.join(here, 'no-tests.ini')
parser.read(manifest)
self.assertEqual(len(parser.tests), 0)
self.assertTrue(len(parser.manifests()) == 1)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()