# Lint as: python3 # Copyright 2016 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for glazier.lib.download.""" from absl import flags from absl.testing import absltest from absl.testing import flagsaver from pyfakefs import fake_filesystem from glazier.lib import beyondcorp from glazier.lib import buildinfo from glazier.lib import download import mock import six _TEST_INI = """ [BUILD] release=1.0 branch=stable """ FLAGS = flags.FLAGS SLEEP = 20 TEST_URL = 'https://www.example.com/build.yaml' class PathsTest(absltest.TestCase): def setUp(self): super(PathsTest, self).setUp() self.buildinfo = buildinfo.BuildInfo() @mock.patch.object(buildinfo.BuildInfo, 'ReleasePath', autospec=True) @mock.patch.object(buildinfo.BuildInfo, 'BinaryPath', autospec=True) def testTransform(self, binpath, relpath): # pylint: disable=line-too-long relpath.return_value = 'https://glazier' binpath.return_value = 'https://glazier/bin/' result = download.Transform(r'sccm.x86_64.1.00.1337\#13371337.exe', self.buildinfo) self.assertEqual(result, 'sccm.x86_64.1.00.1337#13371337.exe') result = download.Transform(r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -File #My-Script.ps1 -Verbose', self.buildinfo) self.assertEqual(result, r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -File https://glazier/My-Script.ps1 -Verbose') result = download.Transform(r'@Corp/install/1.0.0/installer.exe sccm.x86_64.1.00.1337\@13371337.exe', self.buildinfo) self.assertEqual(result, 'https://glazier/bin/Corp/install/1.0.0/installer.exe sccm.x86_64.1.00.1337@13371337.exe') result = download.Transform(r'C:\Windows\System32\msiexec.exe /i @Google/Chrome/1.3.3.7/googlechromestandaloneenterprise64.msi /qb /norestart', self.buildinfo) self.assertEqual(result, r'C:\Windows\System32\msiexec.exe /i https://glazier/bin/Google/Chrome/1.3.3.7/googlechromestandaloneenterprise64.msi /qb /norestart') result = download.Transform('nothing _ here', self.buildinfo) self.assertEqual(result, 'nothing _ here') def testPathCompile(self): result = download.PathCompile( self.buildinfo, file_name='file.txt', base='/tmp/base') self.assertEqual(result, '/tmp/base/file.txt') self.buildinfo._active_conf_path = ['sub', 'dir'] result = download.PathCompile( self.buildinfo, file_name='/file.txt', base='/tmp/base') self.assertEqual(result, '/tmp/base/sub/dir/file.txt') result = download.PathCompile( self.buildinfo, file_name='file.txt', base='/tmp/base') self.assertEqual(result, '/tmp/base/sub/dir/file.txt') self.buildinfo._active_conf_path = ['sub', 'dir/other', 'another/'] result = download.PathCompile( self.buildinfo, file_name='/file.txt', base='/tmp/') self.assertEqual(result, '/tmp/sub/dir/other/another/file.txt') class DownloadTest(absltest.TestCase): def setUp(self): super(DownloadTest, self).setUp() self._dl = download.BaseDownloader() # filesystem self.filesystem = fake_filesystem.FakeFilesystem() self.filesystem.CreateFile(r'C:\input.ini', contents=_TEST_INI) download.os = fake_filesystem.FakeOsModule(self.filesystem) download.open = fake_filesystem.FakeFileOpen(self.filesystem) def testConvertBytes(self): self.assertEqual(self._dl._ConvertBytes(123), '123.00B') self.assertEqual(self._dl._ConvertBytes(23455), '22.91KB') self.assertEqual(self._dl._ConvertBytes(3455555), '3.30MB') self.assertEqual(self._dl._ConvertBytes(456555555), '435.41MB') self.assertEqual(self._dl._ConvertBytes(56755555555), '52.86GB') self.assertEqual(self._dl._ConvertBytes(6785555555555), '6.17TB') @mock.patch.object(download.logging, 'info', autospec=True) @mock.patch.object(download.time, 'sleep', autospec=True) def testAttemptResource(self, sleep, i): self._dl._AttemptResource(1, 2, 'file download') i.assert_called_with('Failed attempt %d of %d: Sleeping for %d second(s) ' 'before retrying the %s.', 1, 2, 20, 'file download') sleep.assert_called_with(20) @mock.patch.object(download.logging, 'info', autospec=True) @mock.patch.object(download.time, 'sleep', autospec=True) def testAttemptResourceUnlimited(self, sleep, i): self._dl._AttemptResource(1, -1, 'file download') i.assert_called_with( 'Failed attempt %d of Unlimited: Sleeping for %d second(s) before ' 'retrying the %s.', 1, 20, 'file download') sleep.assert_called_with(20) def testAttemptResourceError(self): self.assertRaises(download.DownloadError, self._dl._AttemptResource, 1, 1, 'file download') @mock.patch.object(download.winpe, 'check_winpe', autospec=True) @mock.patch.object(download.urllib.request, 'urlopen', autospec=True) def testOpenStreamInternal(self, urlopen, wpe): file_stream = mock.Mock() file_stream.getcode.return_value = 200 url = TEST_URL httperr = download.urllib.error.HTTPError('Error', None, None, None, None) urlerr = download.urllib.error.URLError('Error') wpe.return_value = False # 200 urlopen.side_effect = iter([httperr, urlerr, file_stream]) res = self._dl._OpenStream(url, max_retries=4) self.assertEqual(res, file_stream) # Invalid URL with self.assertRaisesRegex(download.DownloadError, 'Invalid remote server URL*'): self._dl._OpenStream('not_a_real_url', max_retries=0) # 404 file_stream.getcode.return_value = 404 urlopen.side_effect = iter([httperr, file_stream]) self.assertRaises(download.DownloadError, self._dl._OpenStream, url) # retries file_stream.getcode.return_value = 200 urlopen.side_effect = iter([httperr, httperr, file_stream]) self.assertRaises(download.DownloadError, self._dl._OpenStream, url, max_retries=2) @mock.patch.object(download.winpe, 'check_winpe', autospec=True) @mock.patch.object(download.urllib.request, 'urlopen', autospec=True) def testCheckUrl(self, urlopen, wpe): file_stream = mock.Mock() file_stream.getcode.return_value = 200 wpe.return_value = False # match urlopen.side_effect = iter([file_stream]) self.assertTrue(self._dl.CheckUrl(TEST_URL, status_codes=[200])) # miss urlopen.side_effect = iter([file_stream]) self.assertFalse( self._dl.CheckUrl(TEST_URL, max_retries=1, status_codes=[201])) @mock.patch.object(download.BaseDownloader, '_StreamToDisk', autospec=True) @mock.patch.object(download.BaseDownloader, '_OpenStream', autospec=True) @mock.patch.object(download.tempfile, 'NamedTemporaryFile', autospec=True) @mock.patch.object(beyondcorp.BeyondCorp, 'CheckBeyondCorp', autospec=True) def testDownloadFileTemp(self, cbc, tempf, downf, todisk): cbc.return_value = False url = TEST_URL path = r'C:\Windows\Temp\tmpblahblah' tempf.return_value.name = path self._dl.DownloadFileTemp(url, max_retries=5) downf.assert_called_with(self._dl, url, 5) todisk.assert_called_with(self._dl, downf.return_value, False) self.assertEqual(self._dl._save_location, path) self._dl.DownloadFileTemp(url, max_retries=5, show_progress=True) downf.assert_called_with(self._dl, url, 5) todisk.assert_called_with(self._dl, downf.return_value, True) self._dl.DownloadFileTemp(url, max_retries=5, show_progress=False) downf.assert_called_with(self._dl, url, 5) todisk.assert_called_with(self._dl, downf.return_value, False) @mock.patch.object(download.BaseDownloader, '_StoreDebugInfo', autospec=True) def testStreamToDisk(self, store_info): # setup http_stream = six.BytesIO() http_stream.write(b'First line.\nSecond line.\n') http_stream.seek(0) download.CHUNK_BYTE_SIZE = 5 file_stream = mock.Mock() file_stream.getcode.return_value = 200 file_stream.geturl.return_value = TEST_URL file_stream.headers.get = lambda x: {'Content-Length': '25'}[x] file_stream.read = http_stream.read # success self._dl._save_location = r'C:\download.txt' self._dl._StreamToDisk(file_stream) # Progress with mock.patch.object( self._dl, '_DownloadChunkReport', autospec=True) as report: # default false self._dl._default_show_progress = False http_stream.seek(0) self._dl._StreamToDisk(file_stream) self.assertFalse(report.called) # override true http_stream.seek(0) report.reset_mock() self._dl._StreamToDisk(file_stream, show_progress=True) self.assertTrue(report.called) # default true self._dl._default_show_progress = True http_stream.seek(0) report.reset_mock() self._dl._StreamToDisk(file_stream) self.assertTrue(report.called) # override false http_stream.seek(0) report.reset_mock() self._dl._StreamToDisk(file_stream, show_progress=False) self.assertFalse(report.called) # HTTP Header returned nothing (NoneType) http_stream.seek(0) report.reset_mock() self.assertRaises(download.DownloadError, self._dl._StreamToDisk, None) # IOError http_stream.seek(0) self.filesystem.CreateDirectory(r'C:\Windows') self._dl._save_location = r'C:\Windows' self.assertRaises(download.DownloadError, self._dl._StreamToDisk, file_stream) # File Size http_stream.seek(0) file_stream.headers.get = lambda x: {'Content-Length': '100000'}[x] self._dl._save_location = r'C:\download.txt' self.assertRaises(download.DownloadError, self._dl._StreamToDisk, file_stream) # Socket Error http_stream.seek(0) file_stream.headers.get = lambda x: {'Content-Length': '25'}[x] file_stream.read = mock.Mock(side_effect=download.socket.error('SocketErr')) self.assertRaises(download.DownloadError, self._dl._StreamToDisk, file_stream) store_info.assert_called_with(self._dl, file_stream, 'SocketErr') # Retries http_stream.seek(0) file_stream.headers.get = lambda x: {'Content-Length': '100000'}[x] self._dl._save_location = r'C:\download.txt' self.assertRaises(download.DownloadError, self._dl._StreamToDisk, file_stream) @mock.patch.object(download.BaseDownloader, '_StoreDebugInfo', autospec=True) def testValidate(self, store_info): file_stream = mock.Mock() self._dl._save_location = r'C:\missing.txt' self.assertRaises(download.DownloadError, self._dl._Validate, file_stream, 200) store_info.assert_called_with(self._dl, file_stream) def testVerifyShaHash(self): test_sha256 = ( '58157BF41CE54731C0577F801035D47EC20ED16A954F10C29359B8ADEDCAE800') # sha256 result = self._dl.VerifyShaHash(r'C:\input.ini', test_sha256) self.assertTrue(result) # missing source result = self._dl.VerifyShaHash(r'C:\missing.ini', test_sha256) self.assertFalse(result) # missing hash result = self._dl.VerifyShaHash(r'C:\input.ini', '') self.assertFalse(result) # mismatch hash test_sha256 = ( '58157bf41ce54731c0577f801035d47ec20ed16a954f10c29359b8adedcae801') result = self._dl.VerifyShaHash(r'C:\input.ini', test_sha256) self.assertFalse(result) if __name__ == '__main__': absltest.main()