xref: /aosp_15_r20/tools/asuite/atest/atest_integration_tests.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2018, The Android Open Source Project
4*c2e18aaaSAndroid Build Coastguard Worker#
5*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*c2e18aaaSAndroid Build Coastguard Worker#
9*c2e18aaaSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*c2e18aaaSAndroid Build Coastguard Worker#
11*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License.
16*c2e18aaaSAndroid Build Coastguard Worker
17*c2e18aaaSAndroid Build Coastguard Worker"""ATest Integration Test Class.
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard WorkerThe purpose is to prevent potential side-effects from breaking ATest at the
20*c2e18aaaSAndroid Build Coastguard Workerearly stage while landing CLs with potential side-effects.
21*c2e18aaaSAndroid Build Coastguard Worker
22*c2e18aaaSAndroid Build Coastguard WorkerIt forks a subprocess with ATest commands to validate if it can pass all the
23*c2e18aaaSAndroid Build Coastguard Workerfinding, running logic of the python code, and waiting for TF to exit properly.
24*c2e18aaaSAndroid Build Coastguard Worker    - When running with ROBOLECTRIC tests, it runs without TF, and will exit
25*c2e18aaaSAndroid Build Coastguard Worker    the subprocess with the message "All tests passed"
26*c2e18aaaSAndroid Build Coastguard Worker    - If FAIL, it means something breaks ATest unexpectedly!
27*c2e18aaaSAndroid Build Coastguard Worker"""
28*c2e18aaaSAndroid Build Coastguard Worker
29*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import print_function
30*c2e18aaaSAndroid Build Coastguard Worker
31*c2e18aaaSAndroid Build Coastguard Workerimport os
32*c2e18aaaSAndroid Build Coastguard Workerimport subprocess
33*c2e18aaaSAndroid Build Coastguard Workerimport sys
34*c2e18aaaSAndroid Build Coastguard Workerimport tempfile
35*c2e18aaaSAndroid Build Coastguard Workerimport time
36*c2e18aaaSAndroid Build Coastguard Workerimport unittest
37*c2e18aaaSAndroid Build Coastguard Worker
38*c2e18aaaSAndroid Build Coastguard Worker
39*c2e18aaaSAndroid Build Coastguard Worker_TEST_RUN_DIR_PREFIX = 'atest_integration_tests_%s_'
40*c2e18aaaSAndroid Build Coastguard Worker_LOG_FILE = 'integration_tests.log'
41*c2e18aaaSAndroid Build Coastguard Worker_FAILED_LINE_LIMIT = 50
42*c2e18aaaSAndroid Build Coastguard Worker_EXIT_TEST_FAILED = 1
43*c2e18aaaSAndroid Build Coastguard Worker_ALTERNATIVES = {'-dev'}
44*c2e18aaaSAndroid Build Coastguard Worker_INTEGRATION_TESTS = [
45*c2e18aaaSAndroid Build Coastguard Worker    os.path.join(
46*c2e18aaaSAndroid Build Coastguard Worker        os.environ.get('ANDROID_BUILD_TOP', os.getcwd()),
47*c2e18aaaSAndroid Build Coastguard Worker        'tools/asuite/atest/test_plans/INTEGRATION_TESTS',
48*c2e18aaaSAndroid Build Coastguard Worker    )
49*c2e18aaaSAndroid Build Coastguard Worker]
50*c2e18aaaSAndroid Build Coastguard Worker
51*c2e18aaaSAndroid Build Coastguard Worker
52*c2e18aaaSAndroid Build Coastguard Workerclass ATestIntegrationTest(unittest.TestCase):
53*c2e18aaaSAndroid Build Coastguard Worker  """ATest Integration Test Class."""
54*c2e18aaaSAndroid Build Coastguard Worker
55*c2e18aaaSAndroid Build Coastguard Worker  NAME = 'ATestIntegrationTest'
56*c2e18aaaSAndroid Build Coastguard Worker  EXECUTABLE = 'atest'
57*c2e18aaaSAndroid Build Coastguard Worker  OPTIONS = ''
58*c2e18aaaSAndroid Build Coastguard Worker  _RUN_CMD = '{exe} {options} {test}'
59*c2e18aaaSAndroid Build Coastguard Worker  _PASSED_CRITERIA = ['will be rescheduled', 'All tests passed']
60*c2e18aaaSAndroid Build Coastguard Worker
61*c2e18aaaSAndroid Build Coastguard Worker  def setUp(self):
62*c2e18aaaSAndroid Build Coastguard Worker    """Set up stuff for testing."""
63*c2e18aaaSAndroid Build Coastguard Worker    self.full_env_vars = os.environ.copy()
64*c2e18aaaSAndroid Build Coastguard Worker    self.test_passed = False
65*c2e18aaaSAndroid Build Coastguard Worker    self.log = []
66*c2e18aaaSAndroid Build Coastguard Worker
67*c2e18aaaSAndroid Build Coastguard Worker  def run_test(self, testcase):
68*c2e18aaaSAndroid Build Coastguard Worker    """Create a subprocess to execute the test command.
69*c2e18aaaSAndroid Build Coastguard Worker
70*c2e18aaaSAndroid Build Coastguard Worker    Strategy:
71*c2e18aaaSAndroid Build Coastguard Worker        Fork a subprocess to wait for TF exit properly, and log the error
72*c2e18aaaSAndroid Build Coastguard Worker        if the exit code isn't 0.
73*c2e18aaaSAndroid Build Coastguard Worker
74*c2e18aaaSAndroid Build Coastguard Worker    Args:
75*c2e18aaaSAndroid Build Coastguard Worker        testcase: A string of testcase name.
76*c2e18aaaSAndroid Build Coastguard Worker    """
77*c2e18aaaSAndroid Build Coastguard Worker    run_cmd_dict = {
78*c2e18aaaSAndroid Build Coastguard Worker        'exe': self.EXECUTABLE,
79*c2e18aaaSAndroid Build Coastguard Worker        'options': self.OPTIONS,
80*c2e18aaaSAndroid Build Coastguard Worker        'test': testcase,
81*c2e18aaaSAndroid Build Coastguard Worker    }
82*c2e18aaaSAndroid Build Coastguard Worker    run_command = self._RUN_CMD.format(**run_cmd_dict)
83*c2e18aaaSAndroid Build Coastguard Worker    try:
84*c2e18aaaSAndroid Build Coastguard Worker      subprocess.check_output(
85*c2e18aaaSAndroid Build Coastguard Worker          run_command,
86*c2e18aaaSAndroid Build Coastguard Worker          stderr=subprocess.PIPE,
87*c2e18aaaSAndroid Build Coastguard Worker          env=self.full_env_vars,
88*c2e18aaaSAndroid Build Coastguard Worker          shell=True,
89*c2e18aaaSAndroid Build Coastguard Worker      )
90*c2e18aaaSAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
91*c2e18aaaSAndroid Build Coastguard Worker      self.log.append(e.output.decode())
92*c2e18aaaSAndroid Build Coastguard Worker      return False
93*c2e18aaaSAndroid Build Coastguard Worker    return True
94*c2e18aaaSAndroid Build Coastguard Worker
95*c2e18aaaSAndroid Build Coastguard Worker  def get_failed_log(self):
96*c2e18aaaSAndroid Build Coastguard Worker    """Get a trimmed failed log.
97*c2e18aaaSAndroid Build Coastguard Worker
98*c2e18aaaSAndroid Build Coastguard Worker    Strategy:
99*c2e18aaaSAndroid Build Coastguard Worker        In order not to show the unnecessary log such as build log,
100*c2e18aaaSAndroid Build Coastguard Worker        it's better to get a trimmed failed log that contains the
101*c2e18aaaSAndroid Build Coastguard Worker        most important information.
102*c2e18aaaSAndroid Build Coastguard Worker
103*c2e18aaaSAndroid Build Coastguard Worker    Returns:
104*c2e18aaaSAndroid Build Coastguard Worker        A trimmed failed log.
105*c2e18aaaSAndroid Build Coastguard Worker    """
106*c2e18aaaSAndroid Build Coastguard Worker    failed_log = '\n'.join(filter(None, self.log[-_FAILED_LINE_LIMIT:]))
107*c2e18aaaSAndroid Build Coastguard Worker    return failed_log
108*c2e18aaaSAndroid Build Coastguard Worker
109*c2e18aaaSAndroid Build Coastguard Worker
110*c2e18aaaSAndroid Build Coastguard Workerdef create_test_method(testcase, log_path):
111*c2e18aaaSAndroid Build Coastguard Worker  """Create a test method according to the testcase.
112*c2e18aaaSAndroid Build Coastguard Worker
113*c2e18aaaSAndroid Build Coastguard Worker  Args:
114*c2e18aaaSAndroid Build Coastguard Worker      testcase: A testcase name.
115*c2e18aaaSAndroid Build Coastguard Worker      log_path: A file path for storing the test result.
116*c2e18aaaSAndroid Build Coastguard Worker
117*c2e18aaaSAndroid Build Coastguard Worker  Returns:
118*c2e18aaaSAndroid Build Coastguard Worker      A created test method, and a test function name.
119*c2e18aaaSAndroid Build Coastguard Worker  """
120*c2e18aaaSAndroid Build Coastguard Worker  test_function_name = 'test_%s' % testcase.replace(' ', '_')
121*c2e18aaaSAndroid Build Coastguard Worker
122*c2e18aaaSAndroid Build Coastguard Worker  # pylint: disable=missing-docstring
123*c2e18aaaSAndroid Build Coastguard Worker  def template_test_method(self):
124*c2e18aaaSAndroid Build Coastguard Worker    self.test_passed = self.run_test(testcase)
125*c2e18aaaSAndroid Build Coastguard Worker    open(log_path, 'a').write('\n'.join(self.log))
126*c2e18aaaSAndroid Build Coastguard Worker    failed_message = 'Running command: %s failed.\n' % testcase
127*c2e18aaaSAndroid Build Coastguard Worker    failed_message += '' if self.test_passed else self.get_failed_log()
128*c2e18aaaSAndroid Build Coastguard Worker    self.assertTrue(self.test_passed, failed_message)
129*c2e18aaaSAndroid Build Coastguard Worker
130*c2e18aaaSAndroid Build Coastguard Worker  return test_function_name, template_test_method
131*c2e18aaaSAndroid Build Coastguard Worker
132*c2e18aaaSAndroid Build Coastguard Worker
133*c2e18aaaSAndroid Build Coastguard Workerdef create_test_run_dir():
134*c2e18aaaSAndroid Build Coastguard Worker  """Create the test run directory in tmp.
135*c2e18aaaSAndroid Build Coastguard Worker
136*c2e18aaaSAndroid Build Coastguard Worker  Returns:
137*c2e18aaaSAndroid Build Coastguard Worker      A string of the directory path.
138*c2e18aaaSAndroid Build Coastguard Worker  """
139*c2e18aaaSAndroid Build Coastguard Worker  utc_epoch_time = int(time.time())
140*c2e18aaaSAndroid Build Coastguard Worker  prefix = _TEST_RUN_DIR_PREFIX % utc_epoch_time
141*c2e18aaaSAndroid Build Coastguard Worker  return tempfile.mkdtemp(prefix=prefix)
142*c2e18aaaSAndroid Build Coastguard Worker
143*c2e18aaaSAndroid Build Coastguard Worker
144*c2e18aaaSAndroid Build Coastguard Workerif __name__ == '__main__':
145*c2e18aaaSAndroid Build Coastguard Worker  # TODO(b/129029189) Implement detail comparison check for dry-run mode.
146*c2e18aaaSAndroid Build Coastguard Worker  ARGS = sys.argv[1:]
147*c2e18aaaSAndroid Build Coastguard Worker  if ARGS:
148*c2e18aaaSAndroid Build Coastguard Worker    for exe in _ALTERNATIVES:
149*c2e18aaaSAndroid Build Coastguard Worker      if exe in ARGS:
150*c2e18aaaSAndroid Build Coastguard Worker        ARGS.remove(exe)
151*c2e18aaaSAndroid Build Coastguard Worker        ATestIntegrationTest.EXECUTABLE += exe
152*c2e18aaaSAndroid Build Coastguard Worker    ATestIntegrationTest.OPTIONS = ' '.join(ARGS)
153*c2e18aaaSAndroid Build Coastguard Worker  print('Running tests with {}\n'.format(ATestIntegrationTest.EXECUTABLE))
154*c2e18aaaSAndroid Build Coastguard Worker  try:
155*c2e18aaaSAndroid Build Coastguard Worker    LOG_PATH = os.path.join(create_test_run_dir(), _LOG_FILE)
156*c2e18aaaSAndroid Build Coastguard Worker    for TEST_PLANS in _INTEGRATION_TESTS:
157*c2e18aaaSAndroid Build Coastguard Worker      with open(TEST_PLANS) as test_plans:
158*c2e18aaaSAndroid Build Coastguard Worker        for test in test_plans:
159*c2e18aaaSAndroid Build Coastguard Worker          # Skip test when the line startswith #.
160*c2e18aaaSAndroid Build Coastguard Worker          if not test.strip() or test.strip().startswith('#'):
161*c2e18aaaSAndroid Build Coastguard Worker            continue
162*c2e18aaaSAndroid Build Coastguard Worker          test_func_name, test_func = create_test_method(test.strip(), LOG_PATH)
163*c2e18aaaSAndroid Build Coastguard Worker          setattr(ATestIntegrationTest, test_func_name, test_func)
164*c2e18aaaSAndroid Build Coastguard Worker    SUITE = unittest.TestLoader().loadTestsFromTestCase(ATestIntegrationTest)
165*c2e18aaaSAndroid Build Coastguard Worker    RESULTS = unittest.TextTestRunner(verbosity=2).run(SUITE)
166*c2e18aaaSAndroid Build Coastguard Worker  finally:
167*c2e18aaaSAndroid Build Coastguard Worker    if RESULTS.failures:
168*c2e18aaaSAndroid Build Coastguard Worker      print('Full test log is saved to %s' % LOG_PATH)
169*c2e18aaaSAndroid Build Coastguard Worker      sys.exit(_EXIT_TEST_FAILED)
170*c2e18aaaSAndroid Build Coastguard Worker    else:
171*c2e18aaaSAndroid Build Coastguard Worker      os.remove(LOG_PATH)
172