1 # Copyright (C) 2010 Google Inc. All rights reserved. 2 # 3 # Redistribution and use in source and binary forms, with or without 4 # modification, are permitted provided that the following conditions are 5 # met: 6 # 7 # * Redistributions of source code must retain the above copyright 8 # notice, this list of conditions and the following disclaimer. 9 # * Redistributions in binary form must reproduce the above 10 # copyright notice, this list of conditions and the following disclaimer 11 # in the documentation and/or other materials provided with the 12 # distribution. 13 # * Neither the name of Google Inc. nor the names of its 14 # contributors may be used to endorse or promote products derived from 15 # this software without specific prior written permission. 16 # 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 import webkitpy.thirdparty.unittest2 as unittest 30 import json 31 import optparse 32 import random 33 34 from webkitpy.common.host_mock import MockHost 35 from webkitpy.layout_tests.layout_package import json_results_generator 36 from webkitpy.layout_tests.models import test_expectations 37 from webkitpy.layout_tests.port import test 38 from webkitpy.thirdparty.mock import Mock 39 40 41 class JSONGeneratorTest(unittest.TestCase): 42 def setUp(self): 43 self.builder_name = 'DUMMY_BUILDER_NAME' 44 self.build_name = 'DUMMY_BUILD_NAME' 45 self.build_number = 'DUMMY_BUILDER_NUMBER' 46 47 # For archived results. 48 self._json = None 49 self._num_runs = 0 50 self._tests_set = set([]) 51 self._test_timings = {} 52 self._failed_count_map = {} 53 54 self._PASS_count = 0 55 self._DISABLED_count = 0 56 self._FLAKY_count = 0 57 self._FAILS_count = 0 58 self._fixable_count = 0 59 60 def test_strip_json_wrapper(self): 61 json = "['contents']" 62 self.assertEqual(json_results_generator.strip_json_wrapper(json_results_generator._JSON_PREFIX + json + json_results_generator._JSON_SUFFIX), json) 63 self.assertEqual(json_results_generator.strip_json_wrapper(json), json) 64 65 def _test_json_generation(self, passed_tests_list, failed_tests_list): 66 tests_set = set(passed_tests_list) | set(failed_tests_list) 67 68 DISABLED_tests = set([t for t in tests_set 69 if t.startswith('DISABLED_')]) 70 FLAKY_tests = set([t for t in tests_set 71 if t.startswith('FLAKY_')]) 72 FAILS_tests = set([t for t in tests_set 73 if t.startswith('FAILS_')]) 74 PASS_tests = tests_set - (DISABLED_tests | FLAKY_tests | FAILS_tests) 75 76 failed_tests = set(failed_tests_list) - DISABLED_tests 77 failed_count_map = dict([(t, 1) for t in failed_tests]) 78 79 test_timings = {} 80 i = 0 81 for test in tests_set: 82 test_timings[test] = float(self._num_runs * 100 + i) 83 i += 1 84 85 test_results_map = dict() 86 for test in tests_set: 87 test_results_map[test] = json_results_generator.TestResult(test, 88 failed=(test in failed_tests), 89 elapsed_time=test_timings[test]) 90 91 host = MockHost() 92 port = Mock() 93 port._filesystem = host.filesystem 94 generator = json_results_generator.JSONResultsGeneratorBase(port, 95 self.builder_name, self.build_name, self.build_number, 96 '', 97 None, # don't fetch past json results archive 98 test_results_map) 99 100 failed_count_map = dict([(t, 1) for t in failed_tests]) 101 102 # Test incremental json results 103 incremental_json = generator.get_json() 104 self._verify_json_results( 105 tests_set, 106 test_timings, 107 failed_count_map, 108 len(PASS_tests), 109 len(DISABLED_tests), 110 len(FLAKY_tests), 111 len(DISABLED_tests | failed_tests), 112 incremental_json, 113 1) 114 115 # We don't verify the results here, but at least we make sure the code runs without errors. 116 generator.generate_json_output() 117 generator.generate_times_ms_file() 118 119 def _verify_json_results(self, tests_set, test_timings, failed_count_map, 120 PASS_count, DISABLED_count, FLAKY_count, 121 fixable_count, 122 json, num_runs): 123 # Aliasing to a short name for better access to its constants. 124 JRG = json_results_generator.JSONResultsGeneratorBase 125 126 self.assertIn(JRG.VERSION_KEY, json) 127 self.assertIn(self.builder_name, json) 128 129 buildinfo = json[self.builder_name] 130 self.assertIn(JRG.FIXABLE, buildinfo) 131 self.assertIn(JRG.TESTS, buildinfo) 132 self.assertEqual(len(buildinfo[JRG.BUILD_NUMBERS]), num_runs) 133 self.assertEqual(buildinfo[JRG.BUILD_NUMBERS][0], self.build_number) 134 135 if tests_set or DISABLED_count: 136 fixable = {} 137 for fixable_items in buildinfo[JRG.FIXABLE]: 138 for (type, count) in fixable_items.iteritems(): 139 if type in fixable: 140 fixable[type] = fixable[type] + count 141 else: 142 fixable[type] = count 143 144 if PASS_count: 145 self.assertEqual(fixable[JRG.PASS_RESULT], PASS_count) 146 else: 147 self.assertTrue(JRG.PASS_RESULT not in fixable or 148 fixable[JRG.PASS_RESULT] == 0) 149 if DISABLED_count: 150 self.assertEqual(fixable[JRG.SKIP_RESULT], DISABLED_count) 151 else: 152 self.assertTrue(JRG.SKIP_RESULT not in fixable or 153 fixable[JRG.SKIP_RESULT] == 0) 154 if FLAKY_count: 155 self.assertEqual(fixable[JRG.FLAKY_RESULT], FLAKY_count) 156 else: 157 self.assertTrue(JRG.FLAKY_RESULT not in fixable or 158 fixable[JRG.FLAKY_RESULT] == 0) 159 160 if failed_count_map: 161 tests = buildinfo[JRG.TESTS] 162 for test_name in failed_count_map.iterkeys(): 163 test = self._find_test_in_trie(test_name, tests) 164 165 failed = 0 166 for result in test[JRG.RESULTS]: 167 if result[1] == JRG.FAIL_RESULT: 168 failed += result[0] 169 self.assertEqual(failed_count_map[test_name], failed) 170 171 timing_count = 0 172 for timings in test[JRG.TIMES]: 173 if timings[1] == test_timings[test_name]: 174 timing_count = timings[0] 175 self.assertEqual(1, timing_count) 176 177 if fixable_count: 178 self.assertEqual(sum(buildinfo[JRG.FIXABLE_COUNT]), fixable_count) 179 180 def _find_test_in_trie(self, path, trie): 181 nodes = path.split("/") 182 sub_trie = trie 183 for node in nodes: 184 self.assertIn(node, sub_trie) 185 sub_trie = sub_trie[node] 186 return sub_trie 187 188 def test_json_generation(self): 189 self._test_json_generation([], []) 190 self._test_json_generation(['A1', 'B1'], []) 191 self._test_json_generation([], ['FAILS_A2', 'FAILS_B2']) 192 self._test_json_generation(['DISABLED_A3', 'DISABLED_B3'], []) 193 self._test_json_generation(['A4'], ['B4', 'FAILS_C4']) 194 self._test_json_generation(['DISABLED_C5', 'DISABLED_D5'], ['A5', 'B5']) 195 self._test_json_generation( 196 ['A6', 'B6', 'FAILS_C6', 'DISABLED_E6', 'DISABLED_F6'], 197 ['FAILS_D6']) 198 199 # Generate JSON with the same test sets. (Both incremental results and 200 # archived results must be updated appropriately.) 201 self._test_json_generation( 202 ['A', 'FLAKY_B', 'DISABLED_C'], 203 ['FAILS_D', 'FLAKY_E']) 204 self._test_json_generation( 205 ['A', 'DISABLED_C', 'FLAKY_E'], 206 ['FLAKY_B', 'FAILS_D']) 207 self._test_json_generation( 208 ['FLAKY_B', 'DISABLED_C', 'FAILS_D'], 209 ['A', 'FLAKY_E']) 210 211 def test_hierarchical_json_generation(self): 212 # FIXME: Re-work tests to be more comprehensible and comprehensive. 213 self._test_json_generation(['foo/A'], ['foo/B', 'bar/C']) 214 215 def test_test_timings_trie(self): 216 test_port = test.TestPort(MockHost()) 217 individual_test_timings = [] 218 individual_test_timings.append(json_results_generator.TestResult('foo/bar/baz.html', elapsed_time=1.2)) 219 individual_test_timings.append(json_results_generator.TestResult('bar.html', elapsed_time=0.0001)) 220 trie = json_results_generator.test_timings_trie(test_port, individual_test_timings) 221 222 expected_trie = { 223 'bar.html': 0, 224 'foo': { 225 'bar': { 226 'baz.html': 1200, 227 } 228 } 229 } 230 231 self.assertEqual(json.dumps(trie), json.dumps(expected_trie)) 232