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