diff --git a/NEWS b/NEWS index 1ba65ea..775ac31 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ += Version 0.3.0 = + +Rudimentary support for writing zip archives. + = Version 0.2.2 = Fixed and extended unit test suite. Updated to work with ruby/zlib diff --git a/README.txt b/README.txt index 2dd80ee..51d6eac 100644 --- a/README.txt +++ b/README.txt @@ -18,6 +18,9 @@ ruby-zlib: http://www.blue.sky.or.jp/atelier/#ruby-zlib This library requires ruby/zlib version 0.5.0 or newer. +As of this writing ruby/zlib 0.5.0 is only available as +ruby-zlib-0.5.0-pre3 at http://www.blue.sky.or.jp/atelier/ruby/ + = LICENSE = diff --git a/zip.rb b/zip.rb index c019fcd..d6ecfdb 100755 --- a/zip.rb +++ b/zip.rb @@ -453,7 +453,7 @@ module Zip @fileName = fileName @outputStream = File.new(@fileName, "wb") @entries = [] - @compressor = NullCompressor + @compressor = NullCompressor.instance end def ZipOutputStream.open(fileName) @@ -483,10 +483,12 @@ module Zip def finalizeCurrentEntry return unless @currentEntry finish - @currentEntry.compressedSize = @outputStream.tell - @currentEntry.localHeaderOffset - @currentEntry.localHeaderSize + @currentEntry.compressedSize = @outputStream.tell - @currentEntry.localHeaderOffset - + @currentEntry.localHeaderSize @currentEntry.size = @compressor.size + @currentEntry.crc = @compressor.crc @currentEntry = nil - @compressor = NullCompressor + @compressor = NullCompressor.instance end def initNextEntry(entry, level = Zlib::DEFAULT_COMPRESSION) @@ -520,13 +522,15 @@ module Zip end protected - def << (data) - @compressor << data - end def finish @compressor.finish end + + public + def << (data) + @compressor << data + end end @@ -538,22 +542,25 @@ module Zip class PassThruCompressor < Compressor def initialize(outputStream) @outputStream = outputStream + @crc = Zlib::crc32 @size = 0 end def << (data) val = data.to_s + @crc = Zlib::crc32(val, @crc) @size += val.size @outputStream << val end - attr_reader :size + attr_reader :size, :crc end class NullCompressor < Compressor include Singleton def << (data) + raise IOError, "closed stream" end attr_reader :size, :compressedSize @@ -564,10 +571,12 @@ module Zip @outputStream = outputStream @zlibDeflater = Zlib::Deflate.new(level, -Zlib::Deflate::MAX_WBITS) @size = 0 + @crc = Zlib::crc32 end def << (data) val = data.to_s + @crc = Zlib::crc32(val, @crc) @size += val.size @outputStream << @zlibDeflater.deflate(data) end @@ -578,7 +587,7 @@ module Zip end end - attr_reader :size + attr_reader :size, :crc end class ZipCentralDirectory diff --git a/ziptest.rb b/ziptest.rb index f7262aa..53f7b6c 100755 --- a/ziptest.rb +++ b/ziptest.rb @@ -551,7 +551,23 @@ class AbstractOutputStreamTest < RUNIT::TestCase 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| @@ -573,9 +589,15 @@ class PassThruCompressorTest < RUNIT::TestCase 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") @@ -608,8 +630,11 @@ class DeflaterTest < RUNIT::TestCase txt = inflater.read } end -end + def test_crc + runCrcTest(Deflater) + end +end class ZipOutputStreamTest < RUNIT::TestCase include AssertEntry @@ -634,18 +659,33 @@ class ZipOutputStreamTest < RUNIT::TestCase assertTestZipContents(TEST_ZIP) end - def test_putOnClosedStream - fail "implement and expect ZipError" - end - def test_writingToClosedStream - fail "implement this test and make sure behaviour is similar to closed File object" + assertIOErrorInClosedStream { |zos| zos << "hello world" } + assertIOErrorInClosedStream { |zos| zos.puts "hello world" } + assertIOErrorInClosedStream { |zos| zos.write "hello world" } end def test_cannotOpenFile - fail "implement and expect zip.closed? and exception from constructor" + name = "emptytestdir" + ensureDir(name) + assert_exception(Errno::EISDIR, "Is a directorYY") { + zos = ZipOutputStream.open(name) + } end + def ensureDir(name) + return if File.stat(name).directory? + File.delete(name) + Dir.mkdir(name) + end + + def assertIOErrorInClosedStream + assert_exception(IOError) { + zos = ZipOutputStream.new("test_putOnClosedStream.zip") + zos.close + yield zos + } + end def writeTestZip(zos) TEST_ZIP.entryNames.each {