| #!/usr/bin/python |
| # -*- coding: utf-8 -*- |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module containing unittests for the image_extractor module.""" |
| |
| import logging |
| import mox |
| import os |
| import shutil |
| import sys |
| import tempfile |
| import unittest |
| import zipfile |
| |
| import constants |
| sys.path.append(constants.SOURCE_ROOT) |
| |
| import image_extractor |
| |
| |
| class ImageExtractorTest(mox.MoxTestBase): |
| """Testing all'm image extractors.""" |
| def setUp(self): |
| super(ImageExtractorTest, self).setUp() |
| |
| self.work_dir = tempfile.mkdtemp('ImageExtractorTest') |
| self.board = 'x86-generic-full' |
| # Set constants to be easily testable. |
| self.archive_dir = os.path.join(self.work_dir, 'archive', self.board) |
| image_extractor.ImageExtractor.SRC_ARCHIVE_DIR = os.path.join(self.work_dir, |
| 'src') |
| |
| # Our test object. |
| self.test_extractor = image_extractor.ImageExtractor(self.archive_dir) |
| |
| # Convenience variables for testing. |
| self.src_archive = image_extractor.ImageExtractor.SRC_ARCHIVE_DIR |
| self.image = image_extractor.ImageExtractor.IMAGE_TO_EXTRACT |
| self.mox.StubOutWithMock(logging, 'error') |
| |
| def tearDown(self): |
| shutil.rmtree(self.work_dir) |
| |
| def _TouchImageZip(self, directory, add_image=True): |
| """Creates a psuedo image.zip file to unzip.""" |
| if not os.path.exists(directory): |
| os.makedirs(directory) |
| |
| with zipfile.ZipFile(os.path.join(directory, 'image.zip'), 'w') as z: |
| # Zip file can't be empty so add a dummy file first. |
| dummy_file = os.path.join(directory, 'TACOS_ARE_DELCIOUS.txt') |
| with open(dummy_file, 'w') as f: |
| f.write('tacos') |
| |
| z.write(dummy_file) |
| |
| if add_image: |
| image_path = os.path.join(directory, self.image) |
| archive_path = self.image |
| # Create a dummy image file. |
| with open(image_path, 'w') as f: |
| f.write('ooga booga') |
| |
| z.write(image_path, archive_path) |
| |
| def CreateFakeArchiveDir(self, number_of_entries, add_build_number=False): |
| """Creates a fake archive dir with specified number of entries.""" |
| # Create local board directory e.g. /var/archive/x86-generic-full. |
| os.makedirs(self.archive_dir) |
| # Create a latest file. |
| open(os.path.join(self.archive_dir, 'LATEST'), 'w').close() |
| version_s = 'R16-158.0.0-a1-b%d' if add_build_number else 'R16-158.0.%d-a1' |
| # Create specified number of entries. |
| for i in range(number_of_entries): |
| new_dir = os.path.join(self.archive_dir, version_s % i) |
| os.makedirs(new_dir) |
| self._TouchImageZip(new_dir) |
| |
| def testGetLatestImageWithNoEntries(self): |
| """Should return None if the directory has no entries.""" |
| self.CreateFakeArchiveDir(0) |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.11-a1') |
| self.assertEqual(latest_image, None) |
| |
| def testGetLatestImageWithOldEntry(self): |
| """Compatibility testing. GetLatestImage should ignore old style entries. |
| """ |
| self.CreateFakeArchiveDir(0) |
| os.makedirs(os.path.join(self.archive_dir, '0-158.0.1-a1')) |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.11-a1') |
| self.assertEqual(latest_image, None) |
| |
| def testGetLatestImageWithBuildEntries(self): |
| """The normal case with build#'s. Return the path to the highest entry. |
| |
| Test both ways to mix version strings with and without build numbers. We |
| generate R16-158.0.0-a1-b[0-10] in the local archive and test again the |
| target version R16-158.0.1-a1 and R16-158.0.0-a1-b11. These both should |
| return R16-158.0.0-a1-b10. |
| """ |
| self.CreateFakeArchiveDir(11, add_build_number=True) |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.1-a1') |
| self.assertEqual(os.path.basename(latest_image), 'R16-158.0.0-a1-b10') |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.0-a1-b11') |
| self.assertEqual(os.path.basename(latest_image), 'R16-158.0.0-a1-b10') |
| |
| def testGetLatestImageWithEntries(self): |
| """The normal case. Return the path to the highest entry.""" |
| self.CreateFakeArchiveDir(11) |
| # Throw in a bad directory for good measure. |
| os.makedirs(os.path.join(self.archive_dir, '0-158.0.1-a1')) |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.11-a1') |
| self.assertEqual(os.path.basename(latest_image), 'R16-158.0.10-a1') |
| |
| def testGetLatestImageWithEntriesAndTarget(self): |
| """The normal case but we pass in a target_version. |
| |
| Returns the path to the highest entry before target and spits out a |
| logging error saying that 10 is too high. |
| """ |
| self.CreateFakeArchiveDir(11) |
| os.makedirs(os.path.join(self.archive_dir, 'R16-158.0.9-a1-b123')) |
| logging.error(mox.IgnoreArg(), 'R16-158.0.10-a1') |
| self.mox.ReplayAll() |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.9-a1') |
| self.assertEqual(os.path.basename(latest_image), 'R16-158.0.8-a1') |
| self.mox.VerifyAll() |
| |
| def testUnzipImageArchiveAlready(self): |
| """Ensure we create a new archive and delete the old one.""" |
| old_entry = os.path.join(self.src_archive, self.board, 'R16-158.0.0-a1') |
| os.makedirs(old_entry) |
| new_entry = os.path.join(self.src_archive, self.board, 'R16-158.0.1-a1') |
| archived_image_dir = os.path.join(self.archive_dir, 'R16-158.0.1-a1') |
| self._TouchImageZip(archived_image_dir) |
| |
| self.mox.ReplayAll() |
| expected_image = os.path.join(new_entry, self.image) |
| image_returned = self.test_extractor.UnzipImage(archived_image_dir) |
| self.mox.VerifyAll() |
| self.assertFalse(os.path.exists(old_entry)) |
| self.assertTrue(os.path.exists(new_entry)) |
| self.assertEqual(expected_image, image_returned) |
| |
| def testUnzipImageNoArchive(self): |
| """Ensure we create a new archive with none before.""" |
| new_entry = os.path.join(self.src_archive, self.board, 'R16-158.0.1-a1') |
| archived_image_dir = os.path.join(self.archive_dir, 'R16-158.0.1-a1') |
| self._TouchImageZip(archived_image_dir) |
| |
| self.mox.ReplayAll() |
| expected_image = os.path.join(new_entry, self.image) |
| image_returned = self.test_extractor.UnzipImage(archived_image_dir) |
| self.mox.VerifyAll() |
| self.assertTrue(os.path.exists(new_entry)) |
| self.assertEqual(expected_image, image_returned) |
| |
| def testUnzipImageMissingImage(self): |
| """Ensure that we return None when the expected image is missing.""" |
| new_entry = os.path.join(self.src_archive, self.board, 'R16-158.0.1-a1') |
| archived_image_dir = os.path.join(self.archive_dir, 'R16-158.0.1-a1') |
| # Don't actually add the image.bin! |
| self._TouchImageZip(archived_image_dir, add_image=False) |
| |
| self.mox.ReplayAll() |
| self.assertEqual(self.test_extractor.UnzipImage(archived_image_dir), None) |
| self.mox.VerifyAll() |
| self.assertTrue(os.path.exists(new_entry)) |
| |
| def testBadZipImageArchive(self): |
| """Ensure we ignore corrupt archives.""" |
| |
| # Create valid archive followed by corrupt one. |
| self.CreateFakeArchiveDir(2, add_build_number=True) |
| bad_zip_name = os.path.join( |
| self.archive_dir, 'R16-158.0.0-a1-b1', 'image.zip') |
| with open(bad_zip_name, 'w') as f: |
| f.write('oogabooga') |
| |
| # This is normally mox'd out to ensure it's never called, but we expect it. |
| logging.error('Version in archive dir is corrupt: %s', 'R16-158.0.0-a1-b1') |
| |
| self.mox.ReplayAll() |
| |
| # Ensure we fine the first one (valid), not the second (corrupt) |
| latest_image = self.test_extractor.GetLatestImage('R16-158.0.1-a1') |
| self.assertEqual(os.path.basename(latest_image), 'R16-158.0.0-a1-b0') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |