require 'test_helper' class ZipFileTest < MiniTest::Test include CommonZipFileFixture include ZipEntryData OK_DELETE_FILE = 'test/data/generated/okToDelete.txt' OK_DELETE_MOVED_FILE = 'test/data/generated/okToDeleteMoved.txt' def teardown ::Zip.write_zip64_support = false end def test_create_from_scratch_to_buffer comment = 'a short comment' buffer = ::Zip::File.add_buffer do |zf| zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment end ::File.open(EMPTY_FILENAME, 'wb') { |file| file.write buffer.string } zfRead = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zfRead.comment) assert_equal(2, zfRead.entries.length) end def test_create_from_scratch comment = 'a short comment' zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment zf.close zfRead = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zfRead.comment) assert_equal(2, zfRead.entries.length) end def test_create_from_scratch_with_old_create_parameter comment = 'a short comment' zf = ::Zip::File.new(EMPTY_FILENAME, 1) zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment zf.close zfRead = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zfRead.comment) assert_equal(2, zfRead.entries.length) end def test_get_input_stream_stored_with_gpflag_bit3 ::Zip::File.open('test/data/gpbit3stored.zip') do |zf| assert_equal("foo\n", zf.read("foo.txt")) end end def test_get_output_stream entryCount = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| entryCount = zf.size zf.get_output_stream('newEntry.txt') do |os| os.write 'Putting stuff in newEntry.txt' end assert_equal(entryCount + 1, zf.size) assert_equal('Putting stuff in newEntry.txt', zf.read('newEntry.txt')) zf.get_output_stream(zf.get_entry('test/data/generated/empty.txt')) do |os| os.write 'Putting stuff in data/generated/empty.txt' end assert_equal(entryCount + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) custom_entry_args = [TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, ::Zip::Entry::STORED, TEST_SIZE, TEST_TIME] zf.get_output_stream('entry_with_custom_args.txt', nil, *custom_entry_args) do |os| os.write 'Some data' end assert_equal(entryCount + 2, zf.size) entry = zf.get_entry('entry_with_custom_args.txt') assert_equal(custom_entry_args[0], entry.comment) assert_equal(custom_entry_args[2], entry.compressed_size) assert_equal(custom_entry_args[3], entry.crc) assert_equal(custom_entry_args[4], entry.compression_method) assert_equal(custom_entry_args[5], entry.size) assert_equal(custom_entry_args[6], entry.time) zf.get_output_stream('entry.bin') do |os| os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) end end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(entryCount + 3, zf.size) assert_equal('Putting stuff in newEntry.txt', zf.read('newEntry.txt')) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) assert_equal(File.open('test/data/generated/5entry.zip', 'rb').read, zf.read('entry.bin')) end end def test_open_buffer_with_string string = File.read('test/data/rubycode.zip') ::Zip::File.open_buffer string do |zf| assert zf.entries.map { |e| e.name }.include?('zippedruby1.rb') end end def test_open_buffer_with_stringio string_io = StringIO.new File.read('test/data/rubycode.zip') ::Zip::File.open_buffer string_io do |zf| assert zf.entries.map { |e| e.name }.include?('zippedruby1.rb') end end def test_close_buffer_with_stringio string_io = StringIO.new File.read('test/data/rubycode.zip') zf = ::Zip::File.open_buffer string_io assert_nil zf.close end def test_open_buffer_no_op_does_not_change_file Dir.mktmpdir do |tmp| test_zip = File.join(tmp, 'test.zip') FileUtils.cp 'test/data/rubycode.zip', test_zip # Note: this may change the file if it is opened with r+b instead of rb. # The 'extra fields' in this particular zip file get reordered. File.open(test_zip, 'rb') do |file| Zip::File.open_buffer(file) do |zf| nil # do nothing end end assert_equal \ File.binread('test/data/rubycode.zip'), File.binread(test_zip) end end def test_open_buffer_close_does_not_change_file Dir.mktmpdir do |tmp| test_zip = File.join(tmp, 'test.zip') FileUtils.cp 'test/data/rubycode.zip', test_zip File.open(test_zip, 'rb') do |file| zf = Zip::File.open_buffer(file) refute zf.commit_required? assert_nil zf.close end assert_equal \ File.binread('test/data/rubycode.zip'), File.binread(test_zip) end end def test_open_buffer_with_io_and_block File.open('test/data/rubycode.zip') do |io| io.set_encoding(Encoding::BINARY) # not strictly required but can be set Zip::File.open_buffer(io) do |zip_io| # left empty on purpose end end end def test_open_buffer_without_block string_io = StringIO.new File.read('test/data/rubycode.zip') zf = ::Zip::File.open_buffer string_io assert zf.entries.map { |e| e.name }.include?('zippedruby1.rb') end def test_cleans_up_tempfiles_after_close zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.get_output_stream('myFile') do |os| @tempfile_path = os.path os.write 'myFile contains just this' end assert_equal(true, File.exist?(@tempfile_path)) zf.close assert_equal(false, File.exist?(@tempfile_path)) end def test_add srcFile = 'test/data/file2.txt' entryName = 'newEntryName.rb' assert(::File.exist?(srcFile)) zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.add(entryName, srcFile) zf.close zfRead = ::Zip::File.new(EMPTY_FILENAME) assert_equal('', zfRead.comment) assert_equal(1, zfRead.entries.length) assert_equal(entryName, zfRead.entries.first.name) AssertEntry.assert_contents(srcFile, zfRead.get_input_stream(entryName) { |zis| zis.read }) end def test_recover_permissions_after_add_files_to_archive srcZip = TEST_ZIP.zip_name ::File.chmod(0o664, srcZip) srcFile = 'test/data/file2.txt' entryName = 'newEntryName.rb' assert_equal(::File.stat(srcZip).mode, 0o100664) assert(::File.exist?(srcZip)) zf = ::Zip::File.new(srcZip, ::Zip::File::CREATE) zf.add(entryName, srcFile) zf.close assert_equal(::File.stat(srcZip).mode, 0o100664) end def test_add_existing_entry_name assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(zf.entries.first.name, 'test/data/file2.txt') end end end def test_add_existing_entry_name_replace gotCalled = false replacedEntry = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| replacedEntry = zf.entries.first.name zf.add(replacedEntry, 'test/data/file2.txt') { gotCalled = true; true } end assert(gotCalled) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_contains(zf, replacedEntry, 'test/data/file2.txt') end end def test_add_directory ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(TestFiles::EMPTY_TEST_DIR, TestFiles::EMPTY_TEST_DIR) end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| dirEntry = zf.entries.detect { |e| e.name == TestFiles::EMPTY_TEST_DIR + '/' } assert(dirEntry.directory?) end end def test_remove entryToRemove, *remainingEntries = TEST_ZIP.entry_names FileUtils.cp(TestZipFile::TEST_ZIP2.zip_name, TEST_ZIP.zip_name) zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(zf.entries.map { |e| e.name }.include?(entryToRemove)) zf.remove(entryToRemove) assert(!zf.entries.map { |e| e.name }.include?(entryToRemove)) assert_equal(zf.entries.map { |x| x.name }.sort, remainingEntries.sort) zf.close zfRead = ::Zip::File.new(TEST_ZIP.zip_name) assert(!zfRead.entries.map { |e| e.name }.include?(entryToRemove)) assert_equal(zfRead.entries.map { |x| x.name }.sort, remainingEntries.sort) zfRead.close end def test_rename entryToRename, * = TEST_ZIP.entry_names zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(zf.entries.map { |e| e.name }.include?(entryToRename)) contents = zf.read(entryToRename) newName = 'changed entry name' assert(!zf.entries.map { |e| e.name }.include?(newName)) zf.rename(entryToRename, newName) assert(zf.entries.map { |e| e.name }.include?(newName)) assert_equal(contents, zf.read(newName)) zf.close zfRead = ::Zip::File.new(TEST_ZIP.zip_name) assert(zfRead.entries.map { |e| e.name }.include?(newName)) assert_equal(contents, zfRead.read(newName)) zfRead.close end def test_rename_with_each zf_name = 'test_rename_zip.zip' ::File.unlink(zf_name) if ::File.exist?(zf_name) arr = [] arr_renamed = [] ::Zip::File.open(zf_name, ::Zip::File::CREATE) do |zf| zf.mkdir('test') arr << 'test/' arr_renamed << 'Ztest/' %w[a b c d].each do |f| zf.get_output_stream("test/#{f}") { |file| file.puts 'aaaa' } arr << "test/#{f}" arr_renamed << "Ztest/#{f}" end end zf = ::Zip::File.open(zf_name) assert_equal(zf.entries.map(&:name), arr) zf.close Zip::File.open(zf_name, 'wb') do |z| z.each do |f| z.rename(f, "Z#{f.name}") end end zf = ::Zip::File.open(zf_name) assert_equal(zf.entries.map(&:name), arr_renamed) zf.close ::File.unlink(zf_name) if ::File.exist?(zf_name) end def test_rename_to_existing_entry oldEntries = nil ::Zip::File.open(TEST_ZIP.zip_name) { |zf| oldEntries = zf.entries } assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.rename(zf.entries[0], zf.entries[1].name) end end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(oldEntries.sort.map { |e| e.name }, zf.entries.sort.map { |e| e.name }) end end def test_rename_to_existing_entry_overwrite oldEntries = nil ::Zip::File.open(TEST_ZIP.zip_name) { |zf| oldEntries = zf.entries } gotCalled = false renamedEntryName = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| renamedEntryName = zf.entries[0].name zf.rename(zf.entries[0], zf.entries[1].name) { gotCalled = true; true } end assert(gotCalled) oldEntries.delete_if { |e| e.name == renamedEntryName } ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(oldEntries.sort.map { |e| e.name }, zf.entries.sort.map { |e| e.name }) end end def test_rename_non_entry nonEntry = 'bogusEntry' target_entry = 'target_entryName' zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(!zf.entries.include?(nonEntry)) assert_raises(Errno::ENOENT) { zf.rename(nonEntry, target_entry) } zf.commit assert(!zf.entries.include?(target_entry)) ensure zf.close end def test_rename_entry_to_existing_entry entry1, entry2, * = TEST_ZIP.entry_names zf = ::Zip::File.new(TEST_ZIP.zip_name) assert_raises(::Zip::EntryExistsError) { zf.rename(entry1, entry2) } ensure zf.close end def test_replace entryToReplace = TEST_ZIP.entry_names[2] newEntrySrcFilename = 'test/data/file2.txt' zf = ::Zip::File.new(TEST_ZIP.zip_name) zf.replace(entryToReplace, newEntrySrcFilename) zf.close zfRead = ::Zip::File.new(TEST_ZIP.zip_name) AssertEntry.assert_contents(newEntrySrcFilename, zfRead.get_input_stream(entryToReplace) { |is| is.read }) AssertEntry.assert_contents(TEST_ZIP.entry_names[0], zfRead.get_input_stream(TEST_ZIP.entry_names[0]) { |is| is.read }) AssertEntry.assert_contents(TEST_ZIP.entry_names[1], zfRead.get_input_stream(TEST_ZIP.entry_names[1]) { |is| is.read }) AssertEntry.assert_contents(TEST_ZIP.entry_names[3], zfRead.get_input_stream(TEST_ZIP.entry_names[3]) { |is| is.read }) zfRead.close end def test_replace_non_entry entryToReplace = 'nonExistingEntryname' ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_raises(Errno::ENOENT) { zf.replace(entryToReplace, 'test/data/file2.txt') } end end def test_commit newName = 'renamedFirst' zf = ::Zip::File.new(TEST_ZIP.zip_name) oldName = zf.entries.first zf.rename(oldName, newName) zf.commit zfRead = ::Zip::File.new(TEST_ZIP.zip_name) assert(zfRead.entries.detect { |e| e.name == newName } != nil) assert(zfRead.entries.detect { |e| e.name == oldName }.nil?) zfRead.close zf.close res = system("unzip -tqq #{TEST_ZIP.zip_name}") assert_equal(res, true) end def test_double_commit(filename = 'test/data/generated/double_commit_test.zip') ::FileUtils.touch('test/data/generated/test_double_commit1.txt') ::FileUtils.touch('test/data/generated/test_double_commit2.txt') zf = ::Zip::File.open(filename, ::Zip::File::CREATE) zf.add('test1.txt', 'test/data/generated/test_double_commit1.txt') zf.commit zf.add('test2.txt', 'test/data/generated/test_double_commit2.txt') zf.commit zf.close zf2 = ::Zip::File.open(filename) assert(zf2.entries.detect { |e| e.name == 'test1.txt' } != nil) assert(zf2.entries.detect { |e| e.name == 'test2.txt' } != nil) res = system("unzip -tqq #{filename}") assert_equal(res, true) end def test_double_commit_zip64 ::Zip.write_zip64_support = true test_double_commit('test/data/generated/double_commit_test64.zip') end def test_write_buffer newName = 'renamedFirst' zf = ::Zip::File.new(TEST_ZIP.zip_name) oldName = zf.entries.first zf.rename(oldName, newName) io = ::StringIO.new('') buffer = zf.write_buffer(io) File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } zfRead = ::Zip::File.new(TEST_ZIP.zip_name) 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_commit_use_zip_entry FileUtils.cp(TestFiles::RANDOM_ASCII_FILE1, OK_DELETE_FILE) zf = ::Zip::File.open(TEST_ZIP.zip_name) zf.add('okToDelete.txt', OK_DELETE_FILE) assert_contains(zf, 'okToDelete.txt') zf.commit File.rename(OK_DELETE_FILE, OK_DELETE_MOVED_FILE) assert_contains(zf, 'okToDelete.txt', OK_DELETE_MOVED_FILE) end # def test_close # zf = ZipFile.new(TEST_ZIP.zip_name) # zf.close # assert_raises(IOError) { # zf.extract(TEST_ZIP.entry_names.first, "hullubullu") # } # end def test_compound1 renamedName = 'renamedName' filename_to_remove = '' begin zf = ::Zip::File.new(TEST_ZIP.zip_name) originalEntries = zf.entries.dup assert_not_contains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, TestFiles::RANDOM_ASCII_FILE1) assert_contains(zf, TestFiles::RANDOM_ASCII_FILE1) entry_to_rename = zf.entries.find { |entry| entry.name.match('longAscii') } zf.rename(entry_to_rename, renamedName) assert_contains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each do |filename| zf.add(filename, filename) assert_contains(zf, filename) end assert_contains(zf, originalEntries.last.to_s) filename_to_remove = originalEntries.map(&:to_s).find { |name| name.match('longBinary') } zf.remove(filename_to_remove) assert_not_contains(zf, filename_to_remove) ensure zf.close end begin zfRead = ::Zip::File.new(TEST_ZIP.zip_name) assert_contains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assert_contains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each do |filename| assert_contains(zfRead, filename) end assert_not_contains(zfRead, filename_to_remove) ensure zfRead.close end end def test_compound2 begin zf = ::Zip::File.new(TEST_ZIP.zip_name) originalEntries = zf.entries.dup originalEntries.each do |entry| zf.remove(entry) assert_not_contains(zf, entry) end assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each do |filename| zf.add(filename, filename) assert_contains(zf, filename) end assert_equal(zf.entries.sort.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) zf.rename(TestFiles::ASCII_TEST_FILES[0], 'newName') assert_not_contains(zf, TestFiles::ASCII_TEST_FILES[0]) assert_contains(zf, 'newName') ensure zf.close end begin zfRead = ::Zip::File.new(TEST_ZIP.zip_name) asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each do |filename| assert_contains(zf, filename) end assert_contains(zf, 'newName') ensure zfRead.close end end def test_change_comment ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.comment = 'my changed comment' end zfRead = ::Zip::File.open(TEST_ZIP.zip_name) assert_equal('my changed comment', zfRead.comment) end def test_preserve_file_order entryNames = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| entryNames = zf.entries.map { |e| e.to_s } zf.get_output_stream('a.txt') { |os| os.write 'this is a.txt' } zf.get_output_stream('z.txt') { |os| os.write 'this is z.txt' } zf.get_output_stream('k.txt') { |os| os.write 'this is k.txt' } entryNames << 'a.txt' << 'z.txt' << 'k.txt' end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(entryNames, zf.entries.map { |e| e.to_s }) entries = zf.entries.sort_by { |e| e.name }.reverse entries.each do |e| zf.remove e zf.get_output_stream(e) { |os| os.write 'foo' } end entryNames = entries.map { |e| e.to_s } end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(entryNames, zf.entries.map { |e| e.to_s }) end end def test_streaming fname = ::File.join(::File.expand_path(::File.dirname(__FILE__)), '../README.md') zname = 'test/data/generated/README.zip' Zip::File.open(zname, Zip::File::CREATE) do |zipfile| zipfile.get_output_stream(File.basename(fname)) do |f| f.puts File.read(fname) end end data = nil File.open(zname, 'rb') do |f| Zip::File.open_buffer(f) do |zipfile| zipfile.each do |entry| next unless entry.name =~ /README.md/ data = zipfile.read(entry) end end end assert data assert data =~ /Simonov/ end def test_nonexistant_zip assert_raises(::Zip::Error) do ::Zip::File.open('fake.zip') end end def test_empty_zip assert_raises(::Zip::Error) do ::Zip::File.open(TestFiles::NULL_FILE) end end def test_odd_extra_field entry_count = 0 File.open 'test/data/oddExtraField.zip', 'rb' do |zip_io| Zip::File.open_buffer zip_io.read do |zip| zip.each do |_zip_entry| entry_count += 1 end end end assert_equal 13, entry_count end def test_open_xls_does_not_raise_type_error ::Zip::File.open('test/data/test.xls') end private def assert_contains(zf, entryName, filename = entryName) assert(zf.entries.detect { |e| e.name == entryName } != nil, "entry #{entryName} not in #{zf.entries.join(', ')} in zip file #{zf}") assert_entry_contents(zf, entryName, filename) if File.exist?(filename) end def assert_not_contains(zf, entryName) assert(zf.entries.detect { |e| e.name == entryName }.nil?, "entry #{entryName} in #{zf.entries.join(', ')} in zip file #{zf}") end end