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