xref: /aosp_15_r20/external/toolchain-utils/crosperf/download_images_unittest.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li#!/usr/bin/env python3
2*760c253cSXin Li# -*- coding: utf-8 -*-
3*760c253cSXin Li# Copyright 2019 The ChromiumOS Authors
4*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be
5*760c253cSXin Li# found in the LICENSE file.
6*760c253cSXin Li
7*760c253cSXin Li"""Download image unittest."""
8*760c253cSXin Li
9*760c253cSXin Li
10*760c253cSXin Liimport os
11*760c253cSXin Liimport re
12*760c253cSXin Liimport unittest
13*760c253cSXin Liimport unittest.mock as mock
14*760c253cSXin Li
15*760c253cSXin Lifrom cros_utils import command_executer
16*760c253cSXin Lifrom cros_utils import logger
17*760c253cSXin Liimport download_images
18*760c253cSXin Liimport test_flag
19*760c253cSXin Li
20*760c253cSXin Li
21*760c253cSXin LiMOCK_LOGGER = logger.GetLogger(log_dir="", mock=True)
22*760c253cSXin Li
23*760c253cSXin Li
24*760c253cSXin Liclass RegexMatcher:
25*760c253cSXin Li    """A regex matcher, for passing to mocks."""
26*760c253cSXin Li
27*760c253cSXin Li    def __init__(self, regex):
28*760c253cSXin Li        self._regex = re.compile(regex)
29*760c253cSXin Li
30*760c253cSXin Li    def __eq__(self, string):
31*760c253cSXin Li        return self._regex.search(string) is not None
32*760c253cSXin Li
33*760c253cSXin Li
34*760c253cSXin Liclass ImageDownloaderTestcast(unittest.TestCase):
35*760c253cSXin Li    """The image downloader test class."""
36*760c253cSXin Li
37*760c253cSXin Li    def __init__(self, *args, **kwargs):
38*760c253cSXin Li        super(ImageDownloaderTestcast, self).__init__(*args, **kwargs)
39*760c253cSXin Li        self.called_download_image = False
40*760c253cSXin Li        self.called_uncompress_image = False
41*760c253cSXin Li        self.called_get_build_id = False
42*760c253cSXin Li        self.called_download_autotest_files = False
43*760c253cSXin Li        self.called_download_debug_file = False
44*760c253cSXin Li
45*760c253cSXin Li    @mock.patch.object(os, "makedirs")
46*760c253cSXin Li    @mock.patch.object(os.path, "exists")
47*760c253cSXin Li    def test_download_image(self, mock_path_exists, mock_mkdirs):
48*760c253cSXin Li        # Set mock and test values.
49*760c253cSXin Li        mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
50*760c253cSXin Li        test_chroot = "/usr/local/home/chromeos"
51*760c253cSXin Li        test_build_id = "lumpy-release/R36-5814.0.0"
52*760c253cSXin Li        image_path = (
53*760c253cSXin Li            "gs://chromeos-image-archive/%s/chromiumos_test_image.tar.xz"
54*760c253cSXin Li            % test_build_id
55*760c253cSXin Li        )
56*760c253cSXin Li
57*760c253cSXin Li        downloader = download_images.ImageDownloader(
58*760c253cSXin Li            logger_to_use=MOCK_LOGGER, cmd_exec=mock_cmd_exec
59*760c253cSXin Li        )
60*760c253cSXin Li
61*760c253cSXin Li        # Set os.path.exists to always return False and run downloader
62*760c253cSXin Li        mock_path_exists.return_value = False
63*760c253cSXin Li        test_flag.SetTestMode(True)
64*760c253cSXin Li        self.assertRaises(
65*760c253cSXin Li            download_images.MissingImage,
66*760c253cSXin Li            downloader.DownloadImage,
67*760c253cSXin Li            test_chroot,
68*760c253cSXin Li            test_build_id,
69*760c253cSXin Li            image_path,
70*760c253cSXin Li        )
71*760c253cSXin Li
72*760c253cSXin Li        # Verify os.path.exists was called thrice, with proper arguments.
73*760c253cSXin Li        self.assertEqual(mock_path_exists.call_count, 3)
74*760c253cSXin Li        mock_path_exists.assert_any_call(
75*760c253cSXin Li            RegexMatcher(
76*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/"
77*760c253cSXin Li                "R36-5814.0.0/chromiumos_test_image.bin"
78*760c253cSXin Li            )
79*760c253cSXin Li        )
80*760c253cSXin Li        mock_path_exists.assert_any_call(
81*760c253cSXin Li            RegexMatcher(
82*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0"
83*760c253cSXin Li            )
84*760c253cSXin Li        )
85*760c253cSXin Li        mock_path_exists.assert_any_call("/etc/cros_chroot_version")
86*760c253cSXin Li
87*760c253cSXin Li        # Verify we called os.mkdirs
88*760c253cSXin Li        self.assertEqual(mock_mkdirs.call_count, 1)
89*760c253cSXin Li        mock_mkdirs.assert_called_with(
90*760c253cSXin Li            RegexMatcher(
91*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0"
92*760c253cSXin Li            )
93*760c253cSXin Li        )
94*760c253cSXin Li
95*760c253cSXin Li        # Verify we called RunCommand once, with proper arguments.
96*760c253cSXin Li        self.assertEqual(mock_cmd_exec.RunCommand.call_count, 1)
97*760c253cSXin Li        expected_args = RegexMatcher(
98*760c253cSXin Li            "/usr/local/home/chromeos/src/chromium/depot_tools/gsutil.py "
99*760c253cSXin Li            "cp gs://chromeos-image-archive/lumpy-release/R36-5814.0.0/"
100*760c253cSXin Li            "chromiumos_test_image.tar.xz "
101*760c253cSXin Li            "/usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0"
102*760c253cSXin Li        )
103*760c253cSXin Li
104*760c253cSXin Li        mock_cmd_exec.RunCommand.assert_called_with(expected_args)
105*760c253cSXin Li
106*760c253cSXin Li        # Reset the velues in the mocks; set os.path.exists to always return
107*760c253cSXin Li        # True (except for "inside chroot" check).
108*760c253cSXin Li        mock_path_exists.reset_mock()
109*760c253cSXin Li        mock_cmd_exec.reset_mock()
110*760c253cSXin Li        mock_path_exists.side_effect = lambda x: x != "/etc/cros_chroot_version"
111*760c253cSXin Li
112*760c253cSXin Li        # Run downloader
113*760c253cSXin Li        downloader.DownloadImage(test_chroot, test_build_id, image_path)
114*760c253cSXin Li
115*760c253cSXin Li        # Verify os.path.exists was called thrice, with proper arguments.
116*760c253cSXin Li        self.assertEqual(mock_path_exists.call_count, 3)
117*760c253cSXin Li        mock_path_exists.assert_called_with(
118*760c253cSXin Li            RegexMatcher(
119*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/"
120*760c253cSXin Li                "R36-5814.0.0/chromiumos_test_image.bin"
121*760c253cSXin Li            )
122*760c253cSXin Li        )
123*760c253cSXin Li        mock_path_exists.assert_any_call(
124*760c253cSXin Li            RegexMatcher(
125*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0"
126*760c253cSXin Li            )
127*760c253cSXin Li        )
128*760c253cSXin Li        mock_path_exists.assert_any_call("/etc/cros_chroot_version")
129*760c253cSXin Li
130*760c253cSXin Li        # Verify we made no RunCommand or ChrootRunCommand calls (since
131*760c253cSXin Li        # os.path.exists returned True, there was no work do be done).
132*760c253cSXin Li        self.assertEqual(mock_cmd_exec.RunCommand.call_count, 0)
133*760c253cSXin Li        self.assertEqual(mock_cmd_exec.ChrootRunCommand.call_count, 0)
134*760c253cSXin Li
135*760c253cSXin Li    @mock.patch.object(os.path, "exists")
136*760c253cSXin Li    def test_uncompress_image(self, mock_path_exists):
137*760c253cSXin Li        # set mock and test values.
138*760c253cSXin Li        mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
139*760c253cSXin Li        test_chroot = "/usr/local/home/chromeos"
140*760c253cSXin Li        test_build_id = "lumpy-release/R36-5814.0.0"
141*760c253cSXin Li
142*760c253cSXin Li        downloader = download_images.ImageDownloader(
143*760c253cSXin Li            logger_to_use=MOCK_LOGGER, cmd_exec=mock_cmd_exec
144*760c253cSXin Li        )
145*760c253cSXin Li
146*760c253cSXin Li        # Set os.path.exists to always return False and run uncompress.
147*760c253cSXin Li        mock_path_exists.return_value = False
148*760c253cSXin Li        self.assertRaises(
149*760c253cSXin Li            download_images.MissingImage,
150*760c253cSXin Li            downloader.UncompressImage,
151*760c253cSXin Li            test_chroot,
152*760c253cSXin Li            test_build_id,
153*760c253cSXin Li        )
154*760c253cSXin Li
155*760c253cSXin Li        # Verify os.path.exists was called twice, with correct arguments.
156*760c253cSXin Li        self.assertEqual(mock_path_exists.call_count, 2)
157*760c253cSXin Li        mock_path_exists.assert_called_with(
158*760c253cSXin Li            RegexMatcher(
159*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/"
160*760c253cSXin Li                "R36-5814.0.0/chromiumos_test_image.bin"
161*760c253cSXin Li            )
162*760c253cSXin Li        )
163*760c253cSXin Li        mock_path_exists.assert_any_call("/etc/cros_chroot_version")
164*760c253cSXin Li
165*760c253cSXin Li        # Verify RunCommand was called twice with correct arguments.
166*760c253cSXin Li        self.assertEqual(mock_cmd_exec.RunCommand.call_count, 2)
167*760c253cSXin Li        # Call 1, should have 2 arguments
168*760c253cSXin Li        self.assertEqual(len(mock_cmd_exec.RunCommand.call_args_list[0]), 2)
169*760c253cSXin Li        actual_arg = mock_cmd_exec.RunCommand.call_args_list[0][0]
170*760c253cSXin Li        expected_arg = (
171*760c253cSXin Li            RegexMatcher(
172*760c253cSXin Li                "cd /usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0 ; "
173*760c253cSXin Li                "tar -Jxf chromiumos_test_image.tar.xz "
174*760c253cSXin Li            ),
175*760c253cSXin Li        )
176*760c253cSXin Li        self.assertEqual(expected_arg, actual_arg)
177*760c253cSXin Li        # 2nd arg must be exception handler
178*760c253cSXin Li        except_handler_string = "RunCommandExceptionHandler.HandleException"
179*760c253cSXin Li        self.assertTrue(
180*760c253cSXin Li            except_handler_string
181*760c253cSXin Li            in repr(mock_cmd_exec.RunCommand.call_args_list[0][1])
182*760c253cSXin Li        )
183*760c253cSXin Li
184*760c253cSXin Li        # Call 2, should have 2 arguments
185*760c253cSXin Li        self.assertEqual(len(mock_cmd_exec.RunCommand.call_args_list[1]), 2)
186*760c253cSXin Li        actual_arg = mock_cmd_exec.RunCommand.call_args_list[1][0]
187*760c253cSXin Li        expected_arg = (
188*760c253cSXin Li            RegexMatcher(
189*760c253cSXin Li                "cd /usr/local/home/chromeos/.*tmp/lumpy-release/R36-5814.0.0 ; "
190*760c253cSXin Li                "rm -f chromiumos_test_image.bin "
191*760c253cSXin Li            ),
192*760c253cSXin Li        )
193*760c253cSXin Li        self.assertEqual(expected_arg, actual_arg)
194*760c253cSXin Li        # 2nd arg must be empty
195*760c253cSXin Li        self.assertTrue(
196*760c253cSXin Li            "{}" in repr(mock_cmd_exec.RunCommand.call_args_list[1][1])
197*760c253cSXin Li        )
198*760c253cSXin Li
199*760c253cSXin Li        # Set os.path.exists to always return True (except for "inside chroot"
200*760c253cSXin Li        # check) and run uncompress.
201*760c253cSXin Li        mock_path_exists.reset_mock()
202*760c253cSXin Li        mock_cmd_exec.reset_mock()
203*760c253cSXin Li        mock_path_exists.side_effect = lambda x: x != "/etc/cros_chroot_version"
204*760c253cSXin Li        downloader.UncompressImage(test_chroot, test_build_id)
205*760c253cSXin Li
206*760c253cSXin Li        # Verify os.path.exists was called once, with correct arguments.
207*760c253cSXin Li        self.assertEqual(mock_path_exists.call_count, 2)
208*760c253cSXin Li        mock_path_exists.assert_called_with(
209*760c253cSXin Li            RegexMatcher(
210*760c253cSXin Li                "/usr/local/home/chromeos/.*tmp/lumpy-release/"
211*760c253cSXin Li                "R36-5814.0.0/chromiumos_test_image.bin"
212*760c253cSXin Li            )
213*760c253cSXin Li        )
214*760c253cSXin Li        mock_path_exists.assert_any_call("/etc/cros_chroot_version")
215*760c253cSXin Li
216*760c253cSXin Li        # Verify RunCommand was not called.
217*760c253cSXin Li        self.assertEqual(mock_cmd_exec.RunCommand.call_count, 0)
218*760c253cSXin Li
219*760c253cSXin Li    def test_run(self):
220*760c253cSXin Li        # Set test arguments
221*760c253cSXin Li        test_chroot = "/usr/local/home/chromeos"
222*760c253cSXin Li        test_build_id = "remote/lumpy/latest-dev"
223*760c253cSXin Li        test_empty_autotest_path = ""
224*760c253cSXin Li        test_empty_debug_path = ""
225*760c253cSXin Li        test_autotest_path = "/tmp/autotest"
226*760c253cSXin Li        test_debug_path = "/tmp/debug"
227*760c253cSXin Li        download_debug = True
228*760c253cSXin Li
229*760c253cSXin Li        # Set values to test/check.
230*760c253cSXin Li        self.called_download_image = False
231*760c253cSXin Li        self.called_uncompress_image = False
232*760c253cSXin Li        self.called_get_build_id = False
233*760c253cSXin Li        self.called_download_autotest_files = False
234*760c253cSXin Li        self.called_download_debug_file = False
235*760c253cSXin Li
236*760c253cSXin Li        # Define fake stub functions for Run to call
237*760c253cSXin Li        def FakeGetBuildID(unused_root, unused_xbuddy_label):
238*760c253cSXin Li            self.called_get_build_id = True
239*760c253cSXin Li            return "lumpy-release/R36-5814.0.0"
240*760c253cSXin Li
241*760c253cSXin Li        def GoodDownloadImage(root, build_id, image_path):
242*760c253cSXin Li            if root or build_id or image_path:
243*760c253cSXin Li                pass
244*760c253cSXin Li            self.called_download_image = True
245*760c253cSXin Li            return "chromiumos_test_image.bin"
246*760c253cSXin Li
247*760c253cSXin Li        def BadDownloadImage(root, build_id, image_path):
248*760c253cSXin Li            if root or build_id or image_path:
249*760c253cSXin Li                pass
250*760c253cSXin Li            self.called_download_image = True
251*760c253cSXin Li            raise download_images.MissingImage("Could not download image")
252*760c253cSXin Li
253*760c253cSXin Li        def FakeUncompressImage(root, build_id):
254*760c253cSXin Li            if root or build_id:
255*760c253cSXin Li                pass
256*760c253cSXin Li            self.called_uncompress_image = True
257*760c253cSXin Li            return 0
258*760c253cSXin Li
259*760c253cSXin Li        def FakeDownloadAutotestFiles(root, build_id):
260*760c253cSXin Li            if root or build_id:
261*760c253cSXin Li                pass
262*760c253cSXin Li            self.called_download_autotest_files = True
263*760c253cSXin Li            return "autotest"
264*760c253cSXin Li
265*760c253cSXin Li        def FakeDownloadDebugFile(root, build_id):
266*760c253cSXin Li            if root or build_id:
267*760c253cSXin Li                pass
268*760c253cSXin Li            self.called_download_debug_file = True
269*760c253cSXin Li            return "debug"
270*760c253cSXin Li
271*760c253cSXin Li        # Initialize downloader
272*760c253cSXin Li        downloader = download_images.ImageDownloader(logger_to_use=MOCK_LOGGER)
273*760c253cSXin Li
274*760c253cSXin Li        # Set downloader to call fake stubs.
275*760c253cSXin Li        downloader.GetBuildID = FakeGetBuildID
276*760c253cSXin Li        downloader.UncompressImage = FakeUncompressImage
277*760c253cSXin Li        downloader.DownloadImage = GoodDownloadImage
278*760c253cSXin Li        downloader.DownloadAutotestFiles = FakeDownloadAutotestFiles
279*760c253cSXin Li        downloader.DownloadDebugFile = FakeDownloadDebugFile
280*760c253cSXin Li
281*760c253cSXin Li        # Call Run.
282*760c253cSXin Li        image_path, autotest_path, debug_path = downloader.Run(
283*760c253cSXin Li            test_chroot,
284*760c253cSXin Li            test_build_id,
285*760c253cSXin Li            test_empty_autotest_path,
286*760c253cSXin Li            test_empty_debug_path,
287*760c253cSXin Li            download_debug,
288*760c253cSXin Li        )
289*760c253cSXin Li
290*760c253cSXin Li        # Make sure it called both _DownloadImage and _UncompressImage
291*760c253cSXin Li        self.assertTrue(self.called_download_image)
292*760c253cSXin Li        self.assertTrue(self.called_uncompress_image)
293*760c253cSXin Li        # Make sure it called DownloadAutotestFiles
294*760c253cSXin Li        self.assertTrue(self.called_download_autotest_files)
295*760c253cSXin Li        # Make sure it called DownloadDebugFile
296*760c253cSXin Li        self.assertTrue(self.called_download_debug_file)
297*760c253cSXin Li        # Make sure it returned an image and autotest path returned from this call
298*760c253cSXin Li        self.assertTrue(image_path == "chromiumos_test_image.bin")
299*760c253cSXin Li        self.assertTrue(autotest_path == "autotest")
300*760c253cSXin Li        self.assertTrue(debug_path == "debug")
301*760c253cSXin Li
302*760c253cSXin Li        # Call Run with a non-empty autotest and debug path
303*760c253cSXin Li        self.called_download_autotest_files = False
304*760c253cSXin Li        self.called_download_debug_file = False
305*760c253cSXin Li
306*760c253cSXin Li        image_path, autotest_path, debug_path = downloader.Run(
307*760c253cSXin Li            test_chroot,
308*760c253cSXin Li            test_build_id,
309*760c253cSXin Li            test_autotest_path,
310*760c253cSXin Li            test_debug_path,
311*760c253cSXin Li            download_debug,
312*760c253cSXin Li        )
313*760c253cSXin Li
314*760c253cSXin Li        # Verify that downloadAutotestFiles was not called
315*760c253cSXin Li        self.assertFalse(self.called_download_autotest_files)
316*760c253cSXin Li        # Make sure it returned the specified autotest path returned from this call
317*760c253cSXin Li        self.assertTrue(autotest_path == test_autotest_path)
318*760c253cSXin Li        # Make sure it returned the specified debug path returned from this call
319*760c253cSXin Li        self.assertTrue(debug_path == test_debug_path)
320*760c253cSXin Li
321*760c253cSXin Li        # Reset values; Now use fake stub that simulates DownloadImage failing.
322*760c253cSXin Li        self.called_download_image = False
323*760c253cSXin Li        self.called_uncompress_image = False
324*760c253cSXin Li        self.called_download_autotest_files = False
325*760c253cSXin Li        self.called_download_debug_file = False
326*760c253cSXin Li        downloader.DownloadImage = BadDownloadImage
327*760c253cSXin Li
328*760c253cSXin Li        # Call Run again.
329*760c253cSXin Li        self.assertRaises(
330*760c253cSXin Li            download_images.MissingImage,
331*760c253cSXin Li            downloader.Run,
332*760c253cSXin Li            test_chroot,
333*760c253cSXin Li            test_autotest_path,
334*760c253cSXin Li            test_debug_path,
335*760c253cSXin Li            test_build_id,
336*760c253cSXin Li            download_debug,
337*760c253cSXin Li        )
338*760c253cSXin Li
339*760c253cSXin Li        # Verify that UncompressImage and downloadAutotestFiles were not called,
340*760c253cSXin Li        # since _DownloadImage "failed"
341*760c253cSXin Li        self.assertTrue(self.called_download_image)
342*760c253cSXin Li        self.assertFalse(self.called_uncompress_image)
343*760c253cSXin Li        self.assertFalse(self.called_download_autotest_files)
344*760c253cSXin Li        self.assertFalse(self.called_download_debug_file)
345*760c253cSXin Li
346*760c253cSXin Li
347*760c253cSXin Liif __name__ == "__main__":
348*760c253cSXin Li    unittest.main()
349