1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
#! /usr/bin/ruby
# coding: utf-8
$: << File.dirname(__FILE__) + '/../lib'
#require 'rubygems'
require 'test/unit'
require 'ole/storage'
require 'digest/sha1'
require 'stringio'
require 'tempfile'
#
# = TODO
#
# These tests could be a lot more complete.
#
# should test resizeable and migrateable IO.
class TestStorageRead < Test::Unit::TestCase
TEST_DIR = File.dirname __FILE__
def setup
@ole = Ole::Storage.open "#{TEST_DIR}/test_word_6.doc", 'rb'
end
def teardown
@ole.close
end
def test_header
# should have further header tests, testing the validation etc.
assert_equal 17, @ole.header.to_a.length
assert_equal 117, @ole.header.dirent_start
assert_equal 1, @ole.header.num_bat
assert_equal 1, @ole.header.num_sbat
assert_equal 0, @ole.header.num_mbat
end
def test_new_without_explicit_mode
open "#{TEST_DIR}/test_word_6.doc", 'rb' do |f|
assert_equal false, Ole::Storage.new(f).writeable
end
end
def capture_warnings
@warn = []
outer_warn = @warn
old_log = Ole::Log
old_verbose = $VERBOSE
begin
$VERBOSE = nil
Ole.const_set :Log, Object.new
# restore for the yield
$VERBOSE = old_verbose
(class << Ole::Log; self; end).send :define_method, :warn do |message|
outer_warn << message
end
yield
ensure
$VERBOSE = nil
Ole.const_set :Log, old_log
$VERBOSE = old_verbose
end
end
def test_invalid
assert_raises Ole::Storage::FormatError do
Ole::Storage.open StringIO.new(0.chr * 1024)
end
assert_raises Ole::Storage::FormatError do
Ole::Storage.open StringIO.new(Ole::Storage::Header::MAGIC + 0.chr * 1024)
end
capture_warnings do
head = Ole::Storage::Header.new
head.threshold = 1024
assert_raises NoMethodError do
Ole::Storage.open StringIO.new(head.to_s + 0.chr * 1024)
end
end
assert_equal ['may not be a valid OLE2 structured storage file'], @warn
end
def test_inspect
assert_match(/#<Ole::Storage io=#<File:.*?test_word_6.doc> root=#<Dirent:"Root Entry">>/, @ole.inspect)
end
def test_fat
# the fat block has all the numbers from 5..118 bar 117
bbat_table = [112] + ((5..118).to_a - [112, 117])
assert_equal bbat_table, @ole.bbat.reject { |i| i >= (1 << 32) - 3 }, 'bbat'
sbat_table = (1..43).to_a - [2, 3]
assert_equal sbat_table, @ole.sbat.reject { |i| i >= (1 << 32) - 3 }, 'sbat'
end
def test_directories
assert_equal 5, @ole.dirents.length, 'have all directories'
# a more complicated one would be good for this
assert_equal 4, @ole.root.children.length, 'properly nested directories'
end
def test_utf16_conversion
assert_equal 'Root Entry', @ole.root.name
assert_equal 'WordDocument', @ole.root.children[2].name
end
def test_read
# the regular String#hash was different on the mac, so asserting
# against full strings. switch this to sha1 instead of this fugly blob
sha1sums = %w[
d3d1cde9eb43ed4b77d197af879f5ca8b8837577
65b75cbdd1f94ade632baeeb0848dec2a342c844
cfc230ec7515892cfdb85e4a173e0ce364094970
ffd859d94647a11b693f06f092d1a2bccc59d50d
]
# test the ole storage type
type = 'Microsoft Word 6.0-Dokument'
assert_equal type, (@ole.root/"\001CompObj").read[32..-1][/([^\x00]+)/m, 1]
# i was actually not loading data correctly before, so carefully check everything here
assert_equal sha1sums, @ole.root.children.map { |child| Digest::SHA1.hexdigest child.read }
end
def test_dirent
dirent = @ole.root.children.first
assert_equal "\001Ole", dirent.name
assert_equal 20, dirent.size
assert_equal '#<Dirent:"Root Entry">', @ole.root.inspect
# exercise Dirent#[]. note that if you use a number, you get the Struct
# fields.
assert_equal dirent, @ole.root["\001Ole"]
assert_equal dirent.name_utf16, dirent[0]
assert_equal nil, @ole.root.time
assert_equal @ole.root.children, @ole.root.to_enum(:each_child).to_a
dirent.open('r') { |f| assert_equal 2, f.first_block }
dirent.open('w') { |f| }
dirent.open('a') { |f| }
end
def test_delete
dirent = @ole.root.children.first
assert_raises(ArgumentError) { @ole.root.delete nil }
assert_equal [dirent], @ole.root.children & [dirent]
assert_equal 20, dirent.size
@ole.root.delete dirent
assert_equal [], @ole.root.children & [dirent]
assert_equal 0, dirent.size
end
end
class TestStorageWrite < Test::Unit::TestCase
TEST_DIR = File.dirname __FILE__
def sha1 str
Digest::SHA1.hexdigest str
end
# try and test all the various things the #flush function does
def test_flush
end
# FIXME
# don't really want to lock down the actual internal api's yet. this will just
# ensure for the time being that #flush continues to work properly. need a host
# of checks involving writes that resize their file bigger/smaller, that resize
# the bats to more blocks, that resizes the sb_blocks, that has migration etc.
def test_write_hash
io = StringIO.open open("#{TEST_DIR}/test_word_6.doc", 'rb', &:read)
assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
Ole::Storage.open(io, :update_timestamps => false) { }
# hash changed. used to be efa8cfaf833b30b1d1d9381771ddaafdfc95305c
# thats because i now truncate the io, and am probably removing some trailing
# allocated available blocks.
assert_equal 'a39e3c4041b8a893c753d50793af8d21ca8f0a86', sha1(io.string)
# add a repack test here
Ole::Storage.open io, :update_timestamps => false, &:repack
assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
end
def test_plain_repack
io = StringIO.open open("#{TEST_DIR}/test_word_6.doc", 'rb', &:read)
assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
Ole::Storage.open io, :update_timestamps => false, &:repack
# note equivalence to the above flush, repack, flush
assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
# lets do it again using memory backing
Ole::Storage.open(io, :update_timestamps => false) { |ole| ole.repack :mem }
# note equivalence to the above flush, repack, flush
assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
assert_raises ArgumentError do
Ole::Storage.open(io, :update_timestamps => false) { |ole| ole.repack :typo }
end
end
def test_create_from_scratch_hash
io = StringIO.new(''.dup)
Ole::Storage.open(io) { }
assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
# more repack test, note invariance
Ole::Storage.open io, :update_timestamps => false, &:repack
assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
end
def test_create_dirent
Ole::Storage.open StringIO.new do |ole|
dirent = Ole::Storage::Dirent.new ole, :name => 'test name', :type => :dir
assert_equal 'test name', dirent.name
assert_equal :dir, dirent.type
# for a dirent created from scratch, type_id is currently not set until serialization:
assert_equal 0, dirent.type_id
dirent.to_s
assert_equal 1, dirent.type_id
assert_raises(ArgumentError) { Ole::Storage::Dirent.new ole, :type => :bogus }
end
end
end
|