| # Copyright 2016 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 json |
| import os |
| import shutil |
| import tempfile |
| import unittest |
| import mock |
| import sys |
| import run_gpu_integration_test |
| import gpu_project_config |
| import inspect |
| import itertools |
| |
| from collections import defaultdict |
| |
| from telemetry.testing import browser_test_runner |
| from telemetry.internal.platform import system_info |
| |
| from gpu_tests import context_lost_integration_test |
| from gpu_tests import gpu_integration_test |
| from gpu_tests import path_util |
| from gpu_tests import pixel_integration_test |
| from gpu_tests import pixel_test_pages |
| from gpu_tests import webgl_conformance_integration_test |
| from gpu_tests import webgl_test_util |
| |
| from py_utils import discover |
| from typ import expectations_parser |
| from typ import json_results |
| |
| path_util.AddDirToPathIfNeeded(path_util.GetChromiumSrcDir(), 'tools', 'perf') |
| from chrome_telemetry_build import chromium_config |
| |
| OS_CONDITIONS = ['win', 'mac', 'android'] |
| GPU_CONDITIONS = ['amd', 'arm', 'broadcom', 'hisilicon', 'intel', 'imagination', |
| 'nvidia', 'qualcomm', 'vivante'] |
| WIN_CONDITIONS = ['xp', 'vista', 'win7', 'win8', 'win10'] |
| MAC_CONDITIONS = ['leopard', 'snowleopard', 'lion', 'mountainlion', |
| 'mavericks', 'yosemite', 'sierra', 'highsierra', 'mojave'] |
| # These aren't expanded out into "lollipop", "marshmallow", etc. |
| ANDROID_CONDITIONS = ['l', 'm', 'n', 'o', 'p', 'q'] |
| GENERIC_CONDITIONS = OS_CONDITIONS + GPU_CONDITIONS |
| |
| _map_specific_to_generic = {sos:'win' for sos in WIN_CONDITIONS} |
| _map_specific_to_generic.update({sos:'mac' for sos in MAC_CONDITIONS}) |
| _map_specific_to_generic.update({sos:'android' for sos in ANDROID_CONDITIONS}) |
| |
| _get_generic = lambda tags: set( |
| [_map_specific_to_generic.get(tag, tag) for tag in tags]) |
| |
| VENDOR_NVIDIA = 0x10DE |
| VENDOR_AMD = 0x1002 |
| VENDOR_INTEL = 0x8086 |
| |
| VENDOR_STRING_IMAGINATION = 'Imagination Technologies' |
| DEVICE_STRING_SGX = 'PowerVR SGX 554' |
| |
| ResultType = json_results.ResultType |
| |
| class MockPlatform(object): |
| def __init__(self, os_name, os_version_name=None): |
| self.os_name = os_name |
| self.os_version_name = os_version_name |
| |
| def GetOSName(self): |
| return self.os_name |
| |
| def GetOSVersionName(self): |
| return self.os_version_name |
| |
| |
| class MockBrowser(object): |
| def __init__(self, platform, gpu='', device='', vendor_string='', |
| device_string='', browser_type=None, gl_renderer=None, |
| passthrough=False): |
| self.platform = platform |
| self.browser_type = browser_type |
| sys_info = { |
| 'model_name': '', |
| 'gpu': { |
| 'devices': [ |
| {'vendor_id': gpu, 'device_id': device, |
| 'vendor_string': vendor_string, 'device_string': device_string}, |
| ], |
| 'aux_attributes': {'passthrough_cmd_decoder': passthrough} |
| } |
| } |
| if gl_renderer: |
| sys_info['gpu']['aux_attributes']['gl_renderer'] = gl_renderer |
| self.system_info = system_info.SystemInfo.FromDict(sys_info) |
| |
| def GetSystemInfo(self): |
| return self.system_info |
| |
| def __enter__(self): |
| return self |
| |
| def __exit__(self, *args): |
| pass |
| |
| |
| class MockArgs(object): |
| |
| def __init__(self, is_asan=False, webgl_version='1.0.0'): |
| self.is_asan = is_asan |
| self.webgl_conformance_version = webgl_version |
| self.webgl2_only = False |
| self.url = 'https://www.google.com' |
| self.duration = 10 |
| self.delay = 10 |
| self.resolution = 100 |
| self.fullscreen = False |
| self.underlay = False |
| self.logdir = '/tmp' |
| self.repeat = 1 |
| self.outliers = 0 |
| self.bypass_ipg = False |
| self.expected_vendor_id = 0 |
| self.expected_device_id = 0 |
| self.browser_options = [] |
| |
| |
| class MockAbstractGpuTestClass(gpu_integration_test.GpuIntegrationTest): |
| |
| @classmethod |
| def GenerateGpuTests(cls, options): |
| return [] |
| |
| def RunActualGpuTest(self, test_path, *args): |
| pass |
| |
| |
| class MockTestCaseWithoutExpectationsFile(MockAbstractGpuTestClass): |
| pass |
| |
| |
| class MockTestCaseWithExpectationsFile(MockAbstractGpuTestClass): |
| |
| @classmethod |
| def ExpectationsFiles(cls): |
| return ['example_test_expectations.txt'] |
| |
| |
| class MockPossibleBrowser(object): |
| |
| def __init__(self, browser=None): |
| self._returned_browser = browser |
| |
| def BrowserSession(self, options): |
| del options |
| return self._returned_browser |
| |
| |
| def _generateNvidiaExampleTagsForTestClassAndArgs(test_class, args): |
| class MockTestCase(test_class, MockAbstractGpuTestClass): |
| |
| @classmethod |
| def ExpectationsFiles(cls): |
| return ['example_test_expectations.txt'] |
| |
| _ = [_ for _ in MockTestCase.GenerateGpuTests(args)] |
| platform = MockPlatform('win', 'win10') |
| browser = MockBrowser( |
| platform, VENDOR_NVIDIA, 0x1cb3, browser_type='release', |
| gl_renderer='ANGLE Direct3D9') |
| possible_browser = MockPossibleBrowser(browser) |
| return set(MockTestCase.GenerateTags(args, possible_browser)) |
| |
| |
| def _checkTestExpectationsAreForExistingTests( |
| test_class, mock_options, test_names=None): |
| test_names = test_names or [ |
| args[0] for args in |
| test_class.GenerateGpuTests(mock_options)] |
| expectations_file = test_class.ExpectationsFiles()[0] |
| trie = {} |
| for test in test_names: |
| _trie = trie.setdefault(test[0], {}) |
| for l in test[1:]: |
| _trie = _trie.setdefault(l, {}) |
| f = open(expectations_file, 'r') |
| expectations_file = os.path.basename(expectations_file) |
| expectations = f.read() |
| f.close() |
| parser = expectations_parser.TaggedTestListParser(expectations) |
| for exp in parser.expectations: |
| _trie = trie |
| for l in exp.test: |
| if l == '*': |
| break |
| assert l in _trie, ( |
| "%s:%d: Glob '%s' does not match with any tests in the %s test suite" % |
| (expectations_file, exp.lineno, exp.test, test_class.Name())) |
| _trie = _trie[l] |
| |
| |
| def is_collision(s1, s2): |
| # s1 collides with s2 when s1 is a subset of s2 |
| # A tag is in both sets if its a generic tag |
| # and its in one set while a specifc tag covered by the generic tag is in |
| # the other set. |
| for tag in s1: |
| if (not tag in s2 and not ( |
| tag in GENERIC_CONDITIONS and |
| any(_map_specific_to_generic.get(t, t) == tag for t in s2)) and |
| not _map_specific_to_generic.get(tag, tag) in s2): |
| return False |
| return True |
| |
| |
| def _glob_conflicts_with_exp(possible_collision, exp): |
| reason_template = ( |
| 'Pattern \'{0}\' on line {1} has the %s expectation however the ' |
| 'the pattern on \'{2}\' line {3} has the Pass expectation'). format( |
| possible_collision.test, possible_collision.lineno, exp.test, |
| exp.lineno) |
| causes_regression = not( |
| ResultType.Failure in exp.results or ResultType.Skip in exp.results) |
| if (ResultType.Skip in possible_collision.results and |
| causes_regression): |
| return reason_template % 'Skip' |
| if (ResultType.Failure in possible_collision.results and |
| causes_regression): |
| return reason_template % 'Failure' |
| return '' |
| |
| |
| def _map_gpu_devices_to_vendors(tag_sets): |
| for tag_set in tag_sets: |
| if any(gpu in tag_set for gpu in GPU_CONDITIONS): |
| _map_specific_to_generic.update( |
| {t[0]: t[1] for t in |
| itertools.permutations(tag_set, 2) if (t[0] + '-').startswith(t[1])}) |
| break |
| |
| |
| def _checkTestExpectationGlobsForCollision(expectations, file_name): |
| """This function looks for collisions between test expectations with patterns |
| that match with test expectation patterns that are globs. A test expectation |
| collides with another if its pattern matches with another's glob and if its |
| tags is a super set of the other expectation's tags. The less specific test |
| expectation must have a failure or skip expectation while the more specific |
| test expectation does not. The more specific test expectation will trump |
| the less specific test expectation and may cause an unexpected regression. |
| |
| Args: |
| expectations: A string containing test expectations in the new format |
| file_name: Name of the file that the test expectations came from |
| """ |
| master_conflicts_found = False |
| error_msg = '' |
| trie = {} |
| parser = expectations_parser.TaggedTestListParser(expectations) |
| globs_to_expectations = defaultdict(list) |
| |
| _map_gpu_devices_to_vendors(parser.tag_sets) |
| |
| for exp in parser.expectations: |
| _trie = trie.setdefault(exp.test[0], {}) |
| for l in exp.test[1:]: |
| _trie = _trie.setdefault(l, {}) |
| _trie.setdefault('$', []).append(exp) |
| |
| for exp in parser.expectations: |
| _trie = trie |
| glob = '' |
| for l in exp.test: |
| if '*' in _trie: |
| globs_to_expectations[glob + '*'].append(exp) |
| glob += l |
| if l in _trie: |
| _trie = _trie[l] |
| else: |
| break |
| if '*' in _trie: |
| globs_to_expectations[glob + '*'].append(exp) |
| |
| for glob, expectations in globs_to_expectations.items(): |
| conflicts_found = False |
| globs_to_match = [e for e in expectations if e.test == glob] |
| matched_to_globs = [e for e in expectations if e.test != glob] |
| for match in matched_to_globs: |
| for possible_collision in globs_to_match: |
| reason = _glob_conflicts_with_exp(possible_collision, match) |
| if (reason and |
| is_collision(possible_collision.tags, match.tags)): |
| if not conflicts_found: |
| error_msg += ('\n\nFound conflicts for pattern %s in %s:\n' % |
| (glob, file_name)) |
| master_conflicts_found = conflicts_found = True |
| error_msg += (' line %d conflicts with line %d: %s\n' % |
| (possible_collision.lineno, match.lineno, reason)) |
| assert not master_conflicts_found, error_msg |
| |
| |
| def _checkTestExpectationPatternsForCollision(expectations, file_name): |
| """This function makes sure that any test expectations that have the same |
| pattern do not collide with each other. They collide when one expectation's |
| tags are a subset of the other expectation's tags. If the expectation with |
| the larger tag set is active during a test run, then the expectation's whose |
| tag set is a subset of the tags will be active as well. |
| |
| Args: |
| expectations: A string containing test expectations in the new format |
| file_name: Name of the file that the test expectations came from |
| """ |
| parser = expectations_parser.TaggedTestListParser(expectations) |
| tests_to_exps = defaultdict(list) |
| master_conflicts_found = False |
| error_msg = '' |
| _map_gpu_devices_to_vendors(parser.tag_sets) |
| |
| for exp in parser.expectations: |
| tests_to_exps[exp.test].append(exp) |
| for pattern, exps in tests_to_exps.items(): |
| conflicts_found = False |
| for e1, e2 in itertools.combinations(exps, 2): |
| if is_collision(e1.tags, e2.tags) or is_collision(e2.tags, e1.tags): |
| if not conflicts_found: |
| error_msg += ('\n\nFound conflicts for test %s in %s:\n' % |
| (pattern, file_name)) |
| master_conflicts_found = conflicts_found = True |
| error_msg += (' line %d conflicts with line %d\n' % |
| (e1.lineno, e2.lineno)) |
| assert not master_conflicts_found, error_msg |
| |
| |
| def _CheckPatternIsValid(pattern): |
| if not '*' in pattern and not 'WebglExtension_' in pattern: |
| full_path = os.path.normpath(os.path.join( |
| webgl_test_util.conformance_path, pattern)) |
| if not os.path.exists(full_path): |
| raise Exception('The WebGL conformance test path specified in ' + |
| 'expectation does not exist: ' + full_path) |
| |
| |
| def _testCheckTestExpectationsAreForExistingTests(expectations): |
| options = MockArgs() |
| expectations_file = tempfile.NamedTemporaryFile(delete=False) |
| expectations_file.write(expectations) |
| expectations_file.close() |
| |
| class _MockTestCase(object): |
| @classmethod |
| def Name(cls): |
| return '_MockTestCase' |
| |
| @classmethod |
| def GenerateGpuTests(cls, options): |
| tests = [('a/b/c', ())] |
| for t in tests: |
| yield t |
| |
| @classmethod |
| def ExpectationsFiles(cls): |
| return [expectations_file.name] |
| |
| _checkTestExpectationsAreForExistingTests(_MockTestCase, options) |
| |
| |
| def _FindTestCases(): |
| test_cases = [] |
| for start_dir in gpu_project_config.CONFIG.start_dirs: |
| modules_to_classes = discover.DiscoverClasses( |
| start_dir, |
| gpu_project_config.CONFIG.top_level_dir, |
| base_class=gpu_integration_test.GpuIntegrationTest) |
| test_cases.extend(modules_to_classes.values()) |
| return test_cases |
| |
| |
| class GpuIntegrationTestUnittest(unittest.TestCase): |
| def setUp(self): |
| self._test_state = {} |
| self._test_result = {} |
| |
| def _RunGpuIntegrationTests(self, test_name, extra_args=None): |
| extra_args = extra_args or [] |
| temp_file = tempfile.NamedTemporaryFile(delete=False) |
| temp_file.close() |
| try: |
| sys.argv = [ |
| run_gpu_integration_test.__file__, |
| test_name, |
| '--write-full-results-to=%s' % temp_file.name, |
| ] + extra_args |
| gpu_project_config.CONFIG = chromium_config.ChromiumConfig( |
| top_level_dir=path_util.GetGpuTestDir(), |
| benchmark_dirs=[ |
| os.path.join(path_util.GetGpuTestDir(), 'unittest_data')]) |
| run_gpu_integration_test.main() |
| with open(temp_file.name) as f: |
| self._test_result = json.load(f) |
| finally: |
| temp_file.close() |
| |
| def testCollisionInTestExpectationsWithSpecifcAndGenericOsTags(self): |
| test_expectations = '''# tags: [ mac win linux xp ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel win ] a/b/c/d [ Failure ] |
| [ intel xp debug ] a/b/c/d [ Skip ] |
| ''' |
| with self.assertRaises(AssertionError): |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testNoCollisionBetweenSpecificOsTags(self): |
| test_expectations = '''# tags: [ mac win linux xp win7 ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel win7 ] a/b/c/d [ Failure ] |
| [ intel xp debug ] a/b/c/d [ Skip ] |
| ''' |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags(self): |
| test_expectations = '''# tags: [ mac win linux xp ] |
| # tags: [ intel amd nvidia nvidia-0x01 ] |
| # tags: [ debug release ] |
| [ nvidia win ] a/b/c/d [ Failure ] |
| [ nvidia-0x01 xp debug ] a/b/c/d [ Skip ] |
| ''' |
| with self.assertRaises(AssertionError): |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags2(self): |
| test_expectations = '''# tags: [ mac win linux xp win7 ] |
| # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ] |
| # tags: [ debug release ] |
| [ nvidia-0x01 win ] a/b/c/* [ Failure ] |
| [ nvidia win7 debug ] a/b/c/* [ Skip ] |
| ''' |
| with self.assertRaises(AssertionError): |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testNoCollisionBetweenGpuDeviceTags(self): |
| test_expectations = '''# tags: [ mac win linux xp win7 ] |
| # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ] |
| # tags: [ debug release ] |
| [ nvidia-0x01 win7 ] a/b/c/d [ Failure ] |
| [ nvidia-0x02 win7 debug ] a/b/c/d [ Skip ] |
| [ nvidia win debug ] a/b/c/* [ Skip ] |
| ''' |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testMixGenericandSpecificTagsInCollidingSets(self): |
| test_expectations = '''# tags: [ mac win linux xp win7 ] |
| # tags: [ intel amd nvidia nvidia-0x01 ] |
| # tags: [ debug release ] |
| [ nvidia-0x01 win ] a/b/c/d [ Failure ] |
| [ nvidia win7 debug ] a/b/c/d [ Skip ] |
| ''' |
| with self.assertRaises(AssertionError): |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testCollisionInTestExpectationCausesAssertion(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel win ] a/b/c/d [ Failure ] |
| [ intel win debug ] a/b/c/d [ Skip ] |
| [ intel ] a/b/c/d [ Failure ] |
| [ amd mac ] a/b [ RetryOnFailure ] |
| [ mac ] a/b [ Skip ] |
| [ amd mac ] a/b/c [ Failure ] |
| [ intel mac ] a/b/c [ Failure ] |
| ''' |
| with self.assertRaises(AssertionError) as context: |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| self.assertIn("Found conflicts for test a/b/c/d in test.txt:", |
| str(context.exception)) |
| self.assertIn('line 4 conflicts with line 5', |
| str(context.exception)) |
| self.assertIn('line 4 conflicts with line 6', |
| str(context.exception)) |
| self.assertIn('line 5 conflicts with line 6', |
| str(context.exception)) |
| self.assertIn("Found conflicts for test a/b in test.txt:", |
| str(context.exception)) |
| self.assertIn('line 7 conflicts with line 8', |
| str(context.exception)) |
| self.assertNotIn("Found conflicts for test a/b/c in test.txt:", |
| str(context.exception)) |
| |
| def testCollisionWithGlobsWithFailureExpectation(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel debug ] a/b/c/d* [ Failure ] |
| [ intel debug mac ] a/b/c/d [ RetryOnFailure ] |
| [ intel debug mac ] a/b/c/d/e [ Failure ] |
| ''' |
| with self.assertRaises(AssertionError) as context: |
| _checkTestExpectationGlobsForCollision(test_expectations, 'test.txt') |
| self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:', |
| str(context.exception)) |
| self.assertIn(("line 4 conflicts with line 5: Pattern 'a/b/c/d*' on line 4 " |
| "has the Failure expectation however the the pattern on 'a/b/c/d'" |
| " line 5 has the Pass expectation"), |
| str(context.exception)) |
| self.assertNotIn('line 4 conflicts with line 6', |
| str(context.exception)) |
| |
| def testNoCollisionWithGlobsWithFailureExpectation(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel debug mac ] a/b/c/* [ Failure ] |
| [ intel debug ] a/b/c/d [ Failure ] |
| [ intel debug ] a/b/c/d [ Skip ] |
| ''' |
| _checkTestExpectationGlobsForCollision(test_expectations, 'test.txt') |
| |
| def testCollisionWithGlobsWithSkipExpectation(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel debug ] a/b/c/d* [ Skip ] |
| [ intel debug mac ] a/b/c/d [ Failure ] |
| [ intel debug mac ] a/b/c/d/e [ RetryOnFailure ] |
| ''' |
| with self.assertRaises(AssertionError) as context: |
| _checkTestExpectationGlobsForCollision(test_expectations, 'test.txt') |
| self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:', |
| str(context.exception)) |
| self.assertNotIn('line 4 conflicts with line 5', |
| str(context.exception)) |
| self.assertIn('line 4 conflicts with line 6', |
| str(context.exception)) |
| self.assertIn(("line 4 conflicts with line 6: Pattern 'a/b/c/d*' on line 4 " |
| "has the Skip expectation however the the pattern on 'a/b/c/d/e'" |
| " line 6 has the Pass expectation"), |
| str(context.exception)) |
| |
| def testNoCollisionWithGlobsWithSkipExpectation(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel debug mac ] a/b/c/* [ Skip ] |
| [ intel debug ] a/b/c/d [ Skip ] |
| [ intel debug ] a/b/c/d [ Skip ] |
| ''' |
| _checkTestExpectationGlobsForCollision(test_expectations, 'test.txt') |
| |
| def testNoCollisionInTestExpectations(self): |
| test_expectations = '''# tags: [ mac win linux ] |
| # tags: [ intel amd nvidia ] |
| # tags: [ debug release ] |
| [ intel win release ] a/b/* [ Failure ] |
| [ intel debug ] a/b/c/d [ Failure ] |
| [ nvidia debug ] a/b/c/d [ Failure ] |
| ''' |
| _checkTestExpectationPatternsForCollision(test_expectations, 'test.txt') |
| |
| def testNoCollisionsForSameTestsInGpuTestExpectations(self): |
| webgl_conformance_test_class = ( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest) |
| for test_case in _FindTestCases(): |
| if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__: |
| for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)): |
| _ = list(test_case.GenerateGpuTests( |
| MockArgs(webgl_version=('%d.0.0' % i)))) |
| if test_case.ExpectationsFiles(): |
| with open(test_case.ExpectationsFiles()[0]) as f: |
| _checkTestExpectationPatternsForCollision(f.read(), |
| os.path.basename(f.name)) |
| |
| def testNoCollisionsWithGlobsInGpuTestExpectations(self): |
| webgl_conformance_test_class = ( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest) |
| for test_case in _FindTestCases(): |
| if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__: |
| for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)): |
| _ = list(test_case.GenerateGpuTests( |
| MockArgs(webgl_version=('%d.0.0' % i)))) |
| if test_case.ExpectationsFiles(): |
| with open(test_case.ExpectationsFiles()[0]) as f: |
| _checkTestExpectationGlobsForCollision(f.read(), |
| os.path.basename(f.name)) |
| |
| def testGpuTestExpectationsAreForExistingTests(self): |
| options = MockArgs() |
| for test_case in _FindTestCases(): |
| if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__: |
| if (test_case.Name() not in ('pixel', 'webgl_conformance') |
| and test_case.ExpectationsFiles()): |
| _checkTestExpectationsAreForExistingTests(test_case, options) |
| |
| def testWebglTestPathsExist(self): |
| webgl_test_class = ( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest) |
| for test_case in _FindTestCases(): |
| if test_case == webgl_test_class: |
| for i in xrange(1, 3): |
| _ = list(test_case.GenerateGpuTests( |
| MockArgs(webgl_version='%d.0.0' % i))) |
| with open(test_case.ExpectationsFiles()[0], 'r') as f: |
| expectations = expectations_parser.TaggedTestListParser(f.read()) |
| for exp in expectations.expectations: |
| _CheckPatternIsValid(exp.test) |
| |
| def testPixelTestsExpectationsAreForExistingTests(self): |
| pixel_test_names = [] |
| for _, method in inspect.getmembers( |
| pixel_test_pages.PixelTestPages, predicate=inspect.isfunction): |
| pixel_test_names.extend( |
| [p.name for p in method( |
| pixel_integration_test.PixelIntegrationTest.test_base_name)]) |
| _checkTestExpectationsAreForExistingTests( |
| pixel_integration_test.PixelIntegrationTest, |
| MockArgs(), pixel_test_names) |
| |
| def testExpectationPatternNotInGeneratedTests(self): |
| with self.assertRaises(AssertionError) as context: |
| _testCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]') |
| self.assertIn(("1: Glob 'a/b/d' does not match with any" |
| " tests in the _MockTestCase test suite"), |
| str(context.exception)) |
| |
| def testGlobMatchesTestName(self): |
| _testCheckTestExpectationsAreForExistingTests('a/b* [ Failure ]') |
| |
| def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self): |
| self._RunGpuIntegrationTests( |
| 'run_tests_with_expectations_files', ['--retry-limit=1']) |
| self.assertEqual( |
| self._test_result['tests']['a']['b'] |
| ['unexpected-fail.html']['actual'], |
| 'FAIL FAIL') |
| |
| def testDefaultRetryArgumentsinRunGpuIntegrationTests(self): |
| self._RunGpuIntegrationTests('run_tests_with_expectations_files') |
| self.assertEqual( |
| self._test_result['tests']['a']['b']['expected-flaky.html']['actual'], |
| 'FAIL FAIL FAIL') |
| |
| def testTestNamePrefixGenerationInRunGpuIntegrationTests(self): |
| self._RunGpuIntegrationTests('simple_integration_unittest') |
| self.assertIn('expected_failure', self._test_result['tests']) |
| |
| def testWithoutExpectationsFilesGenerateTagsReturnsEmptyList(self): |
| # we need to make sure that GenerateTags() returns an empty list if |
| # there are no expectations files returned from ExpectationsFiles() or |
| # else Typ will raise an exception |
| args = MockArgs() |
| possible_browser = MockPossibleBrowser() |
| self.assertFalse(MockTestCaseWithoutExpectationsFile.GenerateTags( |
| args, possible_browser)) |
| |
| def _TestTagGenerationForMockPlatform(self, test_class, args): |
| tag_set = _generateNvidiaExampleTagsForTestClassAndArgs( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest, |
| args) |
| self.assertTrue( |
| set(['win', 'win10', 'd3d9', 'release', |
| 'nvidia', 'nvidia-0x1cb3', 'no-passthrough']).issubset(tag_set)) |
| return tag_set |
| |
| def testGenerateContextLostExampleTagsForAsan(self): |
| args = MockArgs(is_asan=True) |
| tag_set = self._TestTagGenerationForMockPlatform( |
| context_lost_integration_test.ContextLostIntegrationTest, |
| args) |
| self.assertIn('asan', tag_set) |
| self.assertNotIn('no-asan', tag_set) |
| |
| def testGenerateContextLostExampleTagsForNoAsan(self): |
| args = MockArgs() |
| tag_set = self._TestTagGenerationForMockPlatform( |
| context_lost_integration_test.ContextLostIntegrationTest, |
| args) |
| self.assertIn('no-asan', tag_set) |
| self.assertNotIn('asan', tag_set) |
| |
| def testGenerateWebglConformanceExampleTagsForWebglVersion1andAsan(self): |
| args = MockArgs(is_asan=True, webgl_version='1.0.0') |
| tag_set = self._TestTagGenerationForMockPlatform( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest, |
| args) |
| self.assertTrue(set(['asan', 'webgl-version-1']).issubset(tag_set)) |
| self.assertFalse(set(['no-asan', 'webgl-version-2']) & tag_set) |
| |
| def testGenerateWebglConformanceExampleTagsForWebglVersion2andNoAsan(self): |
| args = MockArgs(is_asan=False, webgl_version='2.0.0') |
| tag_set = self._TestTagGenerationForMockPlatform( |
| webgl_conformance_integration_test.WebGLConformanceIntegrationTest, |
| args) |
| self.assertTrue(set(['no-asan', 'webgl-version-2']) .issubset(tag_set)) |
| self.assertFalse(set(['asan', 'webgl-version-1']) & tag_set) |
| |
| def testGenerateNvidiaExampleTags(self): |
| args = MockArgs() |
| platform = MockPlatform('win', 'win10') |
| browser = MockBrowser( |
| platform, VENDOR_NVIDIA, 0x1cb3, browser_type='release', |
| gl_renderer='ANGLE Direct3D9') |
| possible_browser = MockPossibleBrowser(browser) |
| self.assertEqual( |
| set(MockTestCaseWithExpectationsFile.GenerateTags( |
| args, possible_browser)), |
| set(['win', 'win10', 'release', 'nvidia', 'nvidia-0x1cb3', |
| 'd3d9', 'no-passthrough'])) |
| |
| def testGenerateVendorTagUsingVendorString(self): |
| args = MockArgs() |
| platform = MockPlatform('mac', 'mojave') |
| browser = MockBrowser( |
| platform, browser_type='release', |
| gl_renderer='ANGLE OpenGL ES', passthrough=True, |
| vendor_string=VENDOR_STRING_IMAGINATION, |
| device_string=DEVICE_STRING_SGX) |
| possible_browser = MockPossibleBrowser(browser) |
| self.assertEqual( |
| set(MockTestCaseWithExpectationsFile.GenerateTags( |
| args, possible_browser)), |
| set(['mac', 'mojave', 'release', 'imagination', |
| 'imagination-powervr-sgx-554', |
| 'opengles', 'passthrough'])) |
| |
| def testGenerateVendorTagUsingDeviceString(self): |
| args = MockArgs() |
| platform = MockPlatform('mac', 'mojave') |
| browser = MockBrowser( |
| platform, browser_type='release', |
| vendor_string='illegal vendor string', |
| device_string='ANGLE (Imagination, Triangle Monster 3000, 1.0)') |
| possible_browser = MockPossibleBrowser(browser) |
| self.assertEqual( |
| set(MockTestCaseWithExpectationsFile.GenerateTags( |
| args, possible_browser)), |
| set(['mac', 'mojave', 'release', 'imagination', |
| 'imagination-triangle-monster-3000', |
| 'no-angle', 'no-passthrough'])) |
| |
| def testSimpleIntegrationTest(self): |
| self._RunIntegrationTest( |
| 'simple_integration_unittest', |
| ['unexpected_error', |
| 'unexpected_failure'], |
| ['expected_flaky', |
| 'expected_failure'], |
| ['expected_skip'], |
| ['--retry-only-retry-on-failure', '--retry-limit=3', |
| '--test-name-prefix=unittest_data.integration_tests.SimpleTest.']) |
| # TODO(rmhasan): re-enable assertion after landing crrev.com/c/1594272 |
| # The number of browser starts include the one call to StartBrowser at the |
| # beginning of the run of the test suite and for each RestartBrowser call |
| # which happens after every failure |
| # self.assertEquals(self._test_state['num_browser_starts'], 6) |
| |
| def testIntegrationTesttWithBrowserFailure(self): |
| self._RunIntegrationTest( |
| 'browser_start_failure_integration_unittest', [], |
| ['unittest_data.integration_tests.BrowserStartFailureTest.restart'], |
| [], []) |
| self.assertEquals(self._test_state['num_browser_crashes'], 2) |
| self.assertEquals(self._test_state['num_browser_starts'], 3) |
| |
| def testIntegrationTestWithBrowserCrashUponStart(self): |
| self._RunIntegrationTest( |
| 'browser_crash_after_start_integration_unittest', [], |
| [('unittest_data.integration_tests.BrowserCrashAfterStartTest.restart')], |
| [], []) |
| self.assertEquals(self._test_state['num_browser_crashes'], 2) |
| self.assertEquals(self._test_state['num_browser_starts'], 3) |
| |
| def testRetryLimit(self): |
| self._RunIntegrationTest( |
| 'test_retry_limit', |
| ['unittest_data.integration_tests.TestRetryLimit.unexpected_failure'], |
| [], |
| [], |
| ['--retry-limit=2']) |
| # The number of attempted runs is 1 + the retry limit. |
| self.assertEquals(self._test_state['num_test_runs'], 3) |
| |
| def _RunTestsWithExpectationsFiles(self): |
| self._RunIntegrationTest( |
| 'run_tests_with_expectations_files', |
| [('a/b/unexpected-fail.html')], |
| [('a/b/expected-fail.html'), |
| ('a/b/expected-flaky.html')], |
| [('a/b/expected-skip.html')], |
| ['--retry-limit=3', '--retry-only-retry-on-failure-tests', |
| ('--test-name-prefix=unittest_data.integration_tests.' |
| 'RunTestsWithExpectationsFiles.')]) |
| |
| def testUseTestExpectationsFileToHandleExpectedSkip(self): |
| self._RunTestsWithExpectationsFiles() |
| results = self._test_result['tests']['a']['b']['expected-skip.html'] |
| self.assertEqual(results['expected'], 'SKIP') |
| self.assertEqual(results['actual'], 'SKIP') |
| self.assertNotIn('is_regression', results) |
| |
| def testUseTestExpectationsFileToHandleUnexpectedTestFailure(self): |
| self._RunTestsWithExpectationsFiles() |
| results = self._test_result['tests']['a']['b']['unexpected-fail.html'] |
| self.assertEqual(results['expected'], 'PASS') |
| self.assertEqual(results['actual'], 'FAIL') |
| self.assertIn('is_regression', results) |
| |
| def testUseTestExpectationsFileToHandleExpectedFailure(self): |
| self._RunTestsWithExpectationsFiles() |
| results = self._test_result['tests']['a']['b']['expected-fail.html'] |
| self.assertEqual(results['expected'], 'FAIL') |
| self.assertEqual(results['actual'], 'FAIL') |
| self.assertNotIn('is_regression', results) |
| |
| def testUseTestExpectationsFileToHandleExpectedFlakyTest(self): |
| self._RunTestsWithExpectationsFiles() |
| results = self._test_result['tests']['a']['b']['expected-flaky.html'] |
| self.assertEqual(results['expected'], 'PASS') |
| self.assertEqual(results['actual'], 'FAIL FAIL FAIL PASS') |
| self.assertNotIn('is_regression', results) |
| |
| def testRepeat(self): |
| self._RunIntegrationTest( |
| 'test_repeat', |
| [], |
| ['unittest_data.integration_tests.TestRepeat.success'], |
| [], |
| ['--repeat=3']) |
| self.assertEquals(self._test_state['num_test_runs'], 3) |
| |
| def testAlsoRunDisabledTests(self): |
| self._RunIntegrationTest( |
| 'test_also_run_disabled_tests', |
| ['skip', 'flaky'], |
| # Tests that are expected to fail and do fail are treated as test passes |
| ['expected_failure'], |
| [], |
| ['--all', '--test-name-prefix', |
| 'unittest_data.integration_tests.TestAlsoRunDisabledTests.', |
| '--retry-limit=3', '--retry-only-retry-on-failure']) |
| self.assertEquals(self._test_state['num_flaky_test_runs'], 4) |
| self.assertEquals(self._test_state['num_test_runs'], 6) |
| |
| def testStartBrowser_Retries(self): |
| class TestException(Exception): |
| pass |
| def SetBrowserAndRaiseTestException(): |
| gpu_integration_test.GpuIntegrationTest.browser = ( |
| mock.MagicMock()) |
| raise TestException |
| gpu_integration_test.GpuIntegrationTest.browser = None |
| gpu_integration_test.GpuIntegrationTest.platform = None |
| with mock.patch.object( |
| gpu_integration_test.serially_executed_browser_test_case.\ |
| SeriallyExecutedBrowserTestCase, |
| 'StartBrowser', |
| side_effect=SetBrowserAndRaiseTestException) as mock_start_browser: |
| with mock.patch.object( |
| gpu_integration_test.GpuIntegrationTest, |
| 'StopBrowser') as mock_stop_browser: |
| with self.assertRaises(TestException): |
| gpu_integration_test.GpuIntegrationTest.StartBrowser() |
| self.assertEqual(mock_start_browser.call_count, |
| gpu_integration_test._START_BROWSER_RETRIES) |
| self.assertEqual(mock_stop_browser.call_count, |
| gpu_integration_test._START_BROWSER_RETRIES) |
| |
| def _RunIntegrationTest(self, test_name, failures, successes, skips, |
| additional_args): |
| config = chromium_config.ChromiumConfig( |
| top_level_dir=path_util.GetGpuTestDir(), |
| benchmark_dirs=[ |
| os.path.join(path_util.GetGpuTestDir(), 'unittest_data')]) |
| temp_dir = tempfile.mkdtemp() |
| test_results_path = os.path.join(temp_dir, 'test_results.json') |
| test_state_path = os.path.join(temp_dir, 'test_state.json') |
| try: |
| browser_test_runner.Run( |
| config, |
| [test_name, |
| '--write-full-results-to=%s' % test_results_path, |
| '--test-state-json-path=%s' % test_state_path] + additional_args) |
| with open(test_results_path) as f: |
| self._test_result = json.load(f) |
| with open(test_state_path) as f: |
| self._test_state = json.load(f) |
| actual_successes, actual_failures, actual_skips = ( |
| self._ExtractTestResults(self._test_result)) |
| self.assertEquals(set(actual_failures), set(failures)) |
| self.assertEquals(set(actual_successes), set(successes)) |
| self.assertEquals(set(actual_skips), set(skips)) |
| finally: |
| shutil.rmtree(temp_dir) |
| |
| def _ExtractTestResults(self, test_result): |
| delimiter = test_result['path_delimiter'] |
| failures = [] |
| successes = [] |
| skips = [] |
| def _IsLeafNode(node): |
| test_dict = node[1] |
| return ('expected' in test_dict and |
| isinstance(test_dict['expected'], basestring)) |
| node_queues = [] |
| for t in test_result['tests']: |
| node_queues.append((t, test_result['tests'][t])) |
| while node_queues: |
| node = node_queues.pop() |
| full_test_name, test_dict = node |
| if _IsLeafNode(node): |
| if all(res not in test_dict['expected'].split() for res in |
| test_dict['actual'].split()): |
| failures.append(full_test_name) |
| elif test_dict['expected'] == test_dict['actual'] == 'SKIP': |
| skips.append(full_test_name) |
| else: |
| successes.append(full_test_name) |
| else: |
| for k in test_dict: |
| node_queues.append( |
| ('%s%s%s' % (full_test_name, delimiter, k), |
| test_dict[k])) |
| return successes, failures, skips |