#!/usr/bin/env vpython3
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import absolute_import
import logging
import os
import tempfile
import sys
import unittest
import mock # pylint: disable=import-error
import run_cts
sys.path.append(os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir, 'build', 'android'))
# pylint: disable=wrong-import-position,import-error
import devil_chromium # pylint: disable=unused-import
from devil.android.ndk import abis
from devil.android.sdk import version_codes
class _RunCtsTest(unittest.TestCase):
"""Unittests for the run_cts module.
"""
_EXCLUDED_TEST = 'bad#test'
# This conforms to schema of the test_run entry in
# cts_config/webview_cts_gcs_path.json
_CTS_RUN = {'apk': 'module.apk', 'excludes': [{'match': _EXCLUDED_TEST}]}
@staticmethod
def _getArgsMock(**kwargs):
args = {
'test_filter_files': None,
'test_filters': None,
'isolated_script_test_filters': None,
'skip_expected_failures': False
}
args.update(kwargs)
return mock.Mock(**args)
def _getSkipString(self):
return self._EXCLUDED_TEST.replace('#', '.')
def testDetermineArch_arm64(self):
logging_mock = mock.Mock()
logging.info = logging_mock
device = mock.Mock(product_cpu_abi=abis.ARM_64)
self.assertEqual(run_cts.DetermineArch(device), 'arm64')
# We should log a message to explain how we auto-determined the arch. We
# don't assert the message itself, since that's rather strict.
logging_mock.assert_called()
def testDetermineArch_unsupported(self):
device = mock.Mock(product_cpu_abi='madeup-abi')
with self.assertRaises(Exception) as _:
run_cts.DetermineArch(device)
def testDetermineCtsRelease_oreo(self):
logging_mock = mock.Mock()
logging.info = logging_mock
device = mock.Mock(build_version_sdk=version_codes.OREO)
self.assertEqual(run_cts.DetermineCtsRelease(device), 'O')
# We should log a message to explain how we auto-determined the CTS release.
# We don't assert the message itself, since that's rather strict.
logging_mock.assert_called()
def testDetermineCtsRelease_tooLow(self):
device = mock.Mock(build_version_sdk=version_codes.NOUGAT_MR1)
with self.assertRaises(Exception) as cm:
run_cts.DetermineCtsRelease(device)
message = str(cm.exception)
self.assertIn("We don't support running CTS tests on platforms less than",
message)
def testDetermineCtsRelease_tooHigh(self):
device = mock.Mock(build_version_sdk=version_codes.OREO)
# Mock this out with a couple version codes to check that the logic is
# correct, without making assumptions about what version_codes we may
# support in the future.
mock_sdk_platform_dict = {
version_codes.MARSHMALLOW: 'min fake release',
version_codes.NOUGAT: 'max fake release',
}
original_sdk_platform_dict = run_cts.SDK_PLATFORM_DICT
run_cts.SDK_PLATFORM_DICT = mock_sdk_platform_dict
with self.assertRaises(Exception) as cm:
run_cts.DetermineCtsRelease(device)
run_cts.SDK_PLATFORM_DICT = original_sdk_platform_dict
message = str(cm.exception)
self.assertIn('--cts-release max fake release', message,
msg='Should recommend the highest supported CTS release')
def testNoFilter_SkipExpectedFailures(self):
mock_args = self._getArgsMock(skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([run_cts.TEST_FILTER_OPT + '=-' + skip],
run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testNoFilter_ExcludedMatches(self):
mock_args = self._getArgsMock(skip_expected_failures=False)
self.assertEqual([],
run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testFilter_CombinesExcludedMatches(self):
mock_args = self._getArgsMock(test_filters=['good#test'],
skip_expected_failures=False)
self.assertEqual([run_cts.TEST_FILTER_OPT + '=good.test'],
run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testFilter_CombinesAll(self):
mock_args = self._getArgsMock(test_filters=['good#test'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=good.test',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testFilter_ForMultipleTests(self):
mock_args = self._getArgsMock(test_filters=['good#t1:good#t2'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=good.t1:good.t2',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testFilter_IncludesForArchitecture(self):
mock_args = self._getArgsMock()
cts_run = {
'apk':
'module.apk',
'includes': [{
'match': 'good#test1',
'arch': 'x86'
}, {
'match': 'good#test2'
}, {
'match': 'exclude#test4',
'arch': 'arm64'
}]
}
self.assertEqual([run_cts.TEST_FILTER_OPT + '=good.test1:good.test2'],
run_cts.GetTestRunFilterArg(mock_args, cts_run,
arch='x86'))
def testFilter_ExcludesForArchitecture(self):
mock_args = self._getArgsMock(skip_expected_failures=True)
cts_run = {
'apk':
'module.apk',
'excludes': [{
'match': 'good#test1',
'arch': 'x86'
}, {
'match': 'good#test2'
}, {
'match': 'exclude#test4',
'arch': 'arm64'
}]
}
self.assertEqual([run_cts.TEST_FILTER_OPT + '=-good.test1:good.test2'],
run_cts.GetTestRunFilterArg(mock_args, cts_run,
arch='x86'))
def testFilter_IncludesForMode(self):
mock_args = self._getArgsMock()
cts_run = {
'apk':
'module.apk',
'includes': [{
'match': 'good#test1',
'mode': 'instant'
}, {
'match': 'good#test2'
}, {
'match': 'exclude#test4',
'mode': 'full'
}]
}
self.assertEqual([run_cts.TEST_FILTER_OPT + '=good.test1:good.test2'],
run_cts.GetTestRunFilterArg(mock_args,
cts_run,
test_app_mode='instant'))
def testFilter_ExcludesForMode(self):
mock_args = self._getArgsMock(skip_expected_failures=True)
cts_run = {
'apk':
'module.apk',
'excludes': [{
'match': 'good#test1',
'mode': 'instant'
}, {
'match': 'good#test2'
}, {
'match': 'exclude#test4',
'mode': 'full'
}]
}
self.assertEqual([run_cts.TEST_FILTER_OPT + '=-good.test1:good.test2'],
run_cts.GetTestRunFilterArg(mock_args,
cts_run,
test_app_mode='instant'))
def testIsolatedFilter_CombinesExcludedMatches(self):
mock_args = self._getArgsMock(isolated_script_test_filters=['good#test'],
skip_expected_failures=False)
self.assertEqual([run_cts.TEST_FILTER_OPT + '=good.test'],
run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testIsolatedFilter_CombinesAll(self):
mock_args = self._getArgsMock(isolated_script_test_filters=['good#test'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=good.test',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testIsolatedFilter_ForMultipleTests(self):
# Isolated test filters use :: to separate matches
mock_args = self._getArgsMock(
isolated_script_test_filters=['good#t1::good#t2'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=good.t1:good.t2',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
@unittest.skipIf(os.name == "nt", "Opening NamedTemporaryFile by name "
"doesn't work in Windows.")
def testFilterFile_CombinesExcludedMatches(self):
with tempfile.NamedTemporaryFile(prefix='cts_run_test') as filter_file:
filter_file.write('suite.goodtest'.encode())
filter_file.seek(0)
mock_args = self._getArgsMock(test_filter_files=[filter_file.name],
skip_expected_failures=False)
self.assertEqual([run_cts.TEST_FILTER_OPT + '=suite.goodtest'],
run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
@unittest.skipIf(os.name == "nt", "Opening NamedTemporaryFile by name "
"doesn't work in Windows.")
def testFilterFile_MultipleFilters(self):
with tempfile.NamedTemporaryFile(prefix='cts_run_test') as filter_file:
filter_file.write(
'suite.goodtest1\nsuite.goodtest2\n-suite.badtest1'.encode())
filter_file.seek(0)
with tempfile.NamedTemporaryFile(prefix='cts_run_test2') as filter_file2:
filter_file2.write(
'suite.goodtest2\nsuite.goodtest3\n-suite.badtest2'.encode())
filter_file2.seek(0)
mock_args = self._getArgsMock(
test_filter_files=[filter_file.name, filter_file2.name],
skip_expected_failures=False)
self.assertEqual([
run_cts.TEST_FILTER_OPT +
'=suite.goodtest1:suite.goodtest2-suite.badtest1',
run_cts.TEST_FILTER_OPT +
'=suite.goodtest2:suite.goodtest3-suite.badtest2'
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
@unittest.skipIf(os.name == "nt", "Opening NamedTemporaryFile by name "
"doesn't work in Windows.")
def testFilterFile_CombinesAll(self):
with tempfile.NamedTemporaryFile(prefix='cts_run_test') as filter_file:
filter_file.write('suite.goodtest'.encode())
filter_file.seek(0)
mock_args = self._getArgsMock(test_filter_files=[filter_file.name],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=suite.goodtest',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testNegative_Filter(self):
mock_args = self._getArgsMock(test_filters=['-good#t1:good#t2'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=-good.t1:good.t2',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testNegative_IsolatedFilter(self):
mock_args = self._getArgsMock(
isolated_script_test_filters=['-good#t1::good#t2'],
skip_expected_failures=True)
skip = self._getSkipString()
self.assertEqual([
run_cts.TEST_FILTER_OPT + '=-good.t1:good.t2',
run_cts.TEST_FILTER_OPT + '=-' + skip
], run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
def testFilter_OverridesInclusion(self):
mock_args = self._getArgsMock(test_filters=['good#test1'],
skip_expected_failures=False)
cts_run = {'apk': 'module.apk', 'includes': [{'match': 'good#test2'}]}
self.assertEqual([run_cts.TEST_FILTER_OPT + '=good.test1'],
run_cts.GetTestRunFilterArg(mock_args, cts_run))
if __name__ == '__main__':
unittest.main()