Home | History | Annotate | Download | only in layout_package
      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