blob: 446c43fcf020695c3d91a34dce750b495b8efaab [file] [log] [blame]
#!/usr/bin/python
# 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.
"""Unittests for test_runner.py."""
import collections
import glob
import os
import subprocess
import unittest
import test_runner
class TestCase(unittest.TestCase):
"""Test case which supports installing mocks. Uninstalls on tear down."""
def __init__(self, *args, **kwargs):
"""Initializes a new instance of this class."""
super(TestCase, self).__init__(*args, **kwargs)
# Maps object to a dict which maps names of mocked members to their
# original values.
self._mocks = collections.OrderedDict()
def mock(self, obj, member, mock):
"""Installs mock in place of the named member of the given obj.
Args:
obj: Any object.
member: String naming the attribute of the object to mock.
mock: The mock to install.
"""
self._mocks.setdefault(obj, collections.OrderedDict()).setdefault(
member, getattr(obj, member))
setattr(obj, member, mock)
def tearDown(self, *args, **kwargs):
"""Uninstalls mocks."""
super(TestCase, self).tearDown(*args, **kwargs)
for obj in self._mocks:
for member, original_value in self._mocks[obj].iteritems():
setattr(obj, member, original_value)
class GetKIFTestFilterTest(TestCase):
"""Tests for test_runner.get_kif_test_filter."""
def test_correct(self):
"""Ensures correctness of filter."""
tests = [
'KIF.test1',
'KIF.test2',
]
expected = 'NAME:test1|test2'
self.assertEqual(test_runner.get_kif_test_filter(tests), expected)
def test_correct_inverted(self):
"""Ensures correctness of inverted filter."""
tests = [
'KIF.test1',
'KIF.test2',
]
expected = '-NAME:test1|test2'
self.assertEqual(
test_runner.get_kif_test_filter(tests, invert=True), expected)
class GetGTestFilterTest(TestCase):
"""Tests for test_runner.get_gtest_filter."""
def test_correct(self):
"""Ensures correctness of filter."""
tests = [
'test.1',
'test.2',
]
expected = 'test.1:test.2'
self.assertEqual(test_runner.get_gtest_filter(tests), expected)
def test_correct_inverted(self):
"""Ensures correctness of inverted filter."""
tests = [
'test.1',
'test.2',
]
expected = '-test.1:test.2'
self.assertEqual(
test_runner.get_gtest_filter(tests, invert=True), expected)
class InstallXcodeTest(TestCase):
"""Tests install_xcode."""
def setUp(self):
super(InstallXcodeTest, self).setUp()
self.mock(test_runner, 'xcode_select', lambda _: None)
self.mock(os.path, 'exists', lambda _: True)
def test_success(self):
self.assertTrue(test_runner.install_xcode('test_build', 'true', 'path'))
def test_failure(self):
self.assertFalse(test_runner.install_xcode('test_build', 'false', 'path'))
class SimulatorTestRunnerTest(TestCase):
"""Tests for test_runner.SimulatorTestRunner."""
def setUp(self):
super(SimulatorTestRunnerTest, self).setUp()
def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
return True
self.mock(test_runner, 'get_current_xcode_info', lambda: {
'version': 'test version', 'build': 'test build', 'path': 'test/path'})
self.mock(test_runner, 'install_xcode', install_xcode)
self.mock(test_runner.subprocess, 'check_output',
lambda _: 'fake-bundle-id')
self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
self.mock(os.path, 'exists', lambda _: True)
def test_app_not_found(self):
"""Ensures AppNotFoundError is raised."""
self.mock(os.path, 'exists', lambda p: not p.endswith('fake-app'))
with self.assertRaises(test_runner.AppNotFoundError):
test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'', # Empty xcode-build
'out-dir',
)
def test_iossim_not_found(self):
"""Ensures SimulatorNotFoundError is raised."""
self.mock(os.path, 'exists', lambda p: not p.endswith('fake-iossim'))
with self.assertRaises(test_runner.SimulatorNotFoundError):
test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
def test_init(self):
"""Ensures instance is created."""
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
self.assertTrue(tr)
def test_startup_crash(self):
"""Ensures test is relaunched once on startup crash."""
def set_up(self):
return
@staticmethod
def _run(cmd, shards=None):
return collections.namedtuple('result', ['crashed', 'crashed_test'])(
crashed=True, crashed_test=None)
def tear_down(self):
return
self.mock(test_runner.SimulatorTestRunner, 'set_up', set_up)
self.mock(test_runner.TestRunner, '_run', _run)
self.mock(test_runner.SimulatorTestRunner, 'tear_down', tear_down)
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
with self.assertRaises(test_runner.AppLaunchError):
tr.launch()
def test_run(self):
"""Ensures the _run method is correct with test sharding."""
def shard_xctest(object_path, shards, test_cases=None):
return [['a/1', 'b/2'], ['c/3', 'd/4'], ['e/5']]
def run_tests(self, test_shard=None):
out = []
for test in test_shard:
testname = test.split('/')
out.append('Test Case \'-[%s %s]\' started.' %
(testname[0], testname[1]))
out.append('Test Case \'-[%s %s]\' passed (0.1 seconds)' %
(testname[0], testname[1]))
return (out, 0, 0)
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
self.mock(test_runner, 'shard_xctest', shard_xctest)
self.mock(test_runner.SimulatorTestRunner, 'run_tests', run_tests)
tr.xctest_path = 'fake.xctest'
cmd = tr.get_launch_command()
result = tr._run(cmd=cmd, shards=3)
self.assertIn('a/1', result.passed_tests)
self.assertIn('b/2', result.passed_tests)
self.assertIn('c/3', result.passed_tests)
self.assertIn('d/4', result.passed_tests)
self.assertIn('e/5', result.passed_tests)
def test_run_with_system_alert(self):
"""Ensures SystemAlertPresentError is raised when warning 'System alert
view is present, so skipping all tests' is in the output."""
with self.assertRaises(test_runner.SystemAlertPresentError):
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
tr.xctest_path = 'fake.xctest'
cmd = ['echo', 'System alert view is present, so skipping all tests!']
result = tr._run(cmd=cmd)
def test_get_launch_command(self):
"""Ensures launch command is correct with test_filters, test sharding and
test_cases."""
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
tr.xctest_path = 'fake.xctest'
# Cases test_filter is not empty, with empty/non-empty self.test_cases.
tr.test_cases = []
cmd = tr.get_launch_command(['a'], invert=False)
self.assertIn('-t', cmd)
self.assertIn('a', cmd)
tr.test_cases = ['a', 'b']
cmd = tr.get_launch_command(['a'], invert=False)
self.assertIn('-t', cmd)
self.assertIn('a', cmd)
self.assertNotIn('b', cmd)
# Cases test_filter is empty, with empty/non-empty self.test_cases.
tr.test_cases = []
cmd = tr.get_launch_command(test_filter=None, invert=False)
self.assertNotIn('-t', cmd)
tr.test_cases = ['a', 'b']
cmd = tr.get_launch_command(test_filter=None, invert=False)
self.assertIn('-t', cmd)
self.assertIn('a', cmd)
self.assertIn('b', cmd)
def test_relaunch(self):
"""Ensures test is relaunched on test crash until tests complete."""
def set_up(self):
return
@staticmethod
def _run(cmd, shards=None):
result = collections.namedtuple(
'result', [
'crashed',
'crashed_test',
'failed_tests',
'flaked_tests',
'passed_tests',
],
)
if '-e' not in cmd:
# First run, has no test filter supplied. Mock a crash.
return result(
crashed=True,
crashed_test='c',
failed_tests={'b': ['b-out'], 'c': ['Did not complete.']},
flaked_tests={'d': ['d-out']},
passed_tests=['a'],
)
else:
return result(
crashed=False,
crashed_test=None,
failed_tests={},
flaked_tests={},
passed_tests=[],
)
def tear_down(self):
return
self.mock(test_runner.SimulatorTestRunner, 'set_up', set_up)
self.mock(test_runner.TestRunner, '_run', _run)
self.mock(test_runner.SimulatorTestRunner, 'tear_down', tear_down)
tr = test_runner.SimulatorTestRunner(
'fake-app',
'fake-iossim',
'platform',
'os',
'xcode-version',
'xcode-build',
'out-dir',
)
tr.launch()
self.assertTrue(tr.logs)
class WprProxySimulatorTestRunnerTest(TestCase):
"""Tests for test_runner.WprProxySimulatorTestRunner."""
def setUp(self):
super(WprProxySimulatorTestRunnerTest, self).setUp()
def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
return True
self.mock(test_runner, 'get_current_xcode_info', lambda: {
'version': 'test version', 'build': 'test build', 'path': 'test/path'})
self.mock(test_runner, 'install_xcode', install_xcode)
self.mock(test_runner.subprocess, 'check_output',
lambda _: 'fake-bundle-id')
self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
self.mock(os.path, 'exists', lambda _: True)
self.mock(test_runner.SimulatorTestRunner, 'getSimulator', lambda _: 'fake-id')
self.mock(test_runner.SimulatorTestRunner, 'deleteSimulator', lambda a, b: True)
self.mock(test_runner.WprProxySimulatorTestRunner, 'copy_trusted_certificate', lambda a, b: True)
def test_replay_path_not_found(self):
"""Ensures ReplayPathNotFoundError is raised."""
self.mock(os.path, 'exists', lambda p: not p.endswith('bad-replay-path'))
with self.assertRaises(test_runner.ReplayPathNotFoundError):
test_runner.WprProxySimulatorTestRunner(
'fake-app',
'fake-iossim',
'bad-replay-path',
'platform',
'os',
'wpr-tools-path',
'xcode-version',
'xcode-build',
'out-dir',
)
def test_wpr_tools_not_found(self):
"""Ensures WprToolsNotFoundError is raised."""
self.mock(os.path, 'exists', lambda p: not p.endswith('bad-tools-path'))
with self.assertRaises(test_runner.WprToolsNotFoundError):
test_runner.WprProxySimulatorTestRunner(
'fake-app',
'fake-iossim',
'replay-path',
'platform',
'os',
'bad-tools-path',
'xcode-version',
'xcode-build',
'out-dir',
)
def test_init(self):
"""Ensures instance is created."""
tr = test_runner.WprProxySimulatorTestRunner(
'fake-app',
'fake-iossim',
'replay-path',
'platform',
'os',
'wpr-tools-path',
'xcode-version',
'xcode-build',
'out-dir',
)
self.assertTrue(tr)
def run_wpr_test(self, test_filter=[], invert=False):
"""Wrapper that mocks the _run method and returns its result."""
class FakeStdout:
def __init__(self):
self.line_index = 0
self.lines = [
'Test Case \'-[a 1]\' started.',
'Test Case \'-[a 1]\' has uninteresting logs.',
'Test Case \'-[a 1]\' passed (0.1 seconds)',
'Test Case \'-[b 2]\' started.',
'Test Case \'-[b 2]\' passed (0.1 seconds)',
'Test Case \'-[c 3]\' started.',
'Test Case \'-[c 3]\' has interesting failure info.',
'Test Case \'-[c 3]\' failed (0.1 seconds)',
]
def readline(self):
if self.line_index < len(self.lines):
return_line = self.lines[self.line_index]
self.line_index += 1
return return_line
else:
return None
class FakeProcess:
def __init__(self):
self.stdout = FakeStdout()
self.returncode = 0
def stdout(self):
return self.stdout
def wait(self):
return
def popen(recipe_cmd, env, stdout, stderr):
return FakeProcess()
tr = test_runner.WprProxySimulatorTestRunner(
'fake-app',
'fake-iossim',
'replay-path',
'platform',
'os',
'wpr-tools-path',
'xcode-version',
'xcode-build',
'out-dir',
)
self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_start', lambda a,b: None)
self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_stop', lambda _: None)
self.mock(os.path, 'isfile', lambda _: True)
self.mock(glob, 'glob', lambda _: ["file1", "file2"])
self.mock(subprocess, 'Popen', popen)
tr.xctest_path = 'fake.xctest'
cmd = tr.get_launch_command(test_filter=test_filter, invert=invert)
return tr._run(cmd=cmd, shards=1)
def test_run_no_filter(self):
"""Ensures the _run method can handle passed and failed tests."""
result = self.run_wpr_test()
self.assertIn('file1.a/1', result.passed_tests)
self.assertIn('file1.b/2', result.passed_tests)
self.assertIn('file1.c/3', result.failed_tests)
self.assertIn('file2.a/1', result.passed_tests)
self.assertIn('file2.b/2', result.passed_tests)
self.assertIn('file2.c/3', result.failed_tests)
def test_run_with_filter(self):
"""Ensures the _run method works with a filter."""
result = self.run_wpr_test(test_filter=["file1"], invert=False)
self.assertIn('file1.a/1', result.passed_tests)
self.assertIn('file1.b/2', result.passed_tests)
self.assertIn('file1.c/3', result.failed_tests)
self.assertNotIn('file2.a/1', result.passed_tests)
self.assertNotIn('file2.b/2', result.passed_tests)
self.assertNotIn('file2.c/3', result.failed_tests)
def test_run_with_inverted_filter(self):
"""Ensures the _run method works with an inverted filter."""
result = self.run_wpr_test(test_filter=["file1"], invert=True)
self.assertNotIn('file1.a/1', result.passed_tests)
self.assertNotIn('file1.b/2', result.passed_tests)
self.assertNotIn('file1.c/3', result.failed_tests)
self.assertIn('file2.a/1', result.passed_tests)
self.assertIn('file2.b/2', result.passed_tests)
self.assertIn('file2.c/3', result.failed_tests)
class DeviceTestRunnerTest(TestCase):
def setUp(self):
super(DeviceTestRunnerTest, self).setUp()
def install_xcode(build, mac_toolchain_cmd, xcode_app_path):
return True
self.mock(test_runner, 'get_current_xcode_info', lambda: {
'version': 'test version', 'build': 'test build', 'path': 'test/path'})
self.mock(test_runner, 'install_xcode', install_xcode)
self.mock(test_runner.subprocess, 'check_output',
lambda _: 'fake-bundle-id')
self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
self.mock(os.path, 'exists', lambda _: True)
self.tr = test_runner.DeviceTestRunner(
'fake-app',
'xcode-version',
'xcode-build',
'out-dir',
)
self.tr.xctestrun_data = {'TestTargetName':{}}
def test_with_test_filter_without_test_cases(self):
"""Ensures tests in the run with test_filter and no test_cases."""
self.tr.set_xctest_filters(['a', 'b'], invert=False)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
['a', 'b']
)
def test_invert_with_test_filter_without_test_cases(self):
"""Ensures tests in the run invert with test_filter and no test_cases."""
self.tr.set_xctest_filters(['a', 'b'], invert=True)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['SkipTestIdentifiers'],
['a', 'b']
)
def test_with_test_filter_with_test_cases(self):
"""Ensures tests in the run with test_filter and test_cases."""
self.tr.test_cases = ['a', 'b', 'c', 'd']
self.tr.set_xctest_filters(['a', 'b', 'irrelevant test'], invert=False)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
['a', 'b']
)
def test_invert_with_test_filter_with_test_cases(self):
"""Ensures tests in the run invert with test_filter and test_cases."""
self.tr.test_cases = ['a', 'b', 'c', 'd']
self.tr.set_xctest_filters(['a', 'b', 'irrelevant test'], invert=True)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
['c', 'd']
)
def test_without_test_filter_without_test_cases(self):
"""Ensures tests in the run with no test_filter and no test_cases."""
self.tr.set_xctest_filters(test_filter=None, invert=False)
self.assertIsNone(
self.tr.xctestrun_data['TestTargetName'].get('OnlyTestIdentifiers'))
def test_invert_without_test_filter_without_test_cases(self):
"""Ensures tests in the run invert with no test_filter and no test_cases."""
self.tr.set_xctest_filters(test_filter=None, invert=True)
self.assertIsNone(
self.tr.xctestrun_data['TestTargetName'].get('OnlyTestIdentifiers'))
def test_without_test_filter_with_test_cases(self):
"""Ensures tests in the run with no test_filter but test_cases."""
self.tr.test_cases = ['a', 'b', 'c', 'd']
self.tr.set_xctest_filters(test_filter=None, invert=False)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
['a', 'b', 'c', 'd']
)
def test_invert_without_test_filter_with_test_cases(self):
"""Ensures tests in the run invert with no test_filter but test_cases."""
self.tr.test_cases = ['a', 'b', 'c', 'd']
self.tr.set_xctest_filters(test_filter=None, invert=True)
self.assertEqual(
self.tr.xctestrun_data['TestTargetName']['OnlyTestIdentifiers'],
['a', 'b', 'c', 'd']
)
if __name__ == '__main__':
unittest.main()