#!/usr/bin/python2.4 # # Copyright (C) 2008 Google Inc. # # 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 divide_and_compress.py. TODO(jmatt): Add tests for module methods. """ __author__ = 'jmatt@google.com (Justin Mattson)' import os import stat import unittest import zipfile import divide_and_compress import mox class BagOfParts(object): """Just a generic class that I can use to assign random attributes to.""" def NoOp(self): x = 1 class ValidAndRemoveTests(unittest.TestCase): """Test the ArchiveIsValid and RemoveLastFile methods.""" def setUp(self): """Prepare the test. Construct some mock objects for use with the tests. """ self.my_mox = mox.Mox() file1 = BagOfParts() file1.filename = 'file1.txt' file1.contents = 'This is a test file' file2 = BagOfParts() file2.filename = 'file2.txt' file2.contents = ('akdjfk;djsf;kljdslkfjslkdfjlsfjkdvn;kn;2389rtu4i' 'tn;ghf8:89H*hp748FJw80fu9WJFpwf39pujens;fihkhjfk' 'sdjfljkgsc n;iself') self.files = {'file1': file1, 'file2': file2} def tearDown(self): """Remove any stubs we've created.""" self.my_mox.UnsetStubs() def testArchiveIsValid(self): """Test the DirectoryZipper.ArchiveIsValid method. Run two tests, one that we expect to pass and one that we expect to fail """ test_file_size = 1056730 self.my_mox.StubOutWithMock(os, 'stat') os.stat('/foo/0.zip').AndReturn([test_file_size]) self.my_mox.StubOutWithMock(stat, 'ST_SIZE') stat.ST_SIZE = 0 os.stat('/baz/0.zip').AndReturn([test_file_size]) mox.Replay(os.stat) test_target = divide_and_compress.DirectoryZipper('/foo/', 'bar', test_file_size - 1, True) self.assertEqual(False, test_target.ArchiveIsValid(), msg=('ERROR: Test failed, ArchiveIsValid should have ' 'returned false, but returned true')) test_target = divide_and_compress.DirectoryZipper('/baz/', 'bar', test_file_size + 1, True) self.assertEqual(True, test_target.ArchiveIsValid(), msg=('ERROR: Test failed, ArchiveIsValid should have' ' returned true, but returned false')) def testRemoveLastFile(self): """Test DirectoryZipper.RemoveLastFile method. Construct a ZipInfo mock object with two records, verify that write is only called once on the new ZipFile object. """ source = self.CreateZipSource() dest = self.CreateZipDestination() source_path = ''.join([os.getcwd(), '/0-old.zip']) dest_path = ''.join([os.getcwd(), '/0.zip']) test_target = divide_and_compress.DirectoryZipper( ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') test_target.OpenZipFileAtPath(source_path, mode='r').AndReturn(source) test_target.OpenZipFileAtPath(dest_path, compress=zipfile.ZIP_DEFLATED, mode='w').AndReturn(dest) self.my_mox.StubOutWithMock(os, 'rename') os.rename(dest_path, source_path) self.my_mox.StubOutWithMock(os, 'unlink') os.unlink(source_path) self.my_mox.ReplayAll() test_target.RemoveLastFile() self.my_mox.VerifyAll() def CreateZipSource(self): """Create a mock zip sourec object. Read should only be called once, because the second file is the one being removed. Returns: A configured mocked """ source_zip = self.my_mox.CreateMock(zipfile.ZipFile) source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']]) source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']]) source_zip.read(self.files['file1'].filename).AndReturn( self.files['file1'].contents) source_zip.close() return source_zip def CreateZipDestination(self): """Create mock destination zip. Write should only be called once, because there are two files in the source zip and we expect the second to be removed. Returns: A configured mocked """ dest_zip = mox.MockObject(zipfile.ZipFile) dest_zip.writestr(self.files['file1'].filename, self.files['file1'].contents) dest_zip.close() return dest_zip class FixArchiveTests(unittest.TestCase): """Tests for the DirectoryZipper.FixArchive method.""" def setUp(self): """Create a mock file object.""" self.my_mox = mox.Mox() self.file1 = BagOfParts() self.file1.filename = 'file1.txt' self.file1.contents = 'This is a test file' def tearDown(self): """Unset any mocks that we've created.""" self.my_mox.UnsetStubs() def _InitMultiFileData(self): """Create an array of mock file objects. Create three mock file objects that we can use for testing. """ self.multi_file_dir = [] file1 = BagOfParts() file1.filename = 'file1.txt' file1.contents = 'kjaskl;jkdjfkja;kjsnbvjnvnbuewklriujalvjsd' self.multi_file_dir.append(file1) file2 = BagOfParts() file2.filename = 'file2.txt' file2.contents = ('He entered the room and there in the center, it was.' ' Looking upon the thing, suddenly he could not remember' ' whether he had actually seen it before or whether' ' his memory of it was merely the effect of something' ' so often being imagined that it had long since become ' ' manifest in his mind.') self.multi_file_dir.append(file2) file3 = BagOfParts() file3.filename = 'file3.txt' file3.contents = 'Whoa, what is \'file2.txt\' all about?' self.multi_file_dir.append(file3) def testSingleFileArchive(self): """Test behavior of FixArchive when the archive has a single member. We expect that when this method is called with an archive that has a single member that it will return False and unlink the archive. """ test_target = divide_and_compress.DirectoryZipper( ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') test_target.OpenZipFileAtPath( ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn( self.CreateSingleFileMock()) self.my_mox.StubOutWithMock(os, 'unlink') os.unlink(''.join([os.getcwd(), '/0.zip'])) self.my_mox.ReplayAll() self.assertEqual(False, test_target.FixArchive('SIZE')) self.my_mox.VerifyAll() def CreateSingleFileMock(self): """Create a mock ZipFile object for testSingleFileArchive. We just need it to return a single member infolist twice Returns: A configured mock object """ mock_zip = self.my_mox.CreateMock(zipfile.ZipFile) mock_zip.infolist().AndReturn([self.file1]) mock_zip.infolist().AndReturn([self.file1]) mock_zip.close() return mock_zip def testMultiFileArchive(self): """Test behavior of DirectoryZipper.FixArchive with a multi-file archive. We expect that FixArchive will rename the old archive, adding '-old' before '.zip', read all the members except the last one of '-old' into a new archive with the same name as the original, and then unlink the '-old' copy """ test_target = divide_and_compress.DirectoryZipper( ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') test_target.OpenZipFileAtPath( ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn( self.CreateMultiFileMock()) self.my_mox.StubOutWithMock(test_target, 'RemoveLastFile') test_target.RemoveLastFile(''.join([os.getcwd(), '/0.zip'])) self.my_mox.StubOutWithMock(os, 'stat') os.stat(''.join([os.getcwd(), '/0.zip'])).AndReturn([49302]) self.my_mox.StubOutWithMock(stat, 'ST_SIZE') stat.ST_SIZE = 0 self.my_mox.ReplayAll() self.assertEqual(True, test_target.FixArchive('SIZE')) self.my_mox.VerifyAll() def CreateMultiFileMock(self): """Create mock ZipFile object for use with testMultiFileArchive. The mock just needs to return the infolist mock that is prepared in InitMultiFileData() Returns: A configured mock object """ self._InitMultiFileData() mock_zip = self.my_mox.CreateMock(zipfile.ZipFile) mock_zip.infolist().AndReturn(self.multi_file_dir) mock_zip.close() return mock_zip class AddFileToArchiveTest(unittest.TestCase): """Test behavior of method to add a file to an archive.""" def setUp(self): """Setup the arguments for the DirectoryZipper object.""" self.my_mox = mox.Mox() self.output_dir = '%s/' % os.getcwd() self.file_to_add = 'file.txt' self.input_dir = '/foo/bar/baz/' def tearDown(self): self.my_mox.UnsetStubs() def testAddFileToArchive(self): """Test the DirectoryZipper.AddFileToArchive method. We are testing a pretty trivial method, we just expect it to look at the file its adding, so that it possible can through out a warning. """ test_target = divide_and_compress.DirectoryZipper(self.output_dir, self.input_dir, 1024*1024, True) self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') archive_mock = self.CreateArchiveMock() test_target.OpenZipFileAtPath( ''.join([self.output_dir, '0.zip']), compress=zipfile.ZIP_DEFLATED).AndReturn(archive_mock) self.StubOutOsModule() self.my_mox.ReplayAll() test_target.AddFileToArchive(''.join([self.input_dir, self.file_to_add]), zipfile.ZIP_DEFLATED) self.my_mox.VerifyAll() def StubOutOsModule(self): """Create a mock for the os.path and os.stat objects. Create a stub that will return the type (file or directory) and size of the object that is to be added. """ self.my_mox.StubOutWithMock(os.path, 'isfile') os.path.isfile(''.join([self.input_dir, self.file_to_add])).AndReturn(True) self.my_mox.StubOutWithMock(os, 'stat') os.stat(''.join([self.input_dir, self.file_to_add])).AndReturn([39480]) self.my_mox.StubOutWithMock(stat, 'ST_SIZE') stat.ST_SIZE = 0 def CreateArchiveMock(self): """Create a mock ZipFile for use with testAddFileToArchive. Just verify that write is called with the file we expect and that the archive is closed after the file addition Returns: A configured mock object """ archive_mock = self.my_mox.CreateMock(zipfile.ZipFile) archive_mock.write(''.join([self.input_dir, self.file_to_add]), self.file_to_add) archive_mock.close() return archive_mock class CompressDirectoryTest(unittest.TestCase): """Test the master method of the class. Testing with the following directory structure. /dir1/ /dir1/file1.txt /dir1/file2.txt /dir1/dir2/ /dir1/dir2/dir3/ /dir1/dir2/dir4/ /dir1/dir2/dir4/file3.txt /dir1/dir5/ /dir1/dir5/file4.txt /dir1/dir5/file5.txt /dir1/dir5/file6.txt /dir1/dir5/file7.txt /dir1/dir6/ /dir1/dir6/file8.txt file1.txt., file2.txt, file3.txt should be in 0.zip file4.txt should be in 1.zip file5.txt, file6.txt should be in 2.zip file7.txt will not be stored since it will be too large compressed file8.txt should b in 3.zip """ def setUp(self): """Setup all the mocks for this test.""" self.my_mox = mox.Mox() self.base_dir = '/dir1' self.output_path = '/out_dir/' self.test_target = divide_and_compress.DirectoryZipper( self.output_path, self.base_dir, 1024*1024, True) self.InitArgLists() self.InitOsDotPath() self.InitArchiveIsValid() self.InitWriteIndexRecord() self.InitAddFileToArchive() def tearDown(self): self.my_mox.UnsetStubs() def testCompressDirectory(self): """Test the DirectoryZipper.CompressDirectory method.""" self.my_mox.ReplayAll() for arguments in self.argument_lists: self.test_target.CompressDirectory(None, arguments[0], arguments[1]) self.my_mox.VerifyAll() def InitAddFileToArchive(self): """Setup mock for DirectoryZipper.AddFileToArchive. Make sure that the files are added in the order we expect. """ self.my_mox.StubOutWithMock(self.test_target, 'AddFileToArchive') self.test_target.AddFileToArchive('/dir1/file1.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/file2.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir2/dir4/file3.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file4.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file4.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file5.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file5.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file6.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file7.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir5/file7.txt', zipfile.ZIP_DEFLATED) self.test_target.AddFileToArchive('/dir1/dir6/file8.txt', zipfile.ZIP_DEFLATED) def InitWriteIndexRecord(self): """Setup mock for DirectoryZipper.WriteIndexRecord.""" self.my_mox.StubOutWithMock(self.test_target, 'WriteIndexRecord') # we are trying to compress 8 files, but we should only attempt to # write an index record 7 times, because one file is too large to be stored self.test_target.WriteIndexRecord().AndReturn(True) self.test_target.WriteIndexRecord().AndReturn(False) self.test_target.WriteIndexRecord().AndReturn(False) self.test_target.WriteIndexRecord().AndReturn(True) self.test_target.WriteIndexRecord().AndReturn(True) self.test_target.WriteIndexRecord().AndReturn(False) self.test_target.WriteIndexRecord().AndReturn(True) def InitArchiveIsValid(self): """Mock out DirectoryZipper.ArchiveIsValid and DirectoryZipper.FixArchive. Mock these methods out such that file1, file2, and file3 go into one archive. file4 then goes into the next archive, file5 and file6 in the next, file 7 should appear too large to compress into an archive, and file8 goes into the final archive """ self.my_mox.StubOutWithMock(self.test_target, 'ArchiveIsValid') self.my_mox.StubOutWithMock(self.test_target, 'FixArchive') self.test_target.ArchiveIsValid().AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(True) # should be file4.txt self.test_target.ArchiveIsValid().AndReturn(False) self.test_target.FixArchive('SIZE').AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(True) # should be file5.txt self.test_target.ArchiveIsValid().AndReturn(False) self.test_target.FixArchive('SIZE').AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(True) # should be file7.txt self.test_target.ArchiveIsValid().AndReturn(False) self.test_target.FixArchive('SIZE').AndReturn(True) self.test_target.ArchiveIsValid().AndReturn(False) self.test_target.FixArchive('SIZE').AndReturn(False) self.test_target.ArchiveIsValid().AndReturn(True) def InitOsDotPath(self): """Mock out os.path.isfile. Mock this out so the things we want to appear as files appear as files and the things we want to appear as directories appear as directories. Also make sure that the order of file visits is as we expect (which is why InAnyOrder isn't used here). """ self.my_mox.StubOutWithMock(os.path, 'isfile') os.path.isfile('/dir1/dir2').AndReturn(False) os.path.isfile('/dir1/dir5').AndReturn(False) os.path.isfile('/dir1/dir6').AndReturn(False) os.path.isfile('/dir1/file1.txt').AndReturn(True) os.path.isfile('/dir1/file2.txt').AndReturn(True) os.path.isfile('/dir1/dir2/dir3').AndReturn(False) os.path.isfile('/dir1/dir2/dir4').AndReturn(False) os.path.isfile('/dir1/dir2/dir4/file3.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file6.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True) os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True) os.path.isfile('/dir1/dir6/file8.txt').AndReturn(True) def InitArgLists(self): """Create the directory path => directory contents mappings.""" self.argument_lists = [] self.argument_lists.append(['/dir1', ['file1.txt', 'file2.txt', 'dir2', 'dir5', 'dir6']]) self.argument_lists.append(['/dir1/dir2', ['dir3', 'dir4']]) self.argument_lists.append(['/dir1/dir2/dir3', []]) self.argument_lists.append(['/dir1/dir2/dir4', ['file3.txt']]) self.argument_lists.append(['/dir1/dir5', ['file4.txt', 'file5.txt', 'file6.txt', 'file7.txt']]) self.argument_lists.append(['/dir1/dir6', ['file8.txt']]) if __name__ == '__main__': unittest.main()