xref: /aosp_15_r20/build/make/ci/build_test_suites_local_test.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1# Copyright 2024, The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Integration tests for build_test_suites that require a local build env."""
16
17import os
18import pathlib
19import shutil
20import signal
21import subprocess
22import tempfile
23import time
24import ci_test_lib
25
26
27class BuildTestSuitesLocalTest(ci_test_lib.TestCase):
28
29  def setUp(self):
30    self.top_dir = pathlib.Path(os.environ['ANDROID_BUILD_TOP']).resolve()
31    self.executable = self.top_dir.joinpath('build/make/ci/build_test_suites')
32    self.process_session = ci_test_lib.TemporaryProcessSession(self)
33    self.temp_dir = ci_test_lib.TestTemporaryDirectory.create(self)
34
35  def build_subprocess_args(self, build_args: list[str]):
36    env = os.environ.copy()
37    env['TOP'] = str(self.top_dir)
38    env['OUT_DIR'] = self.temp_dir
39
40    args = ([self.executable] + build_args,)
41    kwargs = {
42        'cwd': self.top_dir,
43        'env': env,
44        'text': True,
45    }
46
47    return (args, kwargs)
48
49  def run_build(self, build_args: list[str]) -> subprocess.CompletedProcess:
50    args, kwargs = self.build_subprocess_args(build_args)
51
52    return subprocess.run(
53        *args,
54        **kwargs,
55        check=True,
56        capture_output=True,
57        timeout=5 * 60,
58    )
59
60  def assert_children_alive(self, children: list[int]):
61    for c in children:
62      self.assertTrue(ci_test_lib.process_alive(c))
63
64  def assert_children_dead(self, children: list[int]):
65    for c in children:
66      self.assertFalse(ci_test_lib.process_alive(c))
67
68  def test_fails_for_invalid_arg(self):
69    invalid_arg = '--invalid-arg'
70
71    with self.assertRaises(subprocess.CalledProcessError) as cm:
72      self.run_build([invalid_arg])
73
74    self.assertIn(invalid_arg, cm.exception.stderr)
75
76  def test_builds_successfully(self):
77    self.run_build(['nothing'])
78
79  def test_can_interrupt_build(self):
80    args, kwargs = self.build_subprocess_args(['general-tests'])
81    p = self.process_session.create(args, kwargs)
82
83    # TODO(lucafarsi): Replace this (and other instances) with a condition.
84    time.sleep(5)  # Wait for the build to get going.
85    self.assertIsNone(p.poll())  # Check that the process is still alive.
86    children = query_child_pids(p.pid)
87    self.assert_children_alive(children)
88
89    p.send_signal(signal.SIGINT)
90    p.wait()
91
92    time.sleep(5)  # Wait for things to die out.
93    self.assert_children_dead(children)
94
95  def test_can_kill_build_process_group(self):
96    args, kwargs = self.build_subprocess_args(['general-tests'])
97    p = self.process_session.create(args, kwargs)
98
99    time.sleep(5)  # Wait for the build to get going.
100    self.assertIsNone(p.poll())  # Check that the process is still alive.
101    children = query_child_pids(p.pid)
102    self.assert_children_alive(children)
103
104    os.killpg(os.getpgid(p.pid), signal.SIGKILL)
105    p.wait()
106
107    time.sleep(5)  # Wait for things to die out.
108    self.assert_children_dead(children)
109
110
111# TODO(hzalek): Replace this with `psutils` once available in the tree.
112def query_child_pids(parent_pid: int) -> set[int]:
113  p = subprocess.run(
114      ['pgrep', '-P', str(parent_pid)],
115      check=True,
116      capture_output=True,
117      text=True,
118  )
119  return {int(pid) for pid in p.stdout.splitlines()}
120
121
122if __name__ == '__main__':
123  ci_test_lib.main()
124