#!/usr/bin/env ruby $VERBOSE = true require 'rubyunit' require 'zip' include Zip Dir.chdir "test" class AbstractInputStreamTest < RUNIT::TestCase # AbstractInputStream subclass that provides a read method TEST_LINES = [ "Hello world#{$/}", "this is the second line#{$/}", "this is the last line"] TEST_STRING = TEST_LINES.join class TestAbstractInputStream include AbstractInputStream def initialize(aString) @contents = aString @readPointer = 0 end def read(charsToRead) retVal=@contents[@readPointer, charsToRead] @readPointer+=charsToRead return retVal end def produceInput read(100) end def inputFinished? @contents[@readPointer] == nil end end def setup @io = TestAbstractInputStream.new(TEST_STRING) end def test_gets assert_equals(TEST_LINES[0], @io.gets) assert_equals(TEST_LINES[1], @io.gets) assert_equals(TEST_LINES[2], @io.gets) assert_equals(nil, @io.gets) end def test_getsMultiCharSeperator assert_equals("Hell", @io.gets("ll")) assert_equals("o world#{$/}this is the second l", @io.gets("d l")) end def test_each_line lineNumber=0 @io.each_line { |line| assert_equals(TEST_LINES[lineNumber], line) lineNumber+=1 } end def test_readlines assert_equals(TEST_LINES, @io.readlines) end def test_readline test_gets begin @io.readline fail "EOFError expected" rescue EOFError end end end class ZipEntryTest < RUNIT::TestCase TEST_ZIPFILE = "someZipFile.zip" TEST_COMMENT = "a comment" TEST_COMPRESSED_SIZE = 1234 TEST_CRC = 325324 TEST_EXTRA = "Some data here" TEST_COMPRESSIONMETHOD = ZipEntry::DEFLATED TEST_NAME = "entry name" TEST_SIZE = 8432 TEST_ISDIRECTORY = false def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, TEST_NAME, TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, TEST_COMPRESSIONMETHOD, TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) assert_equals(TEST_CRC, entry.crc) assert_equals(TEST_EXTRA, entry.extra) assert_equals(TEST_COMPRESSIONMETHOD, entry.compressionMethod) assert_equals(TEST_NAME, entry.name) assert_equals(TEST_SIZE, entry.size) assert_equals(TEST_ISDIRECTORY, entry.isDirectory) end def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 123, 1234, ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 1234, ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) assert(entry2 != entry3) assert(entry3 != entry4) assert(entry4 != entry5) assert(entry5 != entry6) assert(entry6 != entry7) assert(entry7 != entry8) assert(entry7 != "hello") assert(entry7 != 12) end end module IOizeString attr_reader :tell def read(count = nil) @tell ||= 0 count = size unless count retVal = slice(@tell, count) @tell += count return retVal end def seek(index, offset) @tell ||= 0 case offset when IO::SEEK_END newPos = size + index when IO::SEEK_SET newPos = index when IO::SEEK_CUR newPos = @tell + index else raise "Error in test method IOizeString::seek" end if (newPos < 0 || newPos >= size) raise Errno::EINVAL else @tell=newPos end end def reset @tell = 0 end end class ZipLocalEntryTest < RUNIT::TestCase def test_readLocalEntryHeaderOfFirstTestZipEntry File.open(TestZipFile::TEST_ZIP3.zipName) { |file| entry = ZipEntry.readLocalEntry(file) assert_equal("", entry.comment) # Differs from windows and unix because of CR LF # assert_equal(480, entry.compressedSize) # assert_equal(0x2a27930f, entry.crc) # extra field is 21 bytes long # probably contains some unix attrutes or something # disabled: assert_equal(nil, entry.extra) assert_equal(ZipEntry::DEFLATED, entry.compressionMethod) assert_equal(TestZipFile::TEST_ZIP3.entryNames[0], entry.name) assert_equal(File.size(TestZipFile::TEST_ZIP3.entryNames[0]), entry.size) assert(! entry.isDirectory) } end def test_readLocalEntryFromNonZipFile File.open("ziptest.rb") { |file| assert_equals(nil, ZipEntry.readLocalEntry(file)) } end def test_readLocalEntryFromTruncatedZipFile zipFragment="" File.open(TestZipFile::TEST_ZIP2.zipName) { |f| zipFragment = f.read(12) } # local header is at least 30 bytes zipFragment.extend(IOizeString).reset entry = ZipEntry.new entry.readLocalEntry(zipFragment) fail "ZipError expected" rescue ZipError end def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", "thisIsSomeExtraInformation", 100, 987654, ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) compareCDirEntryHeaders(entry, entryReadCentral) end private def compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.compressedSize , entry2.compressedSize) assert_equals(entry1.crc , entry2.crc) assert_equals(entry1.extra , entry2.extra) assert_equals(entry1.compressionMethod, entry2.compressionMethod) assert_equals(entry1.name , entry2.name) assert_equals(entry1.size , entry2.size) assert_equals(entry1.localHeaderOffset, entry2.localHeaderOffset) end def compareCDirEntryHeaders(entry1, entry2) compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.comment, entry2.comment) end def writeToFile(localFileName, centralFileName, entry) File.open(localFileName, "wb") { |f| entry.writeLocalEntry(f) } File.open(centralFileName, "wb") { |f| entry.writeCDirEntry(f) } end def readFromFile(localFileName, centralFileName) localEntry = nil cdirEntry = nil File.open(localFileName, "rb") { |f| localEntry = ZipEntry.readLocalEntry(f) } File.open(centralFileName, "rb") { |f| cdirEntry = ZipEntry.readCDirEntry(f) } return [localEntry, cdirEntry] end end module DecompressorTests # expects @refText and @decompressor def test_readEverything assert_equals(@refText, @decompressor.read) end def test_readInChunks chunkSize = 5 while (decompressedChunk = @decompressor.read(chunkSize)) assert_equals(@refText.slice!(0, chunkSize), decompressedChunk) end assert_equals(0, @refText.size) end end class InflaterTest < RUNIT::TestCase include DecompressorTests def setup @file = File.new("file1.txt.deflatedData", "rb") @refText="" File.open("file1.txt") { |f| @refText = f.read } @decompressor = Inflater.new(@file) end def teardown @file.close end end class PassThruDecompressorTest < RUNIT::TestCase include DecompressorTests TEST_FILE="file1.txt" def setup @file = File.new(TEST_FILE) @refText="" File.open(TEST_FILE) { |f| @refText = f.read } @decompressor = PassThruDecompressor.new(@file, File.size(TEST_FILE)) end def teardown @file.close end end module AssertEntry def assertNextEntry(filename, zis) assertEntry(filename, zis, zis.getNextEntry.name) end def assertEntry(filename, zis, entryName) assert_equals(filename, entryName) assertEntryContentsForStream(filename, zis, entryName) end def assertEntryContentsForStream(filename, zis, entryName) File.open(filename, "rb") { |file| expected = file.read actual = zis.read if (expected != actual) if (expected.length > 400 || actual.length > 400) zipEntryFilename=entryName+".zipEntry" File.open(zipEntryFilename, "wb") { |file| file << actual } fail("File '#{filename}' is different from '#{zipEntryFilename}'") else assert_equals(expected, actual) end end } end def AssertEntry.assertContents(filename, aString) fileContents = "" File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) stringFile = filename + ".other" File.open(stringFile, "wb") { |f| f << aString } fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else assert_equals(expected, actual) end end end def assertStreamContents(zis, testZipFile) assert(zis != nil) testZipFile.entryNames.each { |entryName| assertNextEntry(entryName, zis) } assert_equals(nil, zis.getNextEntry) end def assertTestZipContents(testZipFile) ZipInputStream.open(testZipFile.zipName) { |zis| assertStreamContents(zis, testZipFile) } end def assertEntryContents(zipFile, entryName, filename = entryName.to_s) zis = zipFile.getInputStream(entryName) assertEntryContentsForStream(filename, zis, entryName) ensure zis.close if zis end end class ZipInputStreamTest < RUNIT::TestCase include AssertEntry def test_new zis = ZipInputStream.new(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) zis.close end def test_openWithBlock ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| assertStreamContents(zis, TestZipFile::TEST_ZIP2) } end def test_openWithoutBlock zis = ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) end def test_incompleteReads ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[0], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[1], entry.name) assert_equals(0, entry.size) assert_equals(nil, zis.gets) entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[2], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[3], entry.name) assert zis.gets.length > 0 } end end class TestFiles RANDOM_ASCII_FILE1 = "randomAscii1.txt" RANDOM_ASCII_FILE2 = "randomAscii2.txt" RANDOM_ASCII_FILE3 = "randomAscii3.txt" RANDOM_BINARY_FILE1 = "randomBinary1.bin" RANDOM_BINARY_FILE2 = "randomBinary2.bin" EMPTY_TEST_DIR = "emptytestdir" ASCII_TEST_FILES = [ RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3 ] BINARY_TEST_FILES = [ RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2 ] TEST_DIRECTORIES = [ EMPTY_TEST_DIR ] TEST_FILES = [ ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR ].flatten! def TestFiles.createTestFiles(recreate) if (recreate || ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { |filename, index| createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { |filename, index| createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) end end private def TestFiles.createRandomAscii(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand end } end def TestFiles.createRandomBinary(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand.to_a.pack("V") end } end def TestFiles.ensureDir(name) if File.exists?(name) return if File.stat(name).directory? File.delete(name) end Dir.mkdir(name) end end # For representation and creation of # test data class TestZipFile attr_accessor :zipName, :entryNames, :comment def initialize(zipName, entryNames, comment = "") @zipName=zipName @entryNames=entryNames @comment = comment end def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || ! (files.index(TEST_ZIP1.zipName) && files.index(TEST_ZIP2.zipName) && files.index(TEST_ZIP3.zipName) && files.index(TEST_ZIP4.zipName) && files.index("empty.txt") && files.index("short.txt") && files.index("longAscii.txt") && files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} File.open("short.txt", "w") { |file| file << "ABCDEF" } ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { |file| while (file.tell < 1E5) file << ziptestTxt end } testBinaryPattern="" File.open("empty.zip") { |file| testBinaryPattern=file.read } testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { |file| while (file.tell < 3E5) file << testBinaryPattern << rand end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + "\n\nziptest.rb requires the Info-ZIP program 'zip' in the path\n" + "to create test data. If you don't have it you can download\n" + "the necessary test files at http://sf.net/projects/rubyzip." end TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", TestFiles::EMPTY_TEST_DIR]) end class AbstractOutputStreamTest < RUNIT::TestCase class TestOutputStream include AbstractOutputStream attr_accessor :buffer def initialize @buffer = "" end def << (data) @buffer << data self end end def setup @outputStream = TestOutputStream.new @origCommaSep = $, @origOutputSep = $\ end def teardown $, = @origCommaSep $\ = @origOutputSep end def test_write count = @outputStream.write("a little string") assert_equals("a little string", @outputStream.buffer) assert_equals("a little string".length, count) count = @outputStream.write(". a little more") assert_equals("a little string. a little more", @outputStream.buffer) assert_equals(". a little more".length, count) end def test_print $\ = nil # record separator set to nil @outputStream.print("hello") assert_equals("hello", @outputStream.buffer) @outputStream.print(" world.") assert_equals("hello world.", @outputStream.buffer) @outputStream.print(" You ok ", "out ", "there?") assert_equals("hello world. You ok out there?", @outputStream.buffer) $\ = "\n" @outputStream.print assert_equals("hello world. You ok out there?\n", @outputStream.buffer) @outputStream.print("I sure hope so!") assert_equals("hello world. You ok out there?\nI sure hope so!\n", @outputStream.buffer) $, = "X" @outputStream.buffer = "" @outputStream.print("monkey", "duck", "zebra") assert_equals("monkeyXduckXzebra\n", @outputStream.buffer) $\ = nil @outputStream.buffer = "" @outputStream.print(20) assert_equals("20", @outputStream.buffer) end def test_printf @outputStream.printf("%d %04x", 123, 123) assert_equals("123 007b", @outputStream.buffer) end def test_putc @outputStream.putc("A") assert_equals("A", @outputStream.buffer) @outputStream.putc(65) assert_equals("AA", @outputStream.buffer) end def test_puts @outputStream.puts assert_equals("\n", @outputStream.buffer) @outputStream.puts("hello", "world") assert_equals("\nhello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts("hello\n", "world\n") assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"]) assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"], "bingo") assert_equals("hello\nworld\nbingo\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(16, 20, 50, "hello") assert_equals("16\n20\n50\nhello\n", @outputStream.buffer) end end module CrcTest def runCrcTest(compressorClass) str = "Here's a nice little text to compute the crc for! Ho hum, it is nice nice nice nice indeed." fakeOut = AbstractOutputStreamTest::TestOutputStream.new deflater = compressorClass.new(fakeOut) deflater << str assert_equals(0x919920fc, deflater.crc) end end class PassThruCompressorTest < RUNIT::TestCase include CrcTest def test_size File.open("dummy.txt", "wb") { |file| compressor = PassThruCompressor.new(file) assert_equals(0, compressor.size) t1 = "hello world" t2 = "" t3 = "bingo" compressor << t1 assert_equals(compressor.size, t1.size) compressor << t2 assert_equals(compressor.size, t1.size + t2.size) compressor << t3 assert_equals(compressor.size, t1.size + t2.size + t3.size) } end def test_crc runCrcTest(PassThruCompressor) end end class DeflaterTest < RUNIT::TestCase include CrcTest def test_outputOperator txt = loadFile("ziptest.rb") deflate(txt, "deflatertest.bin") inflatedTxt = inflate("deflatertest.bin") assert_equals(txt, inflatedTxt) end private def loadFile(fileName) txt = nil File.open(fileName, "rb") { |f| txt = f.read } end def deflate(data, fileName) File.open(fileName, "wb") { |file| deflater = Deflater.new(file) deflater << data deflater.finish assert_equals(deflater.size, data.size) file << "trailing data for zlib with -MAX_WBITS" } end def inflate(fileName) txt = nil File.open(fileName, "rb") { |file| inflater = Inflater.new(file) txt = inflater.read } end def test_crc runCrcTest(Deflater) end end class ZipOutputStreamTest < RUNIT::TestCase include AssertEntry TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "output.zip" def test_new zos = ZipOutputStream.new(TEST_ZIP.zipName) zos.comment = TEST_ZIP.comment writeTestZip(zos) zos.close assertTestZipContents(TEST_ZIP) end def test_open ZipOutputStream.open(TEST_ZIP.zipName) { |zos| zos.comment = TEST_ZIP.comment writeTestZip(zos) } assertTestZipContents(TEST_ZIP) end def test_writingToClosedStream assertIOErrorInClosedStream { |zos| zos << "hello world" } assertIOErrorInClosedStream { |zos| zos.puts "hello world" } assertIOErrorInClosedStream { |zos| zos.write "hello world" } end def test_cannotOpenFile name = TestFiles::EMPTY_TEST_DIR begin zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux $!.kind_of?(Errno::EEXIST) || # Windows/cygwin $!.kind_of?(Errno::EACCES), # Windows "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end def assertIOErrorInClosedStream assert_exception(IOError) { zos = ZipOutputStream.new("test_putOnClosedStream.zip") zos.close yield zos } end def writeTestZip(zos) TEST_ZIP.entryNames.each { |entryName| zos.putNextEntry(entryName) File.open(entryName, "rb") { |f| zos.write(f.read) } } end end module Enumerable def compareEnumerables(otherEnumerable) otherAsArray = otherEnumerable.to_a index=0 each_with_index { |element, index| return false unless yield(element, otherAsArray[index]) } return index+1 == otherAsArray.size end end class ZipCentralDirectoryEntryTest < RUNIT::TestCase def test_readFromStream File.open("testDirectory.bin", "rb") { |file| entry = ZipEntry.readCDirEntry(file) assert_equals("longAscii.txt", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(106490, entry.size) assert_equals(3784, entry.compressedSize) assert_equals(0xfcd1799c, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("empty.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(0, entry.size) assert_equals(0, entry.compressedSize) assert_equals(0x0, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("short.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(6, entry.size) assert_equals(6, entry.compressedSize) assert_equals(0xbb76fe69, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("longBinary.bin", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(1000024, entry.size) assert_equals(70847, entry.compressedSize) assert_equals(0x10da7d59, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals(nil, entry) # Fields that are not check by this test: # version made by 2 bytes # version needed to extract 2 bytes # general purpose bit flag 2 bytes # last mod file time 2 bytes # last mod file date 2 bytes # compressed size 4 bytes # uncompressed size 4 bytes # disk number start 2 bytes # internal file attributes 2 bytes # external file attributes 4 bytes # relative offset of local header 4 bytes # file name (variable size) # extra field (variable size) # file comment (variable size) } end def test_ReadEntryFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read(12) } # cdir entry header is at least 46 bytes fragment.extend(IOizeString) entry = ZipEntry.new entry.readCDirEntry(fragment) fail "ZipError expected" rescue ZipError end end class ZipCentralDirectoryTest < RUNIT::TestCase def test_readFromStream File.open(TestZipFile::TEST_ZIP2.zipName, "rb") { |zipFile| cdir = ZipCentralDirectory.readFromStream(zipFile) assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { |cdirEntry, testEntryName| cdirEntry.name == testEntryName }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end def test_readFromInvalidStream File.open("ziptest.rb", "rb") { |zipFile| cdir = ZipCentralDirectory.new cdir.readFromStream(zipFile) } fail "ZipError expected!" rescue ZipError end def test_ReadFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read } fragment.slice!(12) # removed part of first cdir entry. eocd structure still complete fragment.extend(IOizeString) entry = ZipCentralDirectory.new entry.readFromStream(fragment) fail "ZipError expected" rescue ZipError end def test_writeToStream entries = [ ZipEntry.new("file.zip", "flimse", "myComment", "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt", "Has a comment too") ] cdir = ZipCentralDirectory.new(entries, "my zip comment") File.open("cdirtest.bin", "wb") { |f| cdir.writeToStream(f) } cdirReadback = ZipCentralDirectory.new File.open("cdirtest.bin", "rb") { |f| cdirReadback.readFromStream(f) } assert_equals(cdir.entries, cdirReadback.entries) end def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) assert(cdir1 != cdir3) assert(cdir2 != cdir3) assert(cdir2 != cdir3) assert(cdir3 != cdir4) assert(cdir3 != "hello") end end class BasicZipFileTest < RUNIT::TestCase include AssertEntry def setup @zipFile = ZipFile.new(TestZipFile::TEST_ZIP2.zipName) @testEntryNameIndex=0 end def nextTestEntryName retVal=TestZipFile::TEST_ZIP2.entryNames[@testEntryNameIndex] @testEntryNameIndex+=1 return retVal end def test_entries assert_equals(TestZipFile::TEST_ZIP2.entryNames, @zipFile.entries.map {|e| e.name} ) end def test_each @zipFile.each { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_foreach ZipFile.foreach(TestZipFile::TEST_ZIP2.zipName) { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStream @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStreamBlock fileAndEntryName = @zipFile.entries.first.name @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, zis, fileAndEntryName) } end end class CommonZipFileFixture < RUNIT::TestCase include AssertEntry EMPTY_FILENAME = "emptyZipFile.zip" TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "4entry_copy.zip" def setup File.delete(EMPTY_FILENAME) if File.exists?(EMPTY_FILENAME) File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) end end class ZipFileTest < CommonZipFileFixture def test_createFromScratch comment = "a short comment" zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.comment = comment zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals(comment, zfRead.comment) assert_equals(0, zfRead.entries.length) end def test_add srcFile = "ziptest.rb" entryName = "newEntryName.rb" assert(File.exists? srcFile) zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.add(entryName, srcFile) zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals("", zfRead.comment) assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(zf.entries.first.name, "ziptest.rb") } } end def test_addExistingEntryNameReplace gotCalled = false replacedEntry = nil ZipFile.open(TEST_ZIP.zipName) { |zf| replacedEntry = zf.entries.first.name zf.add(replacedEntry, "ziptest.rb") { gotCalled = true; true } } assert(gotCalled) ZipFile.open(TEST_ZIP.zipName) { |zf| assertContains(zf, replacedEntry, "ziptest.rb") } end def test_addDirectory ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(TestFiles::EMPTY_TEST_DIR, TestFiles::EMPTY_TEST_DIR) } ZipFile.open(TEST_ZIP.zipName) { |zf| dirEntry = zf.entries.detect { |e| e.name == TestFiles::EMPTY_TEST_DIR+"/" } assert(dirEntry.isDirectory) } end def test_remove entryToRemove, *remainingEntries = TEST_ZIP.entryNames File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include?(entryToRemove)) zf.remove(entryToRemove) assert(! zf.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zf.entries.map {|x| x.name }.sort, remainingEntries.sort) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(! zfRead.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zfRead.entries.map {|x| x.name }.sort, remainingEntries.sort) zfRead.close end def test_rename entryToRename, *remainingEntries = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include? entryToRename) newName = "changed name" assert(! zf.entries.map { |e| e.name }.include?(newName)) zf.rename(entryToRename, newName) assert(zf.entries.map { |e| e.name }.include? newName) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.map { |e| e.name }.include? newName) zfRead.close end def test_renameToExistingEntry oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) } } ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameToExistingEntryOverwrite oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) { gotCalled = true; true } } assert(gotCalled) oldEntries.delete_at(0) ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameNonEntry nonEntry = "bogusEntry" targetEntry = "targetEntryName" zf = ZipFile.new(TEST_ZIP.zipName) assert(! zf.entries.include?(nonEntry)) assert_exception(ZipNoSuchEntryError) { zf.rename(nonEntry, targetEntry) } zf.commit assert(! zf.entries.include?(targetEntry)) ensure zf.close end def test_renameEntryToExistingEntry entry1, entry2, *remaining = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipEntryExistsError) { zf.rename(entry1, entry2) } ensure zf.close end def test_replace unchangedEntries = TEST_ZIP.entryNames.dup entryToReplace = unchangedEntries.delete_at(2) newEntrySrcFilename = "ziptest.rb" zf = ZipFile.new(TEST_ZIP.zipName) zf.replace(entryToReplace, newEntrySrcFilename) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end def test_replaceNonEntry entryToReplace = "nonExistingEntryname" ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { zf.replace(entryToReplace, "ziptest.rb") } } end def test_commit newName = "renamedFirst" zf = ZipFile.new(TEST_ZIP.zipName) oldName = zf.entries.first zf.rename(oldName, newName) zf.commit zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.detect { |e| e.name == newName } != nil) assert(zfRead.entries.detect { |e| e.name == oldName } == nil) zfRead.close zf.close end # This test tests that after commit, you # can delete the file you used to add the entry to the zip file # with def test_commitUseZipEntry File.copy(TestFiles::RANDOM_ASCII_FILE1, "okToDelete.txt") zf = ZipFile.open(TEST_ZIP.zipName) zf.add("okToDelete.txt", "okToDelete.txt") assertContains(zf, "okToDelete.txt") zf.commit File.move("okToDelete.txt", "okToDeleteMoved.txt") assertContains(zf, "okToDelete.txt", "okToDeleteMoved.txt") end # def test_close # zf = ZipFile.new(TEST_ZIP.zipName) # zf.close # assert_exception(IOError) { # zf.extract(TEST_ZIP.entryNames.first, "hullubullu") # } # end def test_compound1 renamedName = "renamedName" originalEntries = [] begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) zf.remove(originalEntries.last.to_s) assertNotContains(zf, originalEntries.last.to_s) ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure zfRead.close end end def test_compound2 begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup originalEntries.each { |entry| zf.remove(entry) assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) zf.rename(TestFiles::ASCII_TEST_FILES[0], "newName") assertNotContains(zf, TestFiles::ASCII_TEST_FILES[0]) assertContains(zf, "newName") ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { |filename| assertContains(zf, filename) } assertContains(zf, "newName") ensure zfRead.close end end private def assertContains(zf, entryName, filename = entryName) assert(zf.entries.detect { |e| e.name == entryName} != nil, "entry #{entryName} not in #{zf.entries.join(', ')} in zip file #{zf}") assertEntryContents(zf, entryName, filename) if File.exists?(filename) end def assertNotContains(zf, entryName) assert(zf.entries.detect { |e| e.name == entryName} == nil, "entry #{entryName} in #{zf.entries.join(', ')} in zip file #{zf}") end end class ZipFileExtractTest < CommonZipFileFixture EXTRACTED_FILENAME = "extEntry" ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entryNames.reverse def setup super File.delete(EXTRACTED_FILENAME) if File.exists?(EXTRACTED_FILENAME) end def test_extract ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end def test_extractExists writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { |f| assert_equals(writtenText, f.read) } end def test_extractExistsOverwrite writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) { gotCalled = true; true } } assert(gotCalled) File.open(EXTRACTED_FILENAME, "r") { |f| assert(writtenText != f.read) } end def test_extractNonEntry zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipNoSuchEntryError) { zf.extract("nonExistingEntry", "nonExistingEntry") } ensure zf.close if zf end def test_extractNonEntry2 outFile = "outfile" assert_exception(ZipNoSuchEntryError) { zf = ZipFile.new(TEST_ZIP.zipName) nonEntry = "hotdog-diddelidoo" assert(! zf.entries.include?(nonEntry)) zf.extract(nonEntry, outFile) zf.close } assert(! File.exists?(outFile)) end end class ZipFileExtractDirectoryTest < CommonZipFileFixture TEST_OUT_NAME = "emptyOutDir" def openZip(&aProc) assert(aProc != nil) ZipFile.open(TestZipFile::TEST_ZIP4.zipName, &aProc) end def extractTestDir(&aProc) openZip { |zf| zf.extract(TestFiles::EMPTY_TEST_DIR, TEST_OUT_NAME, &aProc) } end def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME File.delete(TEST_OUT_NAME) if File.exists? TEST_OUT_NAME end def test_extractDirectory extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsDir Dir.mkdir TEST_OUT_NAME extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsFile File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } assert_exception(ZipDestinationFileExistsError) { extractTestDir } end def test_extractDirectoryExistsAsFileOverwrite File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } gotCalled = false extractTestDir { |entry, destPath| gotCalled = true assert_equals(TEST_OUT_NAME, destPath) assert(entry.isDirectory) true } assert(gotCalled) assert(File.directory? TEST_OUT_NAME) end end TestFiles::createTestFiles(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' #RUNIT::CUI::TestRunner.run(ZipFileTest.suite) # Copyright (C) 2002 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license.