blob: 9619e20e82d79098223ff52409ddf17b9a416b18 [file] [log] [blame]
smut64cb9422016-05-07 00:24:201#!/usr/bin/python
2# Copyright 2016 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Unittests for test_runner.py."""
7
8import collections
Eric Aleshire97b368c92018-10-12 18:09:099import glob
smut64cb9422016-05-07 00:24:2010import json
11import os
Eric Aleshire97b368c92018-10-12 18:09:0912import subprocess
smut64cb9422016-05-07 00:24:2013import sys
14import unittest
15
16import test_runner
17
18
19class TestCase(unittest.TestCase):
20 """Test case which supports installing mocks. Uninstalls on tear down."""
21
22 def __init__(self, *args, **kwargs):
23 """Initializes a new instance of this class."""
24 super(TestCase, self).__init__(*args, **kwargs)
25
26 # Maps object to a dict which maps names of mocked members to their
27 # original values.
28 self._mocks = collections.OrderedDict()
29
30 def mock(self, obj, member, mock):
31 """Installs mock in place of the named member of the given obj.
32
33 Args:
34 obj: Any object.
35 member: String naming the attribute of the object to mock.
36 mock: The mock to install.
37 """
38 self._mocks.setdefault(obj, collections.OrderedDict()).setdefault(
39 member, getattr(obj, member))
40 setattr(obj, member, mock)
41
42 def tearDown(self, *args, **kwargs):
43 """Uninstalls mocks."""
44 super(TestCase, self).tearDown(*args, **kwargs)
45
46 for obj in self._mocks:
47 for member, original_value in self._mocks[obj].iteritems():
48 setattr(obj, member, original_value)
49
50
51class GetKIFTestFilterTest(TestCase):
52 """Tests for test_runner.get_kif_test_filter."""
53
54 def test_correct(self):
55 """Ensures correctness of filter."""
56 tests = [
57 'KIF.test1',
58 'KIF.test2',
59 ]
60 expected = 'NAME:test1|test2'
61
62 self.assertEqual(test_runner.get_kif_test_filter(tests), expected)
63
64 def test_correct_inverted(self):
65 """Ensures correctness of inverted filter."""
66 tests = [
67 'KIF.test1',
68 'KIF.test2',
69 ]
70 expected = '-NAME:test1|test2'
71
72 self.assertEqual(
73 test_runner.get_kif_test_filter(tests, invert=True), expected)
74
75
76class GetGTestFilterTest(TestCase):
77 """Tests for test_runner.get_gtest_filter."""
78
79 def test_correct(self):
80 """Ensures correctness of filter."""
81 tests = [
82 'test.1',
83 'test.2',
84 ]
85 expected = 'test.1:test.2'
86
87 self.assertEqual(test_runner.get_gtest_filter(tests), expected)
88
89 def test_correct_inverted(self):
90 """Ensures correctness of inverted filter."""
91 tests = [
92 'test.1',
93 'test.2',
94 ]
95 expected = '-test.1:test.2'
96
97 self.assertEqual(
98 test_runner.get_gtest_filter(tests, invert=True), expected)
99
100
Sergey Berezin1f457882017-11-20 21:05:39101class InstallXcodeTest(TestCase):
102 """Tests install_xcode."""
103
104 def setUp(self):
105 super(InstallXcodeTest, self).setUp()
106 self.mock(test_runner, 'xcode_select', lambda _: None)
107 self.mock(os.path, 'exists', lambda _: True)
108
109 def test_success(self):
110 self.assertTrue(test_runner.install_xcode('test_build', 'true', 'path'))
111
112 def test_failure(self):
113 self.assertFalse(test_runner.install_xcode('test_build', 'false', 'path'))
114
115
smut64cb9422016-05-07 00:24:20116class SimulatorTestRunnerTest(TestCase):
117 """Tests for test_runner.SimulatorTestRunner."""
118
Sergey Berezin1f457882017-11-20 21:05:39119 def setUp(self):
120 super(SimulatorTestRunnerTest, self).setUp()
121
122 def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
smut64cb9422016-05-07 00:24:20123 return True
124
Sergey Berezin7f9b4bf2018-05-21 19:49:07125 self.mock(test_runner, 'get_current_xcode_info', lambda: {
Sergey Berezin8263e5f2017-11-29 22:51:36126 'version': 'test version', 'build': 'test build', 'path': 'test/path'})
Sergey Berezin1f457882017-11-20 21:05:39127 self.mock(test_runner, 'install_xcode', install_xcode)
128 self.mock(test_runner.subprocess, 'check_output',
129 lambda _: 'fake-bundle-id')
130 self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
131 self.mock(os.path, 'exists', lambda _: True)
smut64cb9422016-05-07 00:24:20132
Sergey Berezin1f457882017-11-20 21:05:39133 def test_app_not_found(self):
134 """Ensures AppNotFoundError is raised."""
smut64cb9422016-05-07 00:24:20135
Sergey Berezin1f457882017-11-20 21:05:39136 self.mock(os.path, 'exists', lambda p: not p.endswith('fake-app'))
smut64cb9422016-05-07 00:24:20137
Sergey Berezin1f457882017-11-20 21:05:39138 with self.assertRaises(test_runner.AppNotFoundError):
139 test_runner.SimulatorTestRunner(
smut64cb9422016-05-07 00:24:20140 'fake-app',
141 'fake-iossim',
142 'platform',
143 'os',
144 'xcode-version',
Sergey Berezin1f457882017-11-20 21:05:39145 '', # Empty xcode-build
smut64cb9422016-05-07 00:24:20146 'out-dir',
Sergey Berezin1f457882017-11-20 21:05:39147 )
smut64cb9422016-05-07 00:24:20148
149 def test_iossim_not_found(self):
150 """Ensures SimulatorNotFoundError is raised."""
Sergey Berezin1f457882017-11-20 21:05:39151 self.mock(os.path, 'exists', lambda p: not p.endswith('fake-iossim'))
smut64cb9422016-05-07 00:24:20152
Sergey Berezin1f457882017-11-20 21:05:39153 with self.assertRaises(test_runner.SimulatorNotFoundError):
154 test_runner.SimulatorTestRunner(
smut64cb9422016-05-07 00:24:20155 'fake-app',
156 'fake-iossim',
157 'platform',
158 'os',
159 'xcode-version',
Sergey Berezin1f457882017-11-20 21:05:39160 'xcode-build',
smut64cb9422016-05-07 00:24:20161 'out-dir',
Sergey Berezin1f457882017-11-20 21:05:39162 )
smut64cb9422016-05-07 00:24:20163
164 def test_init(self):
165 """Ensures instance is created."""
smut64cb9422016-05-07 00:24:20166 tr = test_runner.SimulatorTestRunner(
167 'fake-app',
168 'fake-iossim',
169 'platform',
170 'os',
171 'xcode-version',
Sergey Berezin1f457882017-11-20 21:05:39172 'xcode-build',
smut64cb9422016-05-07 00:24:20173 'out-dir',
174 )
175
Sergey Berezin1f457882017-11-20 21:05:39176 self.assertTrue(tr)
smut64cb9422016-05-07 00:24:20177
178 def test_startup_crash(self):
179 """Ensures test is relaunched once on startup crash."""
smut64cb9422016-05-07 00:24:20180 def set_up(self):
181 return
182
183 @staticmethod
Menglu Huang89909a8e2018-02-21 00:06:42184 def _run(cmd, shards=None):
smut64cb9422016-05-07 00:24:20185 return collections.namedtuple('result', ['crashed', 'crashed_test'])(
186 crashed=True, crashed_test=None)
187
188 def tear_down(self):
189 return
190
smut64cb9422016-05-07 00:24:20191 self.mock(test_runner.SimulatorTestRunner, 'set_up', set_up)
192 self.mock(test_runner.TestRunner, '_run', _run)
193 self.mock(test_runner.SimulatorTestRunner, 'tear_down', tear_down)
194
195 tr = test_runner.SimulatorTestRunner(
196 'fake-app',
197 'fake-iossim',
198 'platform',
199 'os',
200 'xcode-version',
Sergey Berezin1f457882017-11-20 21:05:39201 'xcode-build',
smut64cb9422016-05-07 00:24:20202 'out-dir',
203 )
Sergey Berezin1f457882017-11-20 21:05:39204 with self.assertRaises(test_runner.AppLaunchError):
205 tr.launch()
smut64cb9422016-05-07 00:24:20206
Menglu Huang89909a8e2018-02-21 00:06:42207 def test_run(self):
208 """Ensures the _run method is correct with test sharding."""
209 def shard_xctest(object_path, shards, test_cases=None):
210 return [['a/1', 'b/2'], ['c/3', 'd/4'], ['e/5']]
211
212 def run_tests(self, test_shard=None):
213 out = []
214 for test in test_shard:
215 testname = test.split('/')
216 out.append('Test Case \'-[%s %s]\' started.' %
217 (testname[0], testname[1]))
218 out.append('Test Case \'-[%s %s]\' passed (0.1 seconds)' %
219 (testname[0], testname[1]))
220 return (out, 0, 0)
221
222 tr = test_runner.SimulatorTestRunner(
223 'fake-app',
224 'fake-iossim',
225 'platform',
226 'os',
227 'xcode-version',
228 'xcode-build',
229 'out-dir',
230 )
231 self.mock(test_runner, 'shard_xctest', shard_xctest)
232 self.mock(test_runner.SimulatorTestRunner, 'run_tests', run_tests)
233
234 tr.xctest_path = 'fake.xctest'
235 cmd = tr.get_launch_command()
236 result = tr._run(cmd=cmd, shards=3)
237 self.assertIn('a/1', result.passed_tests)
238 self.assertIn('b/2', result.passed_tests)
239 self.assertIn('c/3', result.passed_tests)
240 self.assertIn('d/4', result.passed_tests)
241 self.assertIn('e/5', result.passed_tests)
242
Menglu Huangeb4d7572018-05-21 18:37:58243 def test_run_with_system_alert(self):
244 """Ensures SystemAlertPresentError is raised when warning 'System alert
245 view is present, so skipping all tests' is in the output."""
246 with self.assertRaises(test_runner.SystemAlertPresentError):
247 tr = test_runner.SimulatorTestRunner(
248 'fake-app',
249 'fake-iossim',
250 'platform',
251 'os',
252 'xcode-version',
253 'xcode-build',
254 'out-dir',
255 )
256 tr.xctest_path = 'fake.xctest'
257 cmd = ['echo', 'System alert view is present, so skipping all tests!']
258 result = tr._run(cmd=cmd)
259
Menglu Huang0f569612017-12-08 06:35:07260 def test_get_launch_command(self):
Menglu Huang89909a8e2018-02-21 00:06:42261 """Ensures launch command is correct with test_filters, test sharding and
262 test_cases."""
Menglu Huang0f569612017-12-08 06:35:07263 tr = test_runner.SimulatorTestRunner(
264 'fake-app',
265 'fake-iossim',
266 'platform',
267 'os',
268 'xcode-version',
269 'xcode-build',
270 'out-dir',
271 )
272 tr.xctest_path = 'fake.xctest'
273 # Cases test_filter is not empty, with empty/non-empty self.test_cases.
274 tr.test_cases = []
275 cmd = tr.get_launch_command(['a'], invert=False)
276 self.assertIn('-t', cmd)
277 self.assertIn('a', cmd)
278
279 tr.test_cases = ['a', 'b']
280 cmd = tr.get_launch_command(['a'], invert=False)
281 self.assertIn('-t', cmd)
282 self.assertIn('a', cmd)
283 self.assertNotIn('b', cmd)
284
285 # Cases test_filter is empty, with empty/non-empty self.test_cases.
286 tr.test_cases = []
287 cmd = tr.get_launch_command(test_filter=None, invert=False)
288 self.assertNotIn('-t', cmd)
289
290 tr.test_cases = ['a', 'b']
291 cmd = tr.get_launch_command(test_filter=None, invert=False)
292 self.assertIn('-t', cmd)
293 self.assertIn('a', cmd)
294 self.assertIn('b', cmd)
295
smut64cb9422016-05-07 00:24:20296 def test_relaunch(self):
297 """Ensures test is relaunched on test crash until tests complete."""
smut64cb9422016-05-07 00:24:20298 def set_up(self):
299 return
300
301 @staticmethod
Menglu Huang89909a8e2018-02-21 00:06:42302 def _run(cmd, shards=None):
smut64cb9422016-05-07 00:24:20303 result = collections.namedtuple(
304 'result', [
305 'crashed',
306 'crashed_test',
307 'failed_tests',
308 'flaked_tests',
309 'passed_tests',
310 ],
311 )
Menglu Huang89909a8e2018-02-21 00:06:42312 if '-e' not in cmd:
smut64cb9422016-05-07 00:24:20313 # First run, has no test filter supplied. Mock a crash.
314 return result(
315 crashed=True,
316 crashed_test='c',
317 failed_tests={'b': ['b-out'], 'c': ['Did not complete.']},
318 flaked_tests={'d': ['d-out']},
319 passed_tests=['a'],
320 )
321 else:
322 return result(
323 crashed=False,
324 crashed_test=None,
325 failed_tests={},
326 flaked_tests={},
327 passed_tests=[],
328 )
329
330 def tear_down(self):
331 return
332
smut64cb9422016-05-07 00:24:20333 self.mock(test_runner.SimulatorTestRunner, 'set_up', set_up)
334 self.mock(test_runner.TestRunner, '_run', _run)
335 self.mock(test_runner.SimulatorTestRunner, 'tear_down', tear_down)
336
337 tr = test_runner.SimulatorTestRunner(
338 'fake-app',
339 'fake-iossim',
340 'platform',
341 'os',
342 'xcode-version',
Sergey Berezin1f457882017-11-20 21:05:39343 'xcode-build',
smut64cb9422016-05-07 00:24:20344 'out-dir',
345 )
346 tr.launch()
Sergey Berezin1f457882017-11-20 21:05:39347 self.assertTrue(tr.logs)
smut64cb9422016-05-07 00:24:20348
349
Eric Aleshire97b368c92018-10-12 18:09:09350class WprProxySimulatorTestRunnerTest(TestCase):
351 """Tests for test_runner.WprProxySimulatorTestRunner."""
352
353 def setUp(self):
354 super(WprProxySimulatorTestRunnerTest, self).setUp()
355
356 def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
357 return True
358
359 self.mock(test_runner, 'get_current_xcode_info', lambda: {
360 'version': 'test version', 'build': 'test build', 'path': 'test/path'})
361 self.mock(test_runner, 'install_xcode', install_xcode)
362 self.mock(test_runner.subprocess, 'check_output',
363 lambda _: 'fake-bundle-id')
364 self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
365 self.mock(os.path, 'exists', lambda _: True)
366
367 def test_replay_path_not_found(self):
368 """Ensures ReplayPathNotFoundError is raised."""
369
370 self.mock(os.path, 'exists', lambda p: not p.endswith('bad-replay-path'))
371
372 with self.assertRaises(test_runner.ReplayPathNotFoundError):
373 test_runner.WprProxySimulatorTestRunner(
374 'fake-app',
375 'fake-iossim',
376 'bad-replay-path',
377 'platform',
378 'os',
379 'wpr-tools-path',
380 'xcode-version',
381 'xcode-build',
382 'out-dir',
383 )
384
385 def test_wpr_tools_not_found(self):
386 """Ensures WprToolsNotFoundError is raised."""
387
388 self.mock(os.path, 'exists', lambda p: not p.endswith('bad-tools-path'))
389
390 with self.assertRaises(test_runner.WprToolsNotFoundError):
391 test_runner.WprProxySimulatorTestRunner(
392 'fake-app',
393 'fake-iossim',
394 'replay-path',
395 'platform',
396 'os',
397 'bad-tools-path',
398 'xcode-version',
399 'xcode-build',
400 'out-dir',
401 )
402
403 def test_init(self):
404 """Ensures instance is created."""
405 tr = test_runner.WprProxySimulatorTestRunner(
406 'fake-app',
407 'fake-iossim',
408 'replay-path',
409 'platform',
410 'os',
411 'wpr-tools-path',
412 'xcode-version',
413 'xcode-build',
414 'out-dir',
415 )
416
417 self.assertTrue(tr)
418
419 def test_run(self):
420 """Ensures the _run method can handle passed and failed tests."""
421
422 class FakeStdout:
423 def __init__(self):
424 self.line_index = 0
425 self.lines = [
426 'Test Case \'-[a 1]\' started.',
427 'Test Case \'-[a 1]\' has uninteresting logs.',
428 'Test Case \'-[a 1]\' passed (0.1 seconds)',
429 'Test Case \'-[b 2]\' started.',
430 'Test Case \'-[b 2]\' passed (0.1 seconds)',
431 'Test Case \'-[c 3]\' started.',
432 'Test Case \'-[c 3]\' has interesting failure info.',
433 'Test Case \'-[c 3]\' failed (0.1 seconds)',
434 ]
435
436 def readline(self):
437 if self.line_index < len(self.lines):
438 return_line = self.lines[self.line_index]
439 self.line_index += 1
440 return return_line
441 else:
442 return None
443
444 class FakeProcess:
445 def __init__(self):
446 self.stdout = FakeStdout()
447 self.returncode = 0
448
449 def stdout(self):
450 return self.stdout
451
452 def wait(self):
453 return
454
455 def popen(recipe_cmd, env, stdout, stderr):
456 return FakeProcess()
457
458 tr = test_runner.WprProxySimulatorTestRunner(
459 'fake-app',
460 'fake-iossim',
461 'replay-path',
462 'platform',
463 'os',
464 'wpr-tools-path',
465 'xcode-version',
466 'xcode-build',
467 'out-dir',
468 )
469 self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_start', lambda a,b: None)
470 self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_stop', lambda _: None)
471
472 self.mock(os.path, 'isfile', lambda _: True)
473 self.mock(glob, 'glob', lambda _: ["file1", "file2"])
474 self.mock(subprocess, 'Popen', popen)
475
476 tr.xctest_path = 'fake.xctest'
477 cmd = tr.get_launch_command()
478 result = tr._run(cmd=cmd, shards=1)
479 self.assertIn('file1.a/1', result.passed_tests)
480 self.assertIn('file1.b/2', result.passed_tests)
481 self.assertIn('file1.c/3', result.failed_tests)
482 self.assertIn('file2.a/1', result.passed_tests)
483 self.assertIn('file2.b/2', result.passed_tests)
484 self.assertIn('file2.c/3', result.failed_tests)
485
486
Menglu Huang0f569612017-12-08 06:35:07487class DeviceTestRunnerTest(TestCase):
488 def setUp(self):
489 super(DeviceTestRunnerTest, self).setUp()
490
491 def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
492 return True
493
Sergey Berezin7f9b4bf2018-05-21 19:49:07494 self.mock(test_runner, 'get_current_xcode_info', lambda: {
Menglu Huang0f569612017-12-08 06:35:07495 'version': 'test version', 'build': 'test build', 'path': 'test/path'})
496 self.mock(test_runner, 'install_xcode', install_xcode)
497 self.mock(test_runner.subprocess, 'check_output',
498 lambda _: 'fake-bundle-id')
499 self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
500 self.mock(os.path, 'exists', lambda _: True)
501
502 self.tr = test_runner.DeviceTestRunner(
503 'fake-app',
504 'xcode-version',
505 'xcode-build',
506 'out-dir',
507 )
508 self.tr.xctestrun_data = {'TestTargetName':{}}
509
510 def test_with_test_filter_without_test_cases(self):
511 """Ensures tests in the run with test_filter and no test_cases."""
512 self.tr.set_xctest_filters(['a', 'b'], invert=False)
513 self.assertEqual(
514 self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
515 ['a', 'b']
516 )
517
518 def test_invert_with_test_filter_without_test_cases(self):
519 """Ensures tests in the run invert with test_filter and no test_cases."""
520 self.tr.set_xctest_filters(['a', 'b'], invert=True)
521 self.assertEqual(
522 self.tr.xctestrun_data['TestTargetName']['SkipTestIdentifiers'],
523 ['a', 'b']
524 )
525
526 def test_with_test_filter_with_test_cases(self):
527 """Ensures tests in the run with test_filter and test_cases."""
528 self.tr.test_cases = ['a', 'b', 'c', 'd']
529 self.tr.set_xctest_filters(['a', 'b', 'irrelevant test'], invert=False)
530 self.assertEqual(
531 self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
532 ['a', 'b']
533 )
534
535 def test_invert_with_test_filter_with_test_cases(self):
536 """Ensures tests in the run invert with test_filter and test_cases."""
537 self.tr.test_cases = ['a', 'b', 'c', 'd']
538 self.tr.set_xctest_filters(['a', 'b', 'irrelevant test'], invert=True)
539 self.assertEqual(
540 self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
541 ['c', 'd']
542 )
543
544 def test_without_test_filter_without_test_cases(self):
545 """Ensures tests in the run with no test_filter and no test_cases."""
546 self.tr.set_xctest_filters(test_filter=None, invert=False)
547 self.assertIsNone(
548 self.tr.xctestrun_data['TestTargetName'].get('OnlyTestIdentifiers'))
549
550 def test_invert_without_test_filter_without_test_cases(self):
551 """Ensures tests in the run invert with no test_filter and no test_cases."""
552 self.tr.set_xctest_filters(test_filter=None, invert=True)
553 self.assertIsNone(
554 self.tr.xctestrun_data['TestTargetName'].get('OnlyTestIdentifiers'))
555
556 def test_without_test_filter_with_test_cases(self):
557 """Ensures tests in the run with no test_filter but test_cases."""
558 self.tr.test_cases = ['a', 'b', 'c', 'd']
559 self.tr.set_xctest_filters(test_filter=None, invert=False)
560 self.assertEqual(
561 self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
562 ['a', 'b', 'c', 'd']
563 )
564
565 def test_invert_without_test_filter_with_test_cases(self):
566 """Ensures tests in the run invert with no test_filter but test_cases."""
567 self.tr.test_cases = ['a', 'b', 'c', 'd']
568 self.tr.set_xctest_filters(test_filter=None, invert=True)
569 self.assertEqual(
570 self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
571 ['a', 'b', 'c', 'd']
572 )
573
574
smut64cb9422016-05-07 00:24:20575if __name__ == '__main__':
576 unittest.main()