From edaf7af30bddef699ea68726fedd7a28d1dc999a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 16 Oct 2014 18:43:13 +0200 Subject: [PATCH] test: make test runner multi-arch/mode compatible Make `python tools/test.py --arch=ia32,x64 --mode=debug,release` work. The test runner looks for the `node` binary in `out/${arch}.${mode}/`. Running tools/test.py without --arch makes it use `out/Release/node` or `out/Debug/node` like before. This commit removes `test/simple/test-executable-path.js` because the assumptions it makes about the locations of the debug and release binaries are now outdated. PR-URL: https://github.com/node-forward/node/pull/24 Reviewed-By: Fedor Indutny --- test/message/testcfg.py | 13 +++--- test/simple/test-executable-path.js | 61 ------------------------- test/testpy/__init__.py | 15 ++++--- test/timers/testcfg.py | 12 ++--- tools/test.py | 89 ++++++++++++++----------------------- 5 files changed, 56 insertions(+), 134 deletions(-) delete mode 100644 test/simple/test-executable-path.js diff --git a/test/message/testcfg.py b/test/message/testcfg.py index 105fac6..60e64e2 100644 --- a/test/message/testcfg.py +++ b/test/message/testcfg.py @@ -34,11 +34,12 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") class MessageTestCase(test.TestCase): - def __init__(self, path, file, expected, mode, context, config): - super(MessageTestCase, self).__init__(context, path, mode) + def __init__(self, path, file, expected, arch, mode, context, config): + super(MessageTestCase, self).__init__(context, path, arch, mode) self.file = file self.expected = expected self.config = config + self.arch = arch self.mode = mode def IgnoreLine(self, str): @@ -92,7 +93,7 @@ class MessageTestCase(test.TestCase): return self.path[-1] def GetCommand(self): - result = [self.config.context.GetVm(self.mode)] + result = [self.config.context.GetVm(self.arch, self.mode)] source = open(self.file).read() flags_match = FLAGS_PATTERN.search(source) if flags_match: @@ -117,7 +118,7 @@ class MessageTestConfiguration(test.TestConfiguration): else: return [] - def ListTests(self, current_path, path, mode): + def ListTests(self, current_path, path, arch, mode): all_tests = [current_path + [t] for t in self.Ls(self.root)] result = [] for test in all_tests: @@ -128,8 +129,8 @@ class MessageTestConfiguration(test.TestConfiguration): if not exists(output_path): print "Could not find %s" % output_path continue - result.append(MessageTestCase(test, file_path, output_path, mode, - self.context, self)) + result.append(MessageTestCase(test, file_path, output_path, + arch, mode, self.context, self)) return result def GetBuildRequirements(self): diff --git a/test/simple/test-executable-path.js b/test/simple/test-executable-path.js deleted file mode 100644 index 57205af..0000000 --- a/test/simple/test-executable-path.js +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); -var path = require('path'); -var match = false; - -var isDebug = process.features.debug; -var isWindows = process.platform === 'win32'; - -var debugPaths = [path.normalize(path.join(__dirname, '..', '..', - 'out', 'Debug', 'node')), - path.normalize(path.join(__dirname, '..', '..', - 'Debug', 'node'))]; -var defaultPaths = [path.normalize(path.join(__dirname, '..', '..', - 'out', 'Release', 'node')), - path.normalize(path.join(__dirname, '..', '..', - 'Release', 'node'))]; - -console.error('debugPaths: ' + debugPaths); -console.error('defaultPaths: ' + defaultPaths); -console.error('process.execPath: ' + process.execPath); - -function pathStartsWith(a, b) { - if (isWindows) - return (a.toLowerCase().indexOf(b.toLowerCase()) == 0); - else - return (a.indexOf(b) == 0); -} - - -if (isDebug) { - debugPaths.forEach(function(path) { - match = match || pathStartsWith(process.execPath, path); - }); -} else { - defaultPaths.forEach(function(path) { - match = match || pathStartsWith(process.execPath, path); - }); -} - -assert.ok(match); diff --git a/test/testpy/__init__.py b/test/testpy/__init__.py index 9593a1f..cf64ba1 100644 --- a/test/testpy/__init__.py +++ b/test/testpy/__init__.py @@ -41,10 +41,11 @@ FILES_PATTERN = re.compile(r"//\s+Files:(.*)") class SimpleTestCase(test.TestCase): - def __init__(self, path, file, mode, context, config, additional=[]): - super(SimpleTestCase, self).__init__(context, path, mode) + def __init__(self, path, file, arch, mode, context, config, additional=[]): + super(SimpleTestCase, self).__init__(context, path, arch, mode) self.file = file self.config = config + self.arch = arch self.mode = mode self.tmpdir = join(dirname(self.config.root), 'tmp') self.additional_flags = additional @@ -82,7 +83,7 @@ class SimpleTestCase(test.TestCase): return self.path[-1] def GetCommand(self): - result = [self.config.context.GetVm(self.mode)] + result = [self.config.context.GetVm(self.arch, self.mode)] source = open(self.file).read() flags_match = FLAGS_PATTERN.search(source) if flags_match: @@ -117,14 +118,14 @@ class SimpleTestConfiguration(test.TestConfiguration): return name.startswith('test-') and name.endswith('.js') return [f[:-3] for f in os.listdir(path) if SelectTest(f)] - def ListTests(self, current_path, path, mode): + def ListTests(self, current_path, path, arch, mode): all_tests = [current_path + [t] for t in self.Ls(join(self.root))] result = [] for test in all_tests: if self.Contains(path, test): file_path = join(self.root, reduce(join, test[1:], "") + ".js") - result.append(SimpleTestCase(test, file_path, mode, self.context, self, - self.additional_flags)) + result.append(SimpleTestCase(test, file_path, arch, mode, self.context, + self, self.additional_flags)) return result def GetBuildRequirements(self): @@ -151,7 +152,7 @@ class AddonTestConfiguration(SimpleTestConfiguration): result.append([subpath, f[:-3]]) return result - def ListTests(self, current_path, path, mode): + def ListTests(self, current_path, path, arch, mode): all_tests = [current_path + t for t in self.Ls(join(self.root))] result = [] for test in all_tests: diff --git a/test/timers/testcfg.py b/test/timers/testcfg.py index 98653e4..a7c248f 100644 --- a/test/timers/testcfg.py +++ b/test/timers/testcfg.py @@ -40,10 +40,11 @@ FAKETIME_BIN_PATH = os.path.join("tools", "faketime", "src", "faketime") class TimersTestCase(test.TestCase): - def __init__(self, path, file, mode, context, config): - super(TimersTestCase, self).__init__(context, path, mode) + def __init__(self, path, file, arch, mode, context, config): + super(TimersTestCase, self).__init__(context, path, arch, mode) self.file = file self.config = config + self.arch = arch self.mode = mode def GetLabel(self): @@ -60,7 +61,7 @@ class TimersTestCase(test.TestCase): if faketime_flags_match: result += shlex.split(faketime_flags_match.group(1).strip()) - result += [self.config.context.GetVm(self.mode)] + result += [self.config.context.GetVm(self.arch, self.mode)] result += [self.file] return result @@ -79,13 +80,14 @@ class TimersTestConfiguration(test.TestConfiguration): return name.startswith('test-') and name.endswith('.js') return [f[:-3] for f in os.listdir(path) if SelectTest(f)] - def ListTests(self, current_path, path, mode): + def ListTests(self, current_path, path, arch, mode): all_tests = [current_path + [t] for t in self.Ls(join(self.root))] result = [] for test in all_tests: if self.Contains(path, test): file_path = join(self.root, reduce(join, test[1:], "") + ".js") - result.append(TimersTestCase(test, file_path, mode, self.context, self)) + result.append(TimersTestCase(test, file_path, arch, mode, + self.context, self)) return result def GetBuildRequirements(self): diff --git a/tools/test.py b/tools/test.py index 0772f9a..08cf4d1 100755 --- a/tools/test.py +++ b/tools/test.py @@ -368,10 +368,11 @@ class CommandOutput(object): class TestCase(object): - def __init__(self, context, path, mode): + def __init__(self, context, path, arch, mode): self.path = path self.context = context self.duration = None + self.arch = arch self.mode = mode def IsNegative(self): @@ -644,9 +645,10 @@ class TestRepository(TestSuite): def GetBuildRequirements(self, path, context): return self.GetConfiguration(context).GetBuildRequirements() - def AddTestsToList(self, result, current_path, path, context, mode): + def AddTestsToList(self, result, current_path, path, context, arch, mode): for v in VARIANT_FLAGS: - tests = self.GetConfiguration(context).ListTests(current_path, path, mode) + tests = self.GetConfiguration(context).ListTests(current_path, path, + arch, mode) for t in tests: t.variant_flags = v result += tests @@ -669,14 +671,14 @@ class LiteralTestSuite(TestSuite): result += test.GetBuildRequirements(rest, context) return result - def ListTests(self, current_path, path, context, mode): + def ListTests(self, current_path, path, context, arch, mode): (name, rest) = CarCdr(path) result = [ ] for test in self.tests: test_name = test.GetName() if not name or name.match(test_name): full_path = current_path + [test_name] - test.AddTestsToList(result, full_path, path, context, mode) + test.AddTestsToList(result, full_path, path, context, arch, mode) result.sort(cmp=lambda a, b: cmp(a.GetName(), b.GetName())) return result @@ -708,11 +710,11 @@ class Context(object): self.suppress_dialogs = suppress_dialogs self.store_unexpected_output = store_unexpected_output - def GetVm(self, mode): - if mode == 'debug': - name = 'out/Debug/node' + def GetVm(self, arch, mode): + if arch == 'none': + name = 'out/Debug/node' if mode == 'debug' else 'out/Release/node' else: - name = 'out/Release/node' + name = 'out/%s.%s/node' % (arch, mode) # Currently GYP does not support output_dir for MSVS. # http://code.google.com/p/gyp/issues/detail?id=40 @@ -729,9 +731,6 @@ class Context(object): return name - def GetVmCommand(self, testcase, mode): - return [self.GetVm(mode)] + self.GetVmFlags(testcase, mode) - def GetVmFlags(self, testcase, mode): return testcase.variant_flags + FLAGS[mode] @@ -1203,8 +1202,6 @@ def BuildOptions(): default='none') result.add_option("--snapshot", help="Run the tests with snapshot turned on", default=False, action="store_true") - result.add_option("--simulator", help="Run tests with architecture simulator", - default='none') result.add_option("--special-command", default=None) result.add_option("--use-http1", help="Pass --use-http1 switch to node", default=False, action="store_true") @@ -1235,29 +1232,8 @@ def BuildOptions(): def ProcessOptions(options): global VERBOSE VERBOSE = options.verbose + options.arch = options.arch.split(',') options.mode = options.mode.split(',') - for mode in options.mode: - if not mode in ['debug', 'release']: - print "Unknown mode %s" % mode - return False - if options.simulator != 'none': - # Simulator argument was set. Make sure arch and simulator agree. - if options.simulator != options.arch: - if options.arch == 'none': - options.arch = options.simulator - else: - print "Architecture %s does not match sim %s" %(options.arch, options.simulator) - return False - # Ensure that the simulator argument is handed down to scons. - options.scons_flags.append("simulator=" + options.simulator) - else: - # If options.arch is not set by the command line and no simulator setting - # was found, set the arch to the guess. - if options.arch == 'none': - options.arch = ARCH_GUESS - options.scons_flags.append("arch=" + options.arch) - if options.snapshot: - options.scons_flags.append("snapshot=on") return True @@ -1415,25 +1391,28 @@ def Main(): unclassified_tests = [ ] globally_unused_rules = None for path in paths: - for mode in options.mode: - if not exists(context.GetVm(mode)): - print "Can't find shell executable: '%s'" % context.GetVm(mode) - continue - env = { - 'mode': mode, - 'system': utils.GuessOS(), - 'arch': options.arch, - 'simulator': options.simulator - } - test_list = root.ListTests([], path, context, mode) - unclassified_tests += test_list - (cases, unused_rules, all_outcomes) = config.ClassifyTests(test_list, env) - if globally_unused_rules is None: - globally_unused_rules = set(unused_rules) - else: - globally_unused_rules = globally_unused_rules.intersection(unused_rules) - all_cases += cases - all_unused.append(unused_rules) + for arch in options.arch: + for mode in options.mode: + vm = context.GetVm(arch, mode) + if not exists(vm): + print "Can't find shell executable: '%s'" % vm + continue + env = { + 'mode': mode, + 'system': utils.GuessOS(), + 'arch': arch, + } + test_list = root.ListTests([], path, context, arch, mode) + unclassified_tests += test_list + (cases, unused_rules, all_outcomes) = ( + config.ClassifyTests(test_list, env)) + if globally_unused_rules is None: + globally_unused_rules = set(unused_rules) + else: + globally_unused_rules = ( + globally_unused_rules.intersection(unused_rules)) + all_cases += cases + all_unused.append(unused_rules) if options.cat: visited = set() -- 2.7.4