blob: af845eb4eaa4a427e54a260d22bcd13e79c64039 [file] [log] [blame]
Mike Frysingerd0b43852021-02-12 19:04:57 -05001# Copyright 2021 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unittests for wrapper3"""
6
7import os
8from pathlib import Path
9import sys
10from typing import List, Union
11
12from chromite.lib import constants
13from chromite.lib import cros_build_lib
14from chromite.lib import cros_test_lib
15from chromite.lib import timeout_util
Mike Frysingerd0b43852021-02-12 19:04:57 -050016
17
Mike Frysingerd0b43852021-02-12 19:04:57 -050018WRAPPER = Path(__file__).resolve().parent / 'wrapper3.py'
19
20
21class FindTargetTests(cros_test_lib.TempDirTestCase):
22 """Tests for FindTarget()."""
23
24 def setUp(self):
Mike Frysingerd0b43852021-02-12 19:04:57 -050025 # TODO)vapier): Switch tempdir to pathlib.
26 self.tempdir = Path(self.tempdir)
27
28 # Create a skeleton chromite layout.
29 # tmpdir/
30 # chromite/
31 # bin/<wrapper>
32 # scripts/
33 # api -> <real chromite>/api/
34 # lib -> <real chromite>/lib/
35 # utils -> <real chromite>/utils/
36 # __init__.py # Marker file for Python module import.
37 # PRESUBMIT.cfg # Marker file for our wrapper to find chromite.
38 self.chromite_dir = self.tempdir / 'chromite'
39 self.bindir = self.chromite_dir / 'bin'
40 self.bindir.mkdir(parents=True)
41 self.scripts_dir = self.chromite_dir / 'scripts'
42 self.scripts_dir.mkdir()
Mike Frysinger8de7e862021-05-14 00:55:25 -040043 for subdir in ('api', 'lib', 'third_party', 'utils'):
Mike Frysingerd0b43852021-02-12 19:04:57 -050044 (self.chromite_dir / subdir).symlink_to(
45 Path(constants.CHROMITE_DIR) / subdir)
46 for subfile in ('__init__.py', 'PRESUBMIT.cfg'):
47 (self.chromite_dir / subfile).touch()
48 self.wrapper = self.scripts_dir / WRAPPER.name
49 # Copy over the wrapper. We can't just symlink it because the code also
50 # walks & resolves symlinks on itself. Try hardlink at first, but if the
51 # tempdir is on a diff mount, fallback to a copy.
52 try:
53 if sys.version_info >= (3, 8):
54 self.wrapper.link_to(WRAPPER)
55 else:
56 os.link(WRAPPER, self.wrapper)
57 except OSError:
58 self.wrapper.write_bytes(WRAPPER.read_bytes())
59 self.wrapper.chmod(0o755)
60
61 @staticmethod
62 def insert_path(var: str, value: str):
63 """Insert |value| into the start of the environment |var|."""
64 if var in os.environ:
65 value += f':{os.environ[var]}'
66 os.environ[var] = value
67
68 def gen_script(self, path: Path, wrapper: Path = None):
69 """Create a script at |path|."""
70 path.parent.mkdir(parents=True, exist_ok=True)
71 path = path.with_suffix('.py')
72 path.write_text('def main(argv):\n print("hi", argv)\n')
73 if wrapper is None:
74 wrapper = path.with_suffix('')
75 wrapper.symlink_to(self.wrapper)
76
77 def run_script(self, argv: List[Union[Path, str]], **kwargs):
78 """Run |prog| and return the output."""
79 # Log the directory layout to help with debugging.
80 try:
81 cros_build_lib.run(['tree', '-p', str(self.tempdir)], encoding='utf-8',
82 print_cmd=False)
83 except cros_build_lib.RunCommandError:
84 pass
85
86 # Helper to include a small timeout in case of bugs.
87 with timeout_util.Timeout(30):
88 return cros_build_lib.run([str(x) for x in argv], capture_output=True,
89 encoding='utf-8', **kwargs)
90
91 def _run_tests(self, prog: Path, verify=None, **kwargs):
92 """Run |prog| in the different fun ways."""
93 if verify is None:
94 verify = lambda result: self.assertEqual('hi []\n', result.output)
95
96 # Execute absolute path.
97 result = self.run_script([prog], **kwargs)
98 verify(result)
99
100 # Execute ./ relative path.
101 result = self.run_script([f'./{prog.name}'], cwd=prog.parent, **kwargs)
102 verify(result)
103
104 # Execute ./path/ relative path.
105 result = self.run_script([f'./{prog.parent.name}/{prog.name}'],
106 cwd=prog.parent.parent, **kwargs)
107 verify(result)
108
109 # Run via $PATH.
110 self.insert_path('PATH', str(prog.parent))
111 result = self.run_script([prog.name], **kwargs)
112 verify(result)
113
114 def testExternal(self):
115 """Verify use from outside of chromite/ works with main() scripts."""
116 prog = self.tempdir / 'path' / 'prog'
117 self.gen_script(prog)
118 self._run_tests(prog)
119
120 def testChromiteBin(self):
121 """Verify chromite/bin/ works with module in chromite/scripts/."""
122 prog = self.bindir / 'prog'
123 self.gen_script(self.scripts_dir / prog.name, prog)
124 self._run_tests(prog)
125
126 def testChromiteScripts(self):
127 """Verify chromite/scripts/ works with main() scripts."""
128 prog = self.scripts_dir / 'prog'
129 self.gen_script(prog)
130 self._run_tests(prog)
131
132 def testChromiteCustomdir(self):
133 """Verify chromite/customdir/ works with main() scripts."""
134 prog = self.chromite_dir / 'customdir' / 'prog'
135 self.gen_script(prog)
136 self._run_tests(prog)
137
138 def testChromiteTopdir(self):
139 """Verify chromite/ works with main() scripts."""
140 prog = self.chromite_dir / 'prog'
141 self.gen_script(prog)
142 self._run_tests(prog)
143
144 def testUnittests(self):
145 """Allow direct execution of unittests."""
146 prog = self.chromite_dir / 'subdir' / 'prog_unittest'
147 prog.parent.mkdir(parents=True, exist_ok=True)
148 path = prog.with_suffix('.py')
149 path.write_text('import sys; print("hi", sys.argv[1:])\n')
150 prog.symlink_to(self.wrapper)
151 self._run_tests(prog)
152
153 def testTests(self):
154 """Allow direct execution of tests."""
155 prog = self.chromite_dir / 'subdir' / 'prog_unittest'
156 prog.parent.mkdir(parents=True, exist_ok=True)
157 prog.symlink_to(self.wrapper)
158 prog.with_suffix('.py').write_text(
159 'import sys; print("hi", sys.argv[1:])\n')
160 self._run_tests(prog)
161
162 def testWrapper(self):
163 """Fail quickly when running the wrapper directly."""
164 verify = lambda result: self.assertEqual(result.returncode, 100)
165 self._run_tests(self.wrapper, verify=verify, check=False)
166
167 def testMissingScript(self):
168 """Fail quickly if wrapped script is missing."""
169 verify = lambda result: self.assertNotEqual(result.returncode, 0)
170 prog = self.bindir / 'prog'
171 prog.symlink_to(self.wrapper)
172 self._run_tests(prog, verify=verify, check=False)
173
174 def testBrokenScript(self):
175 """Fail quickly if wrapped script is corrupt."""
176 verify = lambda result: self.assertNotEqual(result.returncode, 0)
177 prog = self.scripts_dir / 'prog'
178 prog.symlink_to(self.wrapper)
179 # Script has syntax errors and cannot be imported.
180 prog.with_suffix('.py').write_text('}')
181 self._run_tests(prog, verify=verify, check=False)