• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env vpython3
2# Copyright 2016 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import copy
7import unittest
8
9import six
10
11import results_merger
12
13GOOD_JSON_TEST_RESULT_0 = {
14    'tests': {
15        'car': {
16            'honda': {
17                'expected': 'PASS',
18                'actual': 'PASS'
19            },
20            'toyota': {
21                'expected': 'FAIL',
22                'actual': 'FAIL'
23            }
24        },
25        'computer': {
26            'dell': {
27                'expected': 'PASS',
28                'actual': 'PASS'
29            }
30        },
31    },
32    'interrupted': False,
33    'version': 3,
34    'seconds_since_epoch': 1406662289.76,
35    'num_failures_by_type': {
36        'FAIL': 0,
37        'PASS': 2
38    },
39    'layout_tests_dir': 'abc'
40}
41
42GOOD_JSON_TEST_RESULT_1 = {
43    'tests': {
44        'car': {
45            'tesla': {
46                'expected': 'PASS',
47                'actual': 'PASS'
48            },
49        },
50        'burger': {
51            'mcdonald': {
52                'expected': 'PASS',
53                'actual': 'PASS'
54            }
55        },
56    },
57    'interrupted': False,
58    'version': 3,
59    'seconds_since_epoch': 1406662283.11,
60    'num_failures_by_type': {
61        'FAIL': 0,
62        'PASS': 2
63    },
64    'layout_tests_dir': '123'
65}
66
67GOOD_JSON_TEST_RESULT_2 = {
68    'tests': {
69        'car': {
70            'mercedes': {
71                'expected': 'PASS',
72                'actual': 'FAIL'
73            },
74        },
75        'burger': {
76            'in n out': {
77                'expected': 'PASS',
78                'actual': 'PASS'
79            }
80        },
81    },
82    'interrupted': True,
83    'version': 3,
84    'seconds_since_epoch': 1406662200.01,
85    'num_failures_by_type': {
86        'FAIL': 1,
87        'PASS': 1
88    }
89}
90
91GOOD_JSON_TEST_RESULT_MERGED = {
92    'tests': {
93        'car': {
94            'tesla': {
95                'expected': 'PASS',
96                'actual': 'PASS'
97            },
98            'mercedes': {
99                'expected': 'PASS',
100                'actual': 'FAIL'
101            },
102            'honda': {
103                'expected': 'PASS',
104                'actual': 'PASS'
105            },
106            'toyota': {
107                'expected': 'FAIL',
108                'actual': 'FAIL'
109            }
110        },
111        'computer': {
112            'dell': {
113                'expected': 'PASS',
114                'actual': 'PASS'
115            }
116        },
117        'burger': {
118            'mcdonald': {
119                'expected': 'PASS',
120                'actual': 'PASS'
121            },
122            'in n out': {
123                'expected': 'PASS',
124                'actual': 'PASS'
125            }
126        }
127    },
128    'interrupted': True,
129    'version': 3,
130    'seconds_since_epoch': 1406662200.01,
131    'num_failures_by_type': {
132        'FAIL': 1,
133        'PASS': 5
134    },
135    'layout_tests_dir': '123'
136}
137
138
139def extend(initial, add):
140  out = copy.deepcopy(initial)
141  out.update(add)
142  return out
143
144
145def remove(initial, keys):
146  out = copy.deepcopy(initial)
147  for k in keys:
148    del out[k]
149  return out
150
151
152# These unittests are run in PRESUBMIT, but not by recipe_simulation_test, hence
153# to avoid false alert on missing coverage by recipe_simulation_test, we mark
154# these code as no cover.
155class MergingTest(unittest.TestCase):  # pragma: no cover
156  maxDiff = None  # Show full diff if assertion fail
157
158  def test_merge_tries(self):
159    self.assertEqual({
160        'a': 'A',
161        'b': {
162            'c': 'C'
163        }
164    }, results_merger.merge_tries({
165        'a': 'A',
166        'b': {}
167    }, {'b': {
168        'c': 'C'
169    }}))
170
171  def test_merge_tries_unmergable(self):
172    with six.assertRaisesRegex(self, results_merger.MergeException, 'a:b'):
173      results_merger.merge_tries({'a': {'b': 'A'}}, {'a': {'b': 'C'}})
174
175  def test_merge_metadata(self):
176    metadata1 = {'metadata': {'tags': ['foo', 'bar']}}
177    metadata2 = {'metadata': {'tags': ['foo', 'bat']}}
178    merged_results = results_merger.merge_test_results([
179        extend(GOOD_JSON_TEST_RESULT_0, metadata1),
180        extend(GOOD_JSON_TEST_RESULT_1, metadata2)
181    ])
182    self.assertEqual(merged_results['metadata']['tags'], ['foo', 'bat'])
183
184  def test_merge_json_test_results_nop(self):
185    good_json_results = (GOOD_JSON_TEST_RESULT_0, GOOD_JSON_TEST_RESULT_1,
186                         GOOD_JSON_TEST_RESULT_2, GOOD_JSON_TEST_RESULT_MERGED)
187    for j in good_json_results:
188      # Clone so we can check the input dictionaries are not modified
189      a = copy.deepcopy(j)
190      self.assertEqual(results_merger.merge_test_results([a]), j)
191      self.assertEqual(a, j)
192
193  def test_merge_json_test_results_invalid_version(self):
194    with self.assertRaises(results_merger.MergeException):
195      results_merger.merge_test_results([
196          extend(GOOD_JSON_TEST_RESULT_0, {'version': 5}),
197      ])
198
199    with self.assertRaises(results_merger.MergeException):
200      results_merger.merge_test_results([
201          GOOD_JSON_TEST_RESULT_0,
202          extend(GOOD_JSON_TEST_RESULT_1, {'version': 5}),
203      ])
204
205  def test_merge_json_test_results_missing_version(self):
206    with self.assertRaises(results_merger.MergeException):
207      results_merger.merge_test_results([
208          remove(GOOD_JSON_TEST_RESULT_0, ['version']),
209      ])
210
211    with self.assertRaises(results_merger.MergeException):
212      results_merger.merge_test_results([
213          GOOD_JSON_TEST_RESULT_0,
214          remove(GOOD_JSON_TEST_RESULT_1, ['version']),
215      ])
216
217  def test_merge_json_test_results_invalid_extra(self):
218    with self.assertRaises(results_merger.MergeException):
219      results_merger.merge_test_results([
220          extend(GOOD_JSON_TEST_RESULT_0, {'extra': True}),
221      ])
222
223    with self.assertRaises(results_merger.MergeException):
224      results_merger.merge_test_results([
225          GOOD_JSON_TEST_RESULT_0,
226          extend(GOOD_JSON_TEST_RESULT_1, {'extra': True}),
227      ])
228
229  def test_merge_json_test_results_missing_required(self):
230    with self.assertRaises(results_merger.MergeException):
231      results_merger.merge_test_results([
232          remove(GOOD_JSON_TEST_RESULT_0, ['interrupted']),
233      ])
234
235    with self.assertRaises(results_merger.MergeException):
236      results_merger.merge_test_results([
237          GOOD_JSON_TEST_RESULT_0,
238          remove(GOOD_JSON_TEST_RESULT_1, ['interrupted']),
239      ])
240
241  def test_merge_json_test_results_multiple(self):
242    self.assertEqual(
243        results_merger.merge_test_results([
244            GOOD_JSON_TEST_RESULT_0,
245            GOOD_JSON_TEST_RESULT_1,
246            GOOD_JSON_TEST_RESULT_2,
247        ]), GOOD_JSON_TEST_RESULT_MERGED)
248
249  def test_merge_json_test_results_optional_matches(self):
250    self.assertEqual(
251        results_merger.merge_test_results([
252            extend(GOOD_JSON_TEST_RESULT_0, {'path_delimiter': '.'}),
253            extend(GOOD_JSON_TEST_RESULT_1, {'path_delimiter': '.'}),
254            extend(GOOD_JSON_TEST_RESULT_2, {'path_delimiter': '.'}),
255        ]), extend(GOOD_JSON_TEST_RESULT_MERGED, {'path_delimiter': '.'}))
256
257  def test_merge_json_test_results_optional_differs(self):
258    with self.assertRaises(results_merger.MergeException):
259      results_merger.merge_test_results([
260          extend(GOOD_JSON_TEST_RESULT_0, {'path_delimiter': '.'}),
261          extend(GOOD_JSON_TEST_RESULT_1, {'path_delimiter': '.'}),
262          extend(GOOD_JSON_TEST_RESULT_2, {'path_delimiter': '/'}),
263      ])
264
265  def test_merge_json_test_results_optional_count(self):
266    self.assertEqual(
267        results_merger.merge_test_results([
268            extend(GOOD_JSON_TEST_RESULT_0, {'fixable': 1}),
269            extend(GOOD_JSON_TEST_RESULT_1, {'fixable': 2}),
270            extend(GOOD_JSON_TEST_RESULT_2, {'fixable': 3}),
271        ]), extend(GOOD_JSON_TEST_RESULT_MERGED, {'fixable': 6}))
272
273  def test_merge_nothing(self):
274    self.assertEqual(results_merger.merge_test_results([]), {})
275
276
277# TODO(tansell): Make this test fail properly, currently fails with an
278# AttributeError.
279#  def test_merge_test_name_conflict(self):
280#    self.maxDiff = None  # Show full diff if assertion fail
281#    with self.assertRaises(results_merger.MergeException):
282#      results_merger.merge_test_results(
283#        [GOOD_JSON_TEST_RESULT_0, GOOD_JSON_TEST_RESULT_0])
284
285if __name__ == '__main__':
286  unittest.main()  # pragma: no cover
287