xref: /aosp_15_r20/tools/repohooks/rh/utils_unittest.py (revision d68f33bc6fb0cc2476107c2af0573a2f5a63dfc1)
1*d68f33bcSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*d68f33bcSAndroid Build Coastguard Worker# Copyright 2019 The Android Open Source Project
3*d68f33bcSAndroid Build Coastguard Worker#
4*d68f33bcSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*d68f33bcSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*d68f33bcSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*d68f33bcSAndroid Build Coastguard Worker#
8*d68f33bcSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*d68f33bcSAndroid Build Coastguard Worker#
10*d68f33bcSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*d68f33bcSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*d68f33bcSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d68f33bcSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*d68f33bcSAndroid Build Coastguard Worker# limitations under the License.
15*d68f33bcSAndroid Build Coastguard Worker
16*d68f33bcSAndroid Build Coastguard Worker"""Unittests for the utils module."""
17*d68f33bcSAndroid Build Coastguard Worker
18*d68f33bcSAndroid Build Coastguard Workerimport datetime
19*d68f33bcSAndroid Build Coastguard Workerimport os
20*d68f33bcSAndroid Build Coastguard Workerfrom pathlib import Path
21*d68f33bcSAndroid Build Coastguard Workerimport sys
22*d68f33bcSAndroid Build Coastguard Workerimport unittest
23*d68f33bcSAndroid Build Coastguard Worker
24*d68f33bcSAndroid Build Coastguard Worker_path = os.path.realpath(__file__ + '/../..')
25*d68f33bcSAndroid Build Coastguard Workerif sys.path[0] != _path:
26*d68f33bcSAndroid Build Coastguard Worker    sys.path.insert(0, _path)
27*d68f33bcSAndroid Build Coastguard Workerdel _path
28*d68f33bcSAndroid Build Coastguard Worker
29*d68f33bcSAndroid Build Coastguard Worker# We have to import our local modules after the sys.path tweak.  We can't use
30*d68f33bcSAndroid Build Coastguard Worker# relative imports because this is an executable program, not a module.
31*d68f33bcSAndroid Build Coastguard Worker# pylint: disable=wrong-import-position
32*d68f33bcSAndroid Build Coastguard Workerimport rh
33*d68f33bcSAndroid Build Coastguard Workerimport rh.utils
34*d68f33bcSAndroid Build Coastguard Worker
35*d68f33bcSAndroid Build Coastguard Worker
36*d68f33bcSAndroid Build Coastguard Workerclass TimeDeltaStrTests(unittest.TestCase):
37*d68f33bcSAndroid Build Coastguard Worker    """Verify behavior of timedelta_str object."""
38*d68f33bcSAndroid Build Coastguard Worker
39*d68f33bcSAndroid Build Coastguard Worker    def test_same(self):
40*d68f33bcSAndroid Build Coastguard Worker        """Check timedelta of 0 seconds."""
41*d68f33bcSAndroid Build Coastguard Worker        delta = datetime.timedelta(0)
42*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('0.000s', rh.utils.timedelta_str(delta))
43*d68f33bcSAndroid Build Coastguard Worker
44*d68f33bcSAndroid Build Coastguard Worker    def test_millisecondss(self):
45*d68f33bcSAndroid Build Coastguard Worker        """Check timedelta of milliseconds."""
46*d68f33bcSAndroid Build Coastguard Worker        delta = datetime.timedelta(seconds=0.123456)
47*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('0.123s', rh.utils.timedelta_str(delta))
48*d68f33bcSAndroid Build Coastguard Worker
49*d68f33bcSAndroid Build Coastguard Worker    def test_seconds(self):
50*d68f33bcSAndroid Build Coastguard Worker        """Check timedelta of seconds."""
51*d68f33bcSAndroid Build Coastguard Worker        delta = datetime.timedelta(seconds=12.3)
52*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('12.300s', rh.utils.timedelta_str(delta))
53*d68f33bcSAndroid Build Coastguard Worker
54*d68f33bcSAndroid Build Coastguard Worker    def test_minutes(self):
55*d68f33bcSAndroid Build Coastguard Worker        """Check timedelta of minutes."""
56*d68f33bcSAndroid Build Coastguard Worker        delta = datetime.timedelta(seconds=72.3)
57*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('1m12.300s', rh.utils.timedelta_str(delta))
58*d68f33bcSAndroid Build Coastguard Worker
59*d68f33bcSAndroid Build Coastguard Worker    def test_hours(self):
60*d68f33bcSAndroid Build Coastguard Worker        """Check timedelta of hours."""
61*d68f33bcSAndroid Build Coastguard Worker        delta = datetime.timedelta(seconds=4000.3)
62*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('1h6m40.300s', rh.utils.timedelta_str(delta))
63*d68f33bcSAndroid Build Coastguard Worker
64*d68f33bcSAndroid Build Coastguard Worker
65*d68f33bcSAndroid Build Coastguard Workerclass CompletedProcessTests(unittest.TestCase):
66*d68f33bcSAndroid Build Coastguard Worker    """Verify behavior of CompletedProcess object."""
67*d68f33bcSAndroid Build Coastguard Worker
68*d68f33bcSAndroid Build Coastguard Worker    def test_empty_cmdstr(self):
69*d68f33bcSAndroid Build Coastguard Worker        """Check cmdstr with an empty command."""
70*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess(args=[])
71*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('', result.cmdstr)
72*d68f33bcSAndroid Build Coastguard Worker
73*d68f33bcSAndroid Build Coastguard Worker    def test_basic_cmdstr(self):
74*d68f33bcSAndroid Build Coastguard Worker        """Check cmdstr with a basic command command."""
75*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess(args=['ls', 'a b'])
76*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual("ls 'a b'", result.cmdstr)
77*d68f33bcSAndroid Build Coastguard Worker
78*d68f33bcSAndroid Build Coastguard Worker    def test_str(self):
79*d68f33bcSAndroid Build Coastguard Worker        """Check str() handling."""
80*d68f33bcSAndroid Build Coastguard Worker        # We don't enforce much, just that it doesn't crash.
81*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess()
82*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual('', str(result))
83*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess(args=[])
84*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual('', str(result))
85*d68f33bcSAndroid Build Coastguard Worker
86*d68f33bcSAndroid Build Coastguard Worker    def test_repr(self):
87*d68f33bcSAndroid Build Coastguard Worker        """Check repr() handling."""
88*d68f33bcSAndroid Build Coastguard Worker        # We don't enforce much, just that it doesn't crash.
89*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess()
90*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual('', repr(result))
91*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.CompletedProcess(args=[])
92*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual('', repr(result))
93*d68f33bcSAndroid Build Coastguard Worker
94*d68f33bcSAndroid Build Coastguard Worker
95*d68f33bcSAndroid Build Coastguard Workerclass CalledProcessErrorTests(unittest.TestCase):
96*d68f33bcSAndroid Build Coastguard Worker    """Verify behavior of CalledProcessError object."""
97*d68f33bcSAndroid Build Coastguard Worker
98*d68f33bcSAndroid Build Coastguard Worker    def test_basic(self):
99*d68f33bcSAndroid Build Coastguard Worker        """Basic test we can create a normal instance."""
100*d68f33bcSAndroid Build Coastguard Worker        rh.utils.CalledProcessError(0, ['mycmd'])
101*d68f33bcSAndroid Build Coastguard Worker
102*d68f33bcSAndroid Build Coastguard Worker    def test_stringify(self):
103*d68f33bcSAndroid Build Coastguard Worker        """Check stringify() handling."""
104*d68f33bcSAndroid Build Coastguard Worker        # We don't assert much so we leave flexibility in changing format.
105*d68f33bcSAndroid Build Coastguard Worker        err = rh.utils.CalledProcessError(0, ['mycmd'])
106*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('mycmd', err.stringify())
107*d68f33bcSAndroid Build Coastguard Worker
108*d68f33bcSAndroid Build Coastguard Worker    def test_str(self):
109*d68f33bcSAndroid Build Coastguard Worker        """Check str() handling."""
110*d68f33bcSAndroid Build Coastguard Worker        # We don't assert much so we leave flexibility in changing format.
111*d68f33bcSAndroid Build Coastguard Worker        err = rh.utils.CalledProcessError(0, ['mycmd'])
112*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('mycmd', str(err))
113*d68f33bcSAndroid Build Coastguard Worker
114*d68f33bcSAndroid Build Coastguard Worker    def test_repr(self):
115*d68f33bcSAndroid Build Coastguard Worker        """Check repr() handling."""
116*d68f33bcSAndroid Build Coastguard Worker        # We don't assert much so we leave flexibility in changing format.
117*d68f33bcSAndroid Build Coastguard Worker        err = rh.utils.CalledProcessError(0, ['mycmd'])
118*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual('', repr(err))
119*d68f33bcSAndroid Build Coastguard Worker
120*d68f33bcSAndroid Build Coastguard Worker    def test_output(self):
121*d68f33bcSAndroid Build Coastguard Worker        """Make sure .output is removed and .stdout works."""
122*d68f33bcSAndroid Build Coastguard Worker        e = rh.utils.CalledProcessError(
123*d68f33bcSAndroid Build Coastguard Worker            0, ['true'], stdout='STDOUT', stderr='STDERR')
124*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(AttributeError):
125*d68f33bcSAndroid Build Coastguard Worker            assert e.output is None
126*d68f33bcSAndroid Build Coastguard Worker        assert e.stdout == 'STDOUT'
127*d68f33bcSAndroid Build Coastguard Worker        assert e.stderr == 'STDERR'
128*d68f33bcSAndroid Build Coastguard Worker
129*d68f33bcSAndroid Build Coastguard Worker        e.stdout = 'STDout'
130*d68f33bcSAndroid Build Coastguard Worker        e.stderr = 'STDerr'
131*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(AttributeError):
132*d68f33bcSAndroid Build Coastguard Worker            assert e.output is None
133*d68f33bcSAndroid Build Coastguard Worker        assert e.stdout == 'STDout'
134*d68f33bcSAndroid Build Coastguard Worker        assert e.stderr == 'STDerr'
135*d68f33bcSAndroid Build Coastguard Worker
136*d68f33bcSAndroid Build Coastguard Worker
137*d68f33bcSAndroid Build Coastguard Workerclass RunCommandTests(unittest.TestCase):
138*d68f33bcSAndroid Build Coastguard Worker    """Verify behavior of run helper."""
139*d68f33bcSAndroid Build Coastguard Worker
140*d68f33bcSAndroid Build Coastguard Worker    def test_basic(self):
141*d68f33bcSAndroid Build Coastguard Worker        """Simple basic test."""
142*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['true'])
143*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('true', ret.cmdstr)
144*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stdout)
145*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stderr)
146*d68f33bcSAndroid Build Coastguard Worker
147*d68f33bcSAndroid Build Coastguard Worker    def test_stdout_capture(self):
148*d68f33bcSAndroid Build Coastguard Worker        """Verify output capturing works."""
149*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['echo', 'hi'], redirect_stdout=True)
150*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('hi\n', ret.stdout)
151*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stderr)
152*d68f33bcSAndroid Build Coastguard Worker
153*d68f33bcSAndroid Build Coastguard Worker    def test_stderr_capture(self):
154*d68f33bcSAndroid Build Coastguard Worker        """Verify stderr capturing works."""
155*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['sh', '-c', 'echo hi >&2'], redirect_stderr=True)
156*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stdout)
157*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('hi\n', ret.stderr)
158*d68f33bcSAndroid Build Coastguard Worker
159*d68f33bcSAndroid Build Coastguard Worker    def test_stdout_utf8(self):
160*d68f33bcSAndroid Build Coastguard Worker        """Verify reading UTF-8 data works."""
161*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['printf', r'\xc3\x9f'], redirect_stdout=True)
162*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('ß', ret.stdout)
163*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stderr)
164*d68f33bcSAndroid Build Coastguard Worker
165*d68f33bcSAndroid Build Coastguard Worker    def test_stdin_utf8(self):
166*d68f33bcSAndroid Build Coastguard Worker        """Verify writing UTF-8 data works."""
167*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['cat'], redirect_stdout=True, input='ß')
168*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual('ß', ret.stdout)
169*d68f33bcSAndroid Build Coastguard Worker        self.assertIsNone(ret.stderr)
170*d68f33bcSAndroid Build Coastguard Worker
171*d68f33bcSAndroid Build Coastguard Worker    def test_check_false(self):
172*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of check=False."""
173*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['false'], check=False)
174*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, ret.returncode)
175*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('false', str(ret))
176*d68f33bcSAndroid Build Coastguard Worker
177*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['true'], check=False)
178*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual(0, ret.returncode)
179*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('true', str(ret))
180*d68f33bcSAndroid Build Coastguard Worker
181*d68f33bcSAndroid Build Coastguard Worker    def test_check_true(self):
182*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of check=True."""
183*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(rh.utils.CalledProcessError) as e:
184*d68f33bcSAndroid Build Coastguard Worker            rh.utils.run(['false'], check=True)
185*d68f33bcSAndroid Build Coastguard Worker        err = e.exception
186*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, err.returncode)
187*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('false', str(err))
188*d68f33bcSAndroid Build Coastguard Worker
189*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['true'], check=True)
190*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual(0, ret.returncode)
191*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('true', str(ret))
192*d68f33bcSAndroid Build Coastguard Worker
193*d68f33bcSAndroid Build Coastguard Worker    def test_check_false_output(self):
194*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of output capturing w/check=False."""
195*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(rh.utils.CalledProcessError) as e:
196*d68f33bcSAndroid Build Coastguard Worker            rh.utils.run(['sh', '-c', 'echo out; echo err >&2; false'],
197*d68f33bcSAndroid Build Coastguard Worker                         check=True, capture_output=True)
198*d68f33bcSAndroid Build Coastguard Worker        err = e.exception
199*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, err.returncode)
200*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('false', str(err))
201*d68f33bcSAndroid Build Coastguard Worker
202*d68f33bcSAndroid Build Coastguard Worker    def test_check_true_missing_prog_output(self):
203*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of output capturing w/missing progs."""
204*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(rh.utils.CalledProcessError) as e:
205*d68f33bcSAndroid Build Coastguard Worker            rh.utils.run(['./!~a/b/c/d/'], check=True, capture_output=True)
206*d68f33bcSAndroid Build Coastguard Worker        err = e.exception
207*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, err.returncode)
208*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('a/b/c/d', str(err))
209*d68f33bcSAndroid Build Coastguard Worker
210*d68f33bcSAndroid Build Coastguard Worker    def test_check_false_missing_prog_output(self):
211*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of output capturing w/missing progs."""
212*d68f33bcSAndroid Build Coastguard Worker        ret = rh.utils.run(['./!~a/b/c/d/'], check=False, capture_output=True)
213*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, ret.returncode)
214*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('a/b/c/d', str(ret))
215*d68f33bcSAndroid Build Coastguard Worker
216*d68f33bcSAndroid Build Coastguard Worker    def test_check_false_missing_prog_combined_output(self):
217*d68f33bcSAndroid Build Coastguard Worker        """Verify handling of combined output capturing w/missing progs."""
218*d68f33bcSAndroid Build Coastguard Worker        with self.assertRaises(rh.utils.CalledProcessError) as e:
219*d68f33bcSAndroid Build Coastguard Worker            rh.utils.run(['./!~a/b/c/d/'], check=True,
220*d68f33bcSAndroid Build Coastguard Worker                         combine_stdout_stderr=True)
221*d68f33bcSAndroid Build Coastguard Worker        err = e.exception
222*d68f33bcSAndroid Build Coastguard Worker        self.assertNotEqual(0, err.returncode)
223*d68f33bcSAndroid Build Coastguard Worker        self.assertIn('a/b/c/d', str(err))
224*d68f33bcSAndroid Build Coastguard Worker
225*d68f33bcSAndroid Build Coastguard Worker    def test_pathlib(self):
226*d68f33bcSAndroid Build Coastguard Worker        """Verify pathlib arguments work."""
227*d68f33bcSAndroid Build Coastguard Worker        result = rh.utils.run(['true', Path('/')])
228*d68f33bcSAndroid Build Coastguard Worker        # Verify stringify behavior.
229*d68f33bcSAndroid Build Coastguard Worker        str(result)
230*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual(result.cmdstr, 'true /')
231*d68f33bcSAndroid Build Coastguard Worker
232*d68f33bcSAndroid Build Coastguard Worker
233*d68f33bcSAndroid Build Coastguard Workerif __name__ == '__main__':
234*d68f33bcSAndroid Build Coastguard Worker    unittest.main()
235