| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import os |
| import subprocess |
| import sys |
| import unittest |
| |
| from expect_tests import pipeline |
| from expect_tests import listing |
| from expect_tests.type_definitions import Test, MultiTest |
| |
| SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) |
| DATA_DIR = os.path.join(SCRIPT_DIR, 'data') |
| ROOT_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR)) |
| |
| class ParsingAndConfigTest(unittest.TestCase): |
| def test_get_config(self): |
| """Testing that reading the config file works. |
| |
| Tests requires a specific content for data/.expect_tests.cfg""" |
| black_list = listing.get_config(DATA_DIR) |
| self.assertEqual(black_list, |
| set(['directory1', 'directory2', 'ignored'])) |
| |
| def test_parse_test_glob(self): |
| self.assertEqual(listing.parse_test_glob('a/b/c'), |
| (os.path.abspath('a/b/c'), ('*',))) |
| self.assertEqual(listing.parse_test_glob('a/b/c:'), |
| (os.path.abspath('a/b/c'), ('*',))) |
| self.assertEqual(listing.parse_test_glob('a/b/c:Test'), |
| (os.path.abspath('a/b/c'), ('Test',))) |
| self.assertEqual(listing.parse_test_glob('a/b/c:Test.Name'), |
| (os.path.abspath('a/b/c'), ('Test.Name',))) |
| self.assertRaises(ValueError, listing.parse_test_glob, 'a:b:c',) |
| self.assertRaises(ValueError, listing.parse_test_glob, 'a:b/c',) |
| |
| |
| class PathManipulationTest(unittest.TestCase): |
| """Tests for all path-manipulating functions. |
| |
| This set uses checked-out files in the present repository to avoid mocking |
| the I/O functions. |
| """ |
| |
| def test_get_python_root(self): |
| """This function uses the directory structure under data/""" |
| |
| cases = [ |
| # The root of a directory with no __init__.py file is that directory |
| (DATA_DIR, DATA_DIR), |
| # The root of a package is the parent directory |
| (os.path.join(DATA_DIR, 'package1'), DATA_DIR), |
| # The root of a subpackage is the parent directory of the root package. |
| (os.path.join(DATA_DIR, 'package1', 'subpackage1_1'), DATA_DIR) |
| ] |
| for path, result in cases: |
| self.assertEqual(listing.get_python_root(path), result) |
| |
| # When the path does not exist, you get an error. |
| self.assertRaises(ValueError, listing.get_python_root, |
| '____non-existing-path____') |
| |
| def test_single_dir_runtime_context(self): |
| """Computing RuntimeContext from a single directory path.""" |
| test_globs = [DATA_DIR] |
| contexts = listing.get_runtime_contexts(test_globs) |
| self.assertEqual(len(contexts), 1) |
| self.assertEqual(contexts[0].cwd, DATA_DIR) |
| for testing_c in contexts[0].testing_contexts: |
| self.assertNotEqual(testing_c.package_name, 'ignored') |
| |
| def test_single_package_runtime_context(self): |
| """Computing RuntimeContext from a single package path.""" |
| test_globs = [os.path.join(DATA_DIR, 'package1')] |
| contexts = listing.get_runtime_contexts(test_globs) |
| self.assertEqual(len(contexts), 1) |
| self.assertEqual(contexts[0].cwd, DATA_DIR) |
| testing_c = contexts[0].testing_contexts |
| self.assertEqual(len(testing_c), 1) |
| self.assertEqual(testing_c[0].package_name, 'package1') |
| |
| def test_two_packages_runtime_context(self): |
| """Computing RuntimeContext from two package paths.""" |
| test_globs = [os.path.join(DATA_DIR, 'package1'), |
| os.path.join(DATA_DIR, 'package2')] |
| contexts = listing.get_runtime_contexts(test_globs) |
| self.assertEqual(len(contexts), 1) |
| self.assertEqual(contexts[0].cwd, DATA_DIR) |
| testing_c = contexts[0].testing_contexts |
| self.assertEqual(len(testing_c), 2) |
| package_names = set() |
| for testing_c in contexts[0].testing_contexts: |
| package_names.add(testing_c.package_name) |
| |
| self.assertEqual(package_names, set(('package1', 'package2'))) |
| |
| def test_package_and_directory_runtime_context(self): |
| """Computing RuntimeContext from a package and a directory paths. |
| """ |
| # 'package1' is specified both explicit and implicitly through DATA_DIR |
| # We check that it is accounted for only once. |
| test_globs = [DATA_DIR, os.path.join(DATA_DIR, 'package1')] |
| contexts = listing.get_runtime_contexts(test_globs) |
| self.assertEqual(len(contexts), 1) |
| # 2 is the number of packages under DATA_DIR, not counting |
| # the ignored ones. |
| self.assertEqual(len(contexts[0].testing_contexts), 2) |
| |
| test_globs = [DATA_DIR, os.path.join(DATA_DIR, 'package1'), |
| os.path.join(DATA_DIR, 'package1', 'subpackage1_1')] |
| contexts = listing.get_runtime_contexts(test_globs) |
| self.assertEqual(len(contexts), 1) |
| self.assertEqual(len(contexts[0].testing_contexts), 2) |
| |
| def test_package_testing_context_from_path(self): |
| """Test the PackageTestingContext class""" |
| package_name = 'package1' |
| package1 = os.path.join(SCRIPT_DIR, 'data', package_name) |
| |
| context = listing.PackageTestingContext.from_path(package1) |
| self.assertTrue(os.path.isabs(context.cwd)) |
| self.assertTrue(len(context.package_name) > 0) |
| self.assertEqual(len(context.filters), 1) |
| self.assertEqual(context.filters[0][1], '*') |
| |
| # Testing with an empty tuple. |
| context = listing.PackageTestingContext.from_path(package1, |
| filters=()) |
| self.assertTrue(os.path.isabs(context.cwd)) |
| self.assertTrue(len(context.package_name) > 0) |
| self.assertEqual(len(context.filters), 1) |
| self.assertEqual(context.filters[0][1], '*') |
| |
| context = listing.PackageTestingContext.from_path(package1, |
| filters=('a*', 'b*')) |
| self.assertTrue(os.path.isabs(context.cwd)) |
| self.assertTrue(len(context.package_name) > 0) |
| self.assertEqual(len(context.filters), 2) |
| self.assertEqual(context.filters[0][1], 'a*') |
| self.assertEqual(context.filters[1][1], 'b*') |
| |
| self.assertRaises(ValueError, |
| listing.PackageTestingContext.from_path, |
| package1, filters=None) |
| |
| def test_merging_package_testing_context(self): |
| """Merging PackageTestingContexts pointing at the same package. |
| """ |
| package_name = 'package1' |
| package1 = os.path.join(SCRIPT_DIR, 'data', package_name) |
| package2 = os.path.join(SCRIPT_DIR, 'data', 'package2') |
| other_package1 = os.path.join(SCRIPT_DIR, 'data', 'other', package_name) |
| |
| # from_context_list |
| c1 = listing.PackageTestingContext.from_path(package1, filters=('a',)) |
| c2 = listing.PackageTestingContext.from_path(package1, |
| filters=('b','c')) |
| context = listing.PackageTestingContext.from_context_list((c1, c2)) |
| self.assertEqual(len(context.filters), 3) |
| self.assertEqual(set(filt[1] for filt in context.filters), |
| set(('a', 'b', 'c'))) |
| |
| c1 = listing.PackageTestingContext.from_path(package1, filters=('a',)) |
| c2 = listing.PackageTestingContext.from_path(package2, |
| filters=('b','c')) |
| self.assertRaises(ValueError, |
| listing.PackageTestingContext.from_context_list, |
| (c1, c2)) |
| |
| # Same package name, different paths. |
| c1 = listing.PackageTestingContext.from_path(package1, filters=('a',)) |
| c2 = listing.PackageTestingContext.from_path(other_package1, |
| filters=('b','c')) |
| self.assertRaises(ValueError, |
| listing.PackageTestingContext.from_context_list, |
| (c1, c2)) |
| |
| # Subpackage |
| subpackage_path = 'subpackage1_1' |
| subpackage1 = os.path.join(package1, subpackage_path) |
| c1 = listing.PackageTestingContext.from_path(subpackage1) |
| self.assertEqual(c1.package_name, 'package1') |
| self.assertEqual(c1.filters, [(subpackage_path, '*')]) |
| |
| def test_filter_glob_manipulation(self): |
| """globs to filter tests are modified if they don't end with a *.""" |
| package_name = 'package1' |
| package1 = os.path.join(SCRIPT_DIR, 'data', package_name) |
| subpackage_path = 'subpackage1_1' |
| subpackage1 = os.path.join(package1, subpackage_path) |
| c1 = listing.PackageTestingContext.from_path(subpackage1, |
| filters=('a*',)) |
| self.assertEqual(c1.package_name, 'package1') |
| self.assertEqual(c1.filters, [(subpackage_path, 'a*')]) |
| |
| for subpath, matcher in c1.itermatchers(): |
| self.assertIsNotNone(matcher.match('a')) |
| self.assertIsNotNone(matcher.match('ab')) |
| self.assertIsNone(matcher.match('ba')) |
| self.assertIsNone(matcher.match('b')) |
| self.assertEqual(subpath, subpackage_path) |
| |
| # Test that a star is added to the filter. |
| c1 = listing.PackageTestingContext.from_path(subpackage1, |
| filters=('a',)) |
| self.assertEqual(c1.package_name, 'package1') |
| self.assertEqual(c1.filters, [(subpackage_path, 'a')]) |
| |
| for subpath, matcher in c1.itermatchers(): |
| self.assertIsNotNone(matcher.match('a')) |
| self.assertIsNotNone(matcher.match('ab')) |
| self.assertIsNone(matcher.match('ba')) |
| self.assertIsNone(matcher.match('b')) |
| self.assertEqual(subpath, subpackage_path) |
| |
| |
| def test_processing_context(self): |
| """Test the ProcessingContext class""" |
| package_name = 'package1' |
| package1 = os.path.join(SCRIPT_DIR, 'data', package_name) |
| subpackage1 = os.path.join(SCRIPT_DIR, 'data', |
| package_name, 'subpackage1_1') |
| package2 = os.path.join(SCRIPT_DIR, 'data', 'package2') |
| other_package1 = os.path.join(SCRIPT_DIR, 'data', 'other', package_name) |
| |
| c0 = listing.PackageTestingContext.from_path(package1) |
| c1 = listing.PackageTestingContext.from_path(package1, filters=('a',)) |
| c2 = listing.PackageTestingContext.from_path(subpackage1, |
| filters=('d',)) |
| c3 = listing.PackageTestingContext.from_path(package2, |
| filters=('b','c')) |
| c4 = listing.PackageTestingContext.from_path(other_package1) |
| |
| # A processing context is a cwd + testing contexts. |
| # A testing context is cwd + one package name. |
| context = listing.ProcessingContext((c1, c2)) |
| self.assertEqual(len(context.testing_contexts), 1) |
| self.assertEqual(set(filt[1] |
| for filt in context.testing_contexts[0].filters), |
| set(('a', 'd'))) |
| |
| context = listing.ProcessingContext((c0, c1, c2)) |
| self.assertEqual(len(context.testing_contexts), 1) |
| self.assertEqual(set(filt[1] |
| for filt in context.testing_contexts[0].filters), |
| set(('*', 'a', 'd'))) |
| |
| |
| context = listing.ProcessingContext((c1, c2, c3)) |
| self.assertEqual(len(context.testing_contexts), 2) |
| |
| # Fails because there are two different cwd. |
| self.assertRaises(ValueError, listing.ProcessingContext, (c1, c4)) |
| |
| |
| class TestListingTest(unittest.TestCase): |
| """Test functions related to listing tests.""" |
| def test_walk_package(self): |
| """This function uses the directory structure under data/""" |
| modules = pipeline.walk_package('package1', DATA_DIR) |
| self.assertEqual(sorted(modules), |
| ['package1.file1_test', 'package1.file2_test', |
| 'package1.subpackage1_1.file3_test', |
| 'package1.subpackage1_1.subpackage1_1_1.file4_test']) |
| |
| modules = pipeline.walk_package('package1', DATA_DIR, |
| subpath='subpackage1_1') |
| self.assertEqual(sorted(modules), |
| ['package1.subpackage1_1.file3_test', |
| 'package1.subpackage1_1.subpackage1_1_1.file4_test']) |
| |
| self.assertRaises(ValueError, pipeline.walk_package, |
| 'package1', DATA_DIR, 'non-existing') |
| |
| def test_get_test_gens_package(self): |
| def get_test_names(tests): |
| test_names = [] |
| |
| for gen in tests: |
| for test in gen(): |
| if isinstance(test, MultiTest): |
| subtests = test.tests |
| else: |
| subtests = [test] |
| |
| for subtest in subtests: |
| self.assertIsInstance(subtest, Test) |
| test_names.append(subtest.name) |
| return test_names |
| |
| sys.path.insert(0, DATA_DIR) # Ugh. But won't work otherwise. |
| |
| |
| opts = argparse.Namespace(verbose=True) |
| # TODO(pgervais): add a MultiTest in a package under data/ |
| package1 = os.path.join(DATA_DIR, 'package1') |
| testing_context = listing.PackageTestingContext.from_path(package1) |
| tests = pipeline.get_test_gens_package(testing_context, opts) |
| self.assertEqual( |
| sorted(get_test_names(tests)), |
| ['package1.file1_test.File1Test.test_trivial_1', |
| 'package1.file2_test.File2Test.test_trivial_2', |
| 'package1.subpackage1_1.file3_test.File3Test.test_trivial_3', |
| 'package1.subpackage1_1.subpackage1_1_1.file4_test.' + |
| 'File4Test.test_trivial_4']) |
| |
| tests = pipeline.get_test_gens_package( |
| testing_context, opts, subpath='subpackage1_1') |
| self.assertEqual( |
| sorted(get_test_names(tests)), |
| ['package1.subpackage1_1.file3_test.File3Test.test_trivial_3', |
| 'package1.subpackage1_1.subpackage1_1_1.file4_test.' + |
| 'File4Test.test_trivial_4']) |
| |
| tests = pipeline.get_test_gens_package( |
| testing_context, opts, subpath='subpackage1_1/subpackage1_1_1') |
| |
| self.assertEqual( |
| get_test_names(tests), |
| ['package1.subpackage1_1.subpackage1_1_1.file4_test.' + |
| 'File4Test.test_trivial_4']) |
| |
| |
| class TestLauncherScript(unittest.TestCase): |
| """Test that expect_tests script actually works, in particular on Windows.""" |
| |
| def call(self, args, cwd=ROOT_DIR): |
| # Make sure our copy of expect_test is in front of the import list (in case |
| # there's a system installed expect_tests package). |
| pp = os.environ.get('PYTHONPATH', '').split(os.pathsep) |
| pp.insert(0, ROOT_DIR) |
| env = os.environ.copy() |
| env['PYTHONPATH'] = os.pathsep.join(pp) |
| proc = subprocess.Popen( |
| args=[ |
| sys.executable, os.path.join(ROOT_DIR, 'scripts', 'expect_tests'), |
| ] + list(args), |
| executable=sys.executable, |
| cwd=cwd, |
| env=env, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT) |
| out, _ = proc.communicate() |
| return proc.returncode, out |
| |
| def test_list(self): |
| ret, out = self.call(['list', DATA_DIR]) |
| if ret: |
| print out |
| self.assertEqual(ret, 0) |
| self.assertEqual( |
| sorted(out.splitlines()), |
| ['package1.file1_test.File1Test.test_trivial_1', |
| 'package1.file2_test.File2Test.test_trivial_2', |
| 'package1.subpackage1_1.file3_test.File3Test.test_trivial_3', |
| 'package1.subpackage1_1.subpackage1_1_1.file4_test.' + |
| 'File4Test.test_trivial_4']) |
| |
| def test_fail_on_caught_import_exception(self): |
| ret, out = self.call( |
| ['test', '--jobs', '5', 'package'], |
| cwd=os.path.join(DATA_DIR, 'exceptional', 'import_raises')) |
| if ret == 0: |
| print out |
| self.assertIn('ImportError: No module named this_package_does_not_exist', |
| out) |
| self.assertIn('ABORTED', out) |
| self.assertNotEqual(ret, 0) |
| |
| def test_fail_on_uncaught_import_exception(self): |
| ret, out = self.call( |
| ['test', '--jobs', '5', 'package'], |
| cwd=os.path.join(DATA_DIR, 'exceptional', 'import_crashes')) |
| if ret == 0: |
| print out |
| self.assertIn('ABORTED', out.splitlines()) |
| self.assertNotEqual(ret, 0) |
| |
| def test_fail_on_uncaught_test_exception(self): |
| ret, out = self.call( |
| ['test', '--jobs', '5', 'package'], |
| cwd=os.path.join(DATA_DIR, 'exceptional', 'test_raises')) |
| if ret == 0: |
| print out |
| self.assertIn( |
| '1 or more test runner processes crashed; run with `--jobs 1` to debug', |
| out) |
| self.assertIn('FAILED (crashed=1, process_crashed_errors=1)', out) |
| self.assertNotEqual(ret, 0) |