ruby-ole 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,26 @@
2
2
  #
3
3
  # A file with general support functions used by most files in the project.
4
4
  #
5
+ # These are the only methods added to other classes.
6
+ #
5
7
 
6
8
  require 'logger'
9
+ require 'stringio'
10
+ require 'enumerator'
11
+
12
+ class String # :nodoc:
13
+ # plural of String#index. returns all offsets of +string+. rename to indices?
14
+ #
15
+ # note that it doesn't check for overlapping values.
16
+ def indexes string
17
+ # in some ways i'm surprised that $~ works properly in this case...
18
+ to_enum(:scan, /#{Regexp.quote string}/m).map { $~.begin 0 }
19
+ end
20
+ end
7
21
 
8
22
  class File # :nodoc:
9
- # for consistency with StringIO and others. makes more sense than forcing
10
- # them to provide a #stat
23
+ # for interface consistency with StringIO etc (rather than adding #stat
24
+ # to them). used by RangesIO.
11
25
  def size
12
26
  stat.size
13
27
  end
@@ -32,8 +46,18 @@ module Enumerable # :nodoc:
32
46
  end
33
47
  end
34
48
 
49
+ # move to support?
50
+ class IO # :nodoc:
51
+ def self.copy src, dst
52
+ until src.eof?
53
+ buf = src.read(4096)
54
+ dst.write buf
55
+ end
56
+ end
57
+ end
58
+
35
59
  class Logger # :nodoc:
36
- # A helper method for creating <tt>Logger</tt>s which produce call stack
60
+ # A helper method for creating a +Logger+ which produce call stack
37
61
  # in their output
38
62
  def self.new_with_callstack logdev=STDERR
39
63
  log = Logger.new logdev
@@ -49,3 +73,88 @@ class Logger # :nodoc:
49
73
  end
50
74
  end
51
75
 
76
+ # Include this module into a class that defines #each_child. It should
77
+ # maybe use #each instead, but its easier to be more specific, and use
78
+ # an alias.
79
+ #
80
+ # I don't want to force the class to cache children (eg where children
81
+ # are loaded on request in pst), because that forces the whole tree to
82
+ # be loaded. So, the methods should only call #each_child once, and
83
+ # breadth first iteration holds its own copy of the children around.
84
+ #
85
+ # Main methods are #recursive, and #to_tree
86
+ module RecursivelyEnumerable
87
+ def each_recursive_depth_first(&block)
88
+ each_child do |child|
89
+ yield child
90
+ if child.respond_to? :each_recursive_depth_first
91
+ child.each_recursive_depth_first(&block)
92
+ end
93
+ end
94
+ end
95
+
96
+ def each_recursive_breadth_first(&block)
97
+ children = []
98
+ each_child do |child|
99
+ children << child if child.respond_to? :each_recursive_breadth_first
100
+ yield child
101
+ end
102
+ children.each { |child| child.each_recursive_breadth_first(&block) }
103
+ end
104
+
105
+ def each_recursive mode=:depth_first, &block
106
+ # we always actually yield ourself (the tree root) before recursing
107
+ yield self
108
+ send "each_recursive_#{mode}", &block
109
+ end
110
+
111
+ # the idea of this function, is to allow use of regular Enumerable methods
112
+ # in a recursive fashion. eg:
113
+ #
114
+ # # just looks at top level children
115
+ # root.find { |child| child.some_condition? }
116
+ # # recurse into all children getting non-folders, breadth first
117
+ # root.recursive(:breadth_first).select { |child| !child.folder? }
118
+ # # just get everything
119
+ # items = root.recursive.to_a
120
+ #
121
+ def recursive mode=:depth_first
122
+ to_enum(:each_recursive, mode)
123
+ end
124
+
125
+ # streams a "tree" form of the recursively enumerable structure to +io+, or
126
+ # return a string form instead if +io+ is not specified.
127
+ #
128
+ # mostly a debugging aid. can specify a different +method+ to call if desired,
129
+ # though it should return a single line string.
130
+ def to_tree io=nil, method=:inspect
131
+ unless io
132
+ to_tree io = StringIO.new
133
+ return io.string
134
+ end
135
+ io << "- #{send method}\n"
136
+ to_tree_helper io, method, ' '
137
+ end
138
+
139
+ def to_tree_helper io, method, prefix
140
+ # i need to know when i get to the last child. use delay to know
141
+ child = nil
142
+ each_child do |next_child|
143
+ if child
144
+ io << "#{prefix}|- #{child.send method}\n"
145
+ if child.respond_to? :to_tree_helper
146
+ child.to_tree_helper io, method, prefix + '| '
147
+ end
148
+ end
149
+ child = next_child
150
+ end
151
+ return unless child
152
+ # child is the last child
153
+ io << "#{prefix}\\- #{child.send method}\n"
154
+ if child.respond_to? :to_tree_helper
155
+ child.to_tree_helper io, method, prefix + ' '
156
+ end
157
+ end
158
+ protected :to_tree_helper
159
+ end
160
+
@@ -6,8 +6,91 @@ require 'ole/base'
6
6
  module Ole # :nodoc:
7
7
  # FIXME
8
8
  module Types
9
+ #
10
+ # The OLE variant types, extracted from
11
+ # http://www.marin.clara.net/COM/variant_type_definitions.htm.
12
+ #
13
+ # A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
14
+ #
15
+ # Use like:
16
+ #
17
+ # p Ole::Types::Variant::NAMES[0x001f] => 'VT_LPWSTR'
18
+ # p Ole::Types::VT_DATE # => 7
19
+ #
20
+ # The serialization / deserialization functions should be fixed to make it easier
21
+ # to work with. like
22
+ #
23
+ # Ole::Types.from_str(VT_DATE, data) # and
24
+ # Ole::Types.to_str(VT_DATE, data)
25
+ #
26
+ # Or similar, rather than having to do VT_* <=> ad hoc class name etc as it is
27
+ # currently.
28
+ #
29
+ module Variant
30
+ NAMES = {
31
+ 0x0000 => 'VT_EMPTY',
32
+ 0x0001 => 'VT_NULL',
33
+ 0x0002 => 'VT_I2',
34
+ 0x0003 => 'VT_I4',
35
+ 0x0004 => 'VT_R4',
36
+ 0x0005 => 'VT_R8',
37
+ 0x0006 => 'VT_CY',
38
+ 0x0007 => 'VT_DATE',
39
+ 0x0008 => 'VT_BSTR',
40
+ 0x0009 => 'VT_DISPATCH',
41
+ 0x000a => 'VT_ERROR',
42
+ 0x000b => 'VT_BOOL',
43
+ 0x000c => 'VT_VARIANT',
44
+ 0x000d => 'VT_UNKNOWN',
45
+ 0x000e => 'VT_DECIMAL',
46
+ 0x0010 => 'VT_I1',
47
+ 0x0011 => 'VT_UI1',
48
+ 0x0012 => 'VT_UI2',
49
+ 0x0013 => 'VT_UI4',
50
+ 0x0014 => 'VT_I8',
51
+ 0x0015 => 'VT_UI8',
52
+ 0x0016 => 'VT_INT',
53
+ 0x0017 => 'VT_UINT',
54
+ 0x0018 => 'VT_VOID',
55
+ 0x0019 => 'VT_HRESULT',
56
+ 0x001a => 'VT_PTR',
57
+ 0x001b => 'VT_SAFEARRAY',
58
+ 0x001c => 'VT_CARRAY',
59
+ 0x001d => 'VT_USERDEFINED',
60
+ 0x001e => 'VT_LPSTR',
61
+ 0x001f => 'VT_LPWSTR',
62
+ 0x0040 => 'VT_FILETIME',
63
+ 0x0041 => 'VT_BLOB',
64
+ 0x0042 => 'VT_STREAM',
65
+ 0x0043 => 'VT_STORAGE',
66
+ 0x0044 => 'VT_STREAMED_OBJECT',
67
+ 0x0045 => 'VT_STORED_OBJECT',
68
+ 0x0046 => 'VT_BLOB_OBJECT',
69
+ 0x0047 => 'VT_CF',
70
+ 0x0048 => 'VT_CLSID',
71
+ 0x0fff => 'VT_ILLEGALMASKED',
72
+ 0x0fff => 'VT_TYPEMASK',
73
+ 0x1000 => 'VT_VECTOR',
74
+ 0x2000 => 'VT_ARRAY',
75
+ 0x4000 => 'VT_BYREF',
76
+ 0x8000 => 'VT_RESERVED',
77
+ 0xffff => 'VT_ILLEGAL'
78
+ }
79
+
80
+ module Constants
81
+ NAMES.each { |num, name| const_set name, num }
82
+ end
83
+ end
84
+
85
+ include Variant::Constants
86
+
87
+ # the rest of this file is all a bit of adhoc marshal/unmarshal stuff
88
+
89
+ # for VT_LPWSTR
9
90
  FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
10
91
  TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
92
+
93
+ # for VT_FILETIME
11
94
  EPOCH = DateTime.parse '1601-01-01'
12
95
  # Create a +DateTime+ object from a struct +FILETIME+
13
96
  # (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
@@ -18,7 +101,7 @@ module Ole # :nodoc:
18
101
  low, high = str.unpack 'L2'
19
102
  # we ignore these, without even warning about it
20
103
  return nil if low == 0 and high == 0
21
- time = EPOCH + (high * (1 << 32) + low) * 1e-7 / 86400 rescue return
104
+ time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
22
105
  # extra sanity check...
23
106
  unless (1800...2100) === time.year
24
107
  Log.warn "ignoring unlikely time value #{time.to_s}"
@@ -27,6 +110,18 @@ module Ole # :nodoc:
27
110
  time
28
111
  end
29
112
 
113
+ # +time+ should be able to be either a Time, Date, or DateTime.
114
+ def self.save_time time
115
+ # i think i'll convert whatever i get to be a datetime, because of
116
+ # the covered range.
117
+ return 0.chr * 8 unless time
118
+ time = time.send(:to_datetime) if Time === time
119
+ bignum = ((time - Ole::Types::EPOCH) * 86400 * 1e7.to_i)
120
+ high, low = bignum.divmod 1 << 32
121
+ [low, high].pack 'L2'
122
+ end
123
+
124
+ # for VT_CLSID
30
125
  # Convert a binary guid into a plain string (will move to proper class later).
31
126
  def self.load_guid str
32
127
  "{%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}}" % str.unpack('L S S CC C6')
@@ -0,0 +1,877 @@
1
+ #! /usr/bin/ruby
2
+
3
+ #
4
+ # = NOTE
5
+ #
6
+ # This file was originally called "zipfilesystemtest.rb", and was part of
7
+ # the test case for the "rubyzip" project.
8
+ #
9
+ # As I borrowed the smart idea of using a filesystem style interface, it
10
+ # only seemed right that I appropriate the test case in addition :). It is
11
+ # a testament to the cleanliness of the original api & tests as to how
12
+ # easy it was to repurpose it for this project.
13
+ #
14
+ # I have made some modifications to the file due to some differences in the
15
+ # capabilities of zip vs ole, but the majority of the copyright and credit
16
+ # still goes to Thomas. His original copyright message:
17
+ #
18
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
19
+ # rubyzip is free software; you can redistribute it and/or
20
+ # modify it under the terms of the ruby license.
21
+ #
22
+
23
+ TEST_DIR = File.dirname __FILE__
24
+ $:.unshift "#{TEST_DIR}/../lib"
25
+
26
+ require 'ole/file_system'
27
+ require 'test/unit'
28
+
29
+ module ExtraAssertions
30
+
31
+ def assert_forwarded(anObject, method, retVal, *expectedArgs)
32
+ callArgs = nil
33
+ setCallArgsProc = proc { |args| callArgs = args }
34
+ anObject.instance_eval <<-"end_eval"
35
+ alias #{method}_org #{method}
36
+ def #{method}(*args)
37
+ ObjectSpace._id2ref(#{setCallArgsProc.object_id}).call(args)
38
+ ObjectSpace._id2ref(#{retVal.object_id})
39
+ end
40
+ end_eval
41
+
42
+ assert_equal(retVal, yield) # Invoke test
43
+ assert_equal(expectedArgs, callArgs)
44
+ ensure
45
+ anObject.instance_eval "alias #{method} #{method}_org"
46
+ end
47
+
48
+ end
49
+
50
+ class OleFsNonmutatingTest < Test::Unit::TestCase
51
+ def setup
52
+ @ole = Ole::Storage.open TEST_DIR + '/oleWithDirs.ole'
53
+ end
54
+
55
+ def teardown
56
+ @ole.close if @ole
57
+ end
58
+
59
+ =begin
60
+ def test_umask
61
+ assert_equal(File.umask, @ole.file.umask)
62
+ @ole.file.umask(0006)
63
+ end
64
+ =end
65
+
66
+ def test_exists?
67
+ assert(! @ole.file.exists?("notAFile"))
68
+ assert(@ole.file.exists?("file1"))
69
+ assert(@ole.file.exists?("dir1"))
70
+ assert(@ole.file.exists?("dir1/"))
71
+ assert(@ole.file.exists?("dir1/file12"))
72
+ assert(@ole.file.exist?("dir1/file12")) # notice, tests exist? alias of exists? !
73
+
74
+ @ole.dir.chdir "dir1/"
75
+ assert(!@ole.file.exists?("file1"))
76
+ assert(@ole.file.exists?("file12"))
77
+ end
78
+
79
+ def test_open_read
80
+ blockCalled = false
81
+ @ole.file.open("file1", "r") {
82
+ |f|
83
+ blockCalled = true
84
+ assert_equal("this is the entry 'file1' in my test archive!",
85
+ f.readline.chomp)
86
+ }
87
+ assert(blockCalled)
88
+
89
+ blockCalled = false
90
+ @ole.dir.chdir "dir2"
91
+ @ole.file.open("file21", "r") {
92
+ |f|
93
+ blockCalled = true
94
+ assert_equal("this is the entry 'dir2/file21' in my test archive!",
95
+ f.readline.chomp)
96
+ }
97
+ assert(blockCalled)
98
+ @ole.dir.chdir "/"
99
+
100
+ assert_raise(Errno::ENOENT) {
101
+ @ole.file.open("noSuchEntry")
102
+ }
103
+
104
+ begin
105
+ is = @ole.file.open("file1")
106
+ assert_equal("this is the entry 'file1' in my test archive!",
107
+ is.readline.chomp)
108
+ ensure
109
+ is.close if is
110
+ end
111
+ end
112
+
113
+ def test_new
114
+ begin
115
+ is = @ole.file.new("file1")
116
+ assert_equal("this is the entry 'file1' in my test archive!",
117
+ is.readline.chomp)
118
+ ensure
119
+ is.close if is
120
+ end
121
+ begin
122
+ is = @ole.file.new("file1") {
123
+ fail "should not call block"
124
+ }
125
+ ensure
126
+ is.close if is
127
+ end
128
+ end
129
+
130
+ # currently commented out because I've taken the approach of
131
+ # using implicit NameError rather than explicit NotImplementedError.
132
+ =begin
133
+ def test_symlink
134
+ assert_raise(NotImplementedError) {
135
+ @ole.file.symlink("file1", "aSymlink")
136
+ }
137
+ end
138
+ =end
139
+
140
+ def test_size
141
+ assert_raise(Errno::ENOENT) { @ole.file.size("notAFile") }
142
+ assert_equal(72, @ole.file.size("file1"))
143
+ assert_equal(0, @ole.file.size("dir2/dir21"))
144
+
145
+ assert_equal(72, @ole.file.stat("file1").size)
146
+ assert_equal(0, @ole.file.stat("dir2/dir21").size)
147
+ end
148
+
149
+ =begin
150
+ def test_size?
151
+ assert_equal(nil, @ole.file.size?("notAFile"))
152
+ assert_equal(72, @ole.file.size?("file1"))
153
+ assert_equal(nil, @ole.file.size?("dir2/dir21"))
154
+
155
+ assert_equal(72, @ole.file.stat("file1").size?)
156
+ assert_equal(nil, @ole.file.stat("dir2/dir21").size?)
157
+ end
158
+ =end
159
+
160
+ def test_file?
161
+ assert(@ole.file.file?("file1"))
162
+ assert(@ole.file.file?("dir2/file21"))
163
+ assert(! @ole.file.file?("dir1"))
164
+ assert(! @ole.file.file?("dir1/dir11"))
165
+
166
+ assert(@ole.file.stat("file1").file?)
167
+ assert(@ole.file.stat("dir2/file21").file?)
168
+ assert(! @ole.file.stat("dir1").file?)
169
+ assert(! @ole.file.stat("dir1/dir11").file?)
170
+ end
171
+
172
+ =begin
173
+ include ExtraAssertions
174
+
175
+ def test_dirname
176
+ assert_forwarded(File, :dirname, "retVal", "a/b/c/d") {
177
+ @ole.file.dirname("a/b/c/d")
178
+ }
179
+ end
180
+
181
+ def test_basename
182
+ assert_forwarded(File, :basename, "retVal", "a/b/c/d") {
183
+ @ole.file.basename("a/b/c/d")
184
+ }
185
+ end
186
+
187
+ def test_split
188
+ assert_forwarded(File, :split, "retVal", "a/b/c/d") {
189
+ @ole.file.split("a/b/c/d")
190
+ }
191
+ end
192
+
193
+ def test_join
194
+ assert_equal("a/b/c", @ole.file.join("a/b", "c"))
195
+ assert_equal("a/b/c/d", @ole.file.join("a/b", "c/d"))
196
+ assert_equal("/c/d", @ole.file.join("", "c/d"))
197
+ assert_equal("a/b/c/d", @ole.file.join("a", "b", "c", "d"))
198
+ end
199
+
200
+ def test_utime
201
+ t_now = Time.now
202
+ t_bak = @ole.file.mtime("file1")
203
+ @ole.file.utime(t_now, "file1")
204
+ assert_equal(t_now, @ole.file.mtime("file1"))
205
+ @ole.file.utime(t_bak, "file1")
206
+ assert_equal(t_bak, @ole.file.mtime("file1"))
207
+ end
208
+
209
+
210
+ def assert_always_false(operation)
211
+ assert(! @ole.file.send(operation, "noSuchFile"))
212
+ assert(! @ole.file.send(operation, "file1"))
213
+ assert(! @ole.file.send(operation, "dir1"))
214
+ assert(! @ole.file.stat("file1").send(operation))
215
+ assert(! @ole.file.stat("dir1").send(operation))
216
+ end
217
+
218
+ def assert_true_if_entry_exists(operation)
219
+ assert(! @ole.file.send(operation, "noSuchFile"))
220
+ assert(@ole.file.send(operation, "file1"))
221
+ assert(@ole.file.send(operation, "dir1"))
222
+ assert(@ole.file.stat("file1").send(operation))
223
+ assert(@ole.file.stat("dir1").send(operation))
224
+ end
225
+
226
+ def test_pipe?
227
+ assert_always_false(:pipe?)
228
+ end
229
+
230
+ def test_blockdev?
231
+ assert_always_false(:blockdev?)
232
+ end
233
+
234
+ def test_symlink?
235
+ assert_always_false(:symlink?)
236
+ end
237
+
238
+ def test_socket?
239
+ assert_always_false(:socket?)
240
+ end
241
+
242
+ def test_chardev?
243
+ assert_always_false(:chardev?)
244
+ end
245
+
246
+ def test_truncate
247
+ assert_raise(StandardError, "truncate not supported") {
248
+ @ole.file.truncate("file1", 100)
249
+ }
250
+ end
251
+
252
+ def assert_e_n_o_e_n_t(operation, args = ["NoSuchFile"])
253
+ assert_raise(Errno::ENOENT) {
254
+ @ole.file.send(operation, *args)
255
+ }
256
+ end
257
+
258
+ def test_ftype
259
+ assert_e_n_o_e_n_t(:ftype)
260
+ assert_equal("file", @ole.file.ftype("file1"))
261
+ assert_equal("directory", @ole.file.ftype("dir1/dir11"))
262
+ assert_equal("directory", @ole.file.ftype("dir1/dir11/"))
263
+ end
264
+ =end
265
+
266
+ def test_directory?
267
+ assert(! @ole.file.directory?("notAFile"))
268
+ assert(! @ole.file.directory?("file1"))
269
+ assert(! @ole.file.directory?("dir1/file11"))
270
+ assert(@ole.file.directory?("dir1"))
271
+ assert(@ole.file.directory?("dir1/"))
272
+ assert(@ole.file.directory?("dir2/dir21"))
273
+
274
+ assert(! @ole.file.stat("file1").directory?)
275
+ assert(! @ole.file.stat("dir1/file11").directory?)
276
+ assert(@ole.file.stat("dir1").directory?)
277
+ assert(@ole.file.stat("dir1/").directory?)
278
+ assert(@ole.file.stat("dir2/dir21").directory?)
279
+ end
280
+
281
+ =begin
282
+ def test_chown
283
+ assert_equal(2, @ole.file.chown(1,2, "dir1", "file1"))
284
+ assert_equal(1, @ole.file.stat("dir1").uid)
285
+ assert_equal(2, @ole.file.stat("dir1").gid)
286
+ assert_equal(2, @ole.file.chown(nil, nil, "dir1", "file1"))
287
+ end
288
+
289
+ def test_zero?
290
+ assert(! @ole.file.zero?("notAFile"))
291
+ assert(! @ole.file.zero?("file1"))
292
+ assert(@ole.file.zero?("dir1"))
293
+ blockCalled = false
294
+ ZipFile.open("data/generated/5entry.zip") {
295
+ |zf|
296
+ blockCalled = true
297
+ assert(zf.file.zero?("data/generated/empty.txt"))
298
+ }
299
+ assert(blockCalled)
300
+
301
+ assert(! @ole.file.stat("file1").zero?)
302
+ assert(@ole.file.stat("dir1").zero?)
303
+ blockCalled = false
304
+ ZipFile.open("data/generated/5entry.zip") {
305
+ |zf|
306
+ blockCalled = true
307
+ assert(zf.file.stat("data/generated/empty.txt").zero?)
308
+ }
309
+ assert(blockCalled)
310
+ end
311
+ =end
312
+
313
+ def test_expand_path
314
+ assert_equal("/", @ole.file.expand_path("."))
315
+ @ole.dir.chdir "dir1"
316
+ assert_equal("/dir1", @ole.file.expand_path("."))
317
+ assert_equal("/dir1/file12", @ole.file.expand_path("file12"))
318
+ assert_equal("/", @ole.file.expand_path(".."))
319
+ assert_equal("/dir2/dir21", @ole.file.expand_path("../dir2/dir21"))
320
+ end
321
+
322
+ =begin
323
+ def test_mtime
324
+ assert_equal(Time.at(1027694306),
325
+ @ole.file.mtime("dir2/file21"))
326
+ assert_equal(Time.at(1027690863),
327
+ @ole.file.mtime("dir2/dir21"))
328
+ assert_raise(Errno::ENOENT) {
329
+ @ole.file.mtime("noSuchEntry")
330
+ }
331
+
332
+ assert_equal(Time.at(1027694306),
333
+ @ole.file.stat("dir2/file21").mtime)
334
+ assert_equal(Time.at(1027690863),
335
+ @ole.file.stat("dir2/dir21").mtime)
336
+ end
337
+
338
+ def test_ctime
339
+ assert_nil(@ole.file.ctime("file1"))
340
+ assert_nil(@ole.file.stat("file1").ctime)
341
+ end
342
+
343
+ def test_atime
344
+ assert_nil(@ole.file.atime("file1"))
345
+ assert_nil(@ole.file.stat("file1").atime)
346
+ end
347
+
348
+ def test_readable?
349
+ assert(! @ole.file.readable?("noSuchFile"))
350
+ assert(@ole.file.readable?("file1"))
351
+ assert(@ole.file.readable?("dir1"))
352
+ assert(@ole.file.stat("file1").readable?)
353
+ assert(@ole.file.stat("dir1").readable?)
354
+ end
355
+
356
+ def test_readable_real?
357
+ assert(! @ole.file.readable_real?("noSuchFile"))
358
+ assert(@ole.file.readable_real?("file1"))
359
+ assert(@ole.file.readable_real?("dir1"))
360
+ assert(@ole.file.stat("file1").readable_real?)
361
+ assert(@ole.file.stat("dir1").readable_real?)
362
+ end
363
+
364
+ def test_writable?
365
+ assert(! @ole.file.writable?("noSuchFile"))
366
+ assert(@ole.file.writable?("file1"))
367
+ assert(@ole.file.writable?("dir1"))
368
+ assert(@ole.file.stat("file1").writable?)
369
+ assert(@ole.file.stat("dir1").writable?)
370
+ end
371
+
372
+ def test_writable_real?
373
+ assert(! @ole.file.writable_real?("noSuchFile"))
374
+ assert(@ole.file.writable_real?("file1"))
375
+ assert(@ole.file.writable_real?("dir1"))
376
+ assert(@ole.file.stat("file1").writable_real?)
377
+ assert(@ole.file.stat("dir1").writable_real?)
378
+ end
379
+
380
+ def test_executable?
381
+ assert(! @ole.file.executable?("noSuchFile"))
382
+ assert(! @ole.file.executable?("file1"))
383
+ assert(@ole.file.executable?("dir1"))
384
+ assert(! @ole.file.stat("file1").executable?)
385
+ assert(@ole.file.stat("dir1").executable?)
386
+ end
387
+
388
+ def test_executable_real?
389
+ assert(! @ole.file.executable_real?("noSuchFile"))
390
+ assert(! @ole.file.executable_real?("file1"))
391
+ assert(@ole.file.executable_real?("dir1"))
392
+ assert(! @ole.file.stat("file1").executable_real?)
393
+ assert(@ole.file.stat("dir1").executable_real?)
394
+ end
395
+
396
+ def test_owned?
397
+ assert_true_if_entry_exists(:owned?)
398
+ end
399
+
400
+ def test_grpowned?
401
+ assert_true_if_entry_exists(:grpowned?)
402
+ end
403
+
404
+ def test_setgid?
405
+ assert_always_false(:setgid?)
406
+ end
407
+
408
+ def test_setuid?
409
+ assert_always_false(:setgid?)
410
+ end
411
+
412
+ def test_sticky?
413
+ assert_always_false(:sticky?)
414
+ end
415
+
416
+ def test_stat
417
+ s = @ole.file.stat("file1")
418
+ assert(s.kind_of?(File::Stat)) # It pretends
419
+ assert_raise(Errno::ENOENT, "No such file or directory - noSuchFile") {
420
+ @ole.file.stat("noSuchFile")
421
+ }
422
+ end
423
+
424
+ def test_lstat
425
+ assert(@ole.file.lstat("file1").file?)
426
+ end
427
+
428
+
429
+ def test_chmod
430
+ assert_raise(Errno::ENOENT, "No such file or directory - noSuchFile") {
431
+ @ole.file.chmod(0644, "file1", "NoSuchFile")
432
+ }
433
+ assert_equal(2, @ole.file.chmod(0644, "file1", "dir1"))
434
+ end
435
+
436
+ def test_pipe
437
+ assert_raise(NotImplementedError) {
438
+ @ole.file.pipe
439
+ }
440
+ end
441
+
442
+ def test_foreach
443
+ ZipFile.open("data/generated/zipWithDir.zip") {
444
+ |zf|
445
+ ref = []
446
+ File.foreach("data/file1.txt") { |e| ref << e }
447
+
448
+ index = 0
449
+ zf.file.foreach("data/file1.txt") {
450
+ |l|
451
+ assert_equal(ref[index], l)
452
+ index = index.next
453
+ }
454
+ assert_equal(ref.size, index)
455
+ }
456
+
457
+ ZipFile.open("data/generated/zipWithDir.zip") {
458
+ |zf|
459
+ ref = []
460
+ File.foreach("data/file1.txt", " ") { |e| ref << e }
461
+
462
+ index = 0
463
+ zf.file.foreach("data/file1.txt", " ") {
464
+ |l|
465
+ assert_equal(ref[index], l)
466
+ index = index.next
467
+ }
468
+ assert_equal(ref.size, index)
469
+ }
470
+ end
471
+
472
+ def test_popen
473
+ cmd = /mswin/i =~ RUBY_PLATFORM ? 'dir' : 'ls'
474
+
475
+ assert_equal(File.popen(cmd) { |f| f.read },
476
+ @ole.file.popen(cmd) { |f| f.read })
477
+ end
478
+
479
+ # Can be added later
480
+ # def test_select
481
+ # fail "implement test"
482
+ # end
483
+
484
+ def test_readlines
485
+ ZipFile.open("data/generated/zipWithDir.zip") {
486
+ |zf|
487
+ assert_equal(File.readlines("data/file1.txt"),
488
+ zf.file.readlines("data/file1.txt"))
489
+ }
490
+ end
491
+
492
+ def test_read
493
+ ZipFile.open("data/generated/zipWithDir.zip") {
494
+ |zf|
495
+ assert_equal(File.read("data/file1.txt"),
496
+ zf.file.read("data/file1.txt"))
497
+ }
498
+ end
499
+ =end
500
+ end
501
+
502
+ class OleFsFileStatTest < Test::Unit::TestCase
503
+
504
+ def setup
505
+ @ole = Ole::Storage.open TEST_DIR + '/oleWithDirs.ole'
506
+ end
507
+
508
+ def teardown
509
+ @ole.close if @ole
510
+ end
511
+
512
+ def test_blocks
513
+ assert_equal(2, @ole.file.stat("file1").blocks)
514
+ end
515
+
516
+ def test_ino
517
+ assert_equal(0, @ole.file.stat("file1").ino)
518
+ end
519
+
520
+ def test_uid
521
+ assert_equal(0, @ole.file.stat("file1").uid)
522
+ end
523
+
524
+ def test_gid
525
+ assert_equal(0, @ole.file.stat("file1").gid)
526
+ end
527
+
528
+ def test_ftype
529
+ assert_equal("file", @ole.file.stat("file1").ftype)
530
+ assert_equal("directory", @ole.file.stat("dir1").ftype)
531
+ end
532
+
533
+ =begin
534
+ def test_mode
535
+ assert_equal(0600, @ole.file.stat("file1").mode & 0777)
536
+ assert_equal(0600, @ole.file.stat("file1").mode & 0777)
537
+ assert_equal(0755, @ole.file.stat("dir1").mode & 0777)
538
+ assert_equal(0755, @ole.file.stat("dir1").mode & 0777)
539
+ end
540
+ =end
541
+
542
+ def test_dev
543
+ assert_equal(0, @ole.file.stat("file1").dev)
544
+ end
545
+
546
+ def test_rdev
547
+ assert_equal(0, @ole.file.stat("file1").rdev)
548
+ end
549
+
550
+ def test_rdev_major
551
+ assert_equal(0, @ole.file.stat("file1").rdev_major)
552
+ end
553
+
554
+ def test_rdev_minor
555
+ assert_equal(0, @ole.file.stat("file1").rdev_minor)
556
+ end
557
+
558
+ def test_nlink
559
+ assert_equal(1, @ole.file.stat("file1").nlink)
560
+ end
561
+
562
+ def test_blksize
563
+ assert_equal(64, @ole.file.stat("file1").blksize)
564
+ end
565
+
566
+ end
567
+
568
+ class OleFsFileMutatingTest < Test::Unit::TestCase
569
+ def setup
570
+ # we use an in memory copy of the file instead of the original
571
+ # file based.
572
+ @io = StringIO.new File.read(TEST_DIR + '/oleWithDirs.ole')
573
+ end
574
+
575
+ def teardown
576
+ @io.close if @io
577
+ end
578
+
579
+ def test_delete
580
+ do_test_delete_or_unlink(:delete)
581
+ end
582
+
583
+ def test_unlink
584
+ do_test_delete_or_unlink(:unlink)
585
+ end
586
+
587
+ def test_open_write
588
+ Ole::Storage.open(@io) {
589
+ |zf|
590
+
591
+ zf.file.open("test_open_write_entry", "w") {
592
+ |f|
593
+ blockCalled = true
594
+ f.write "This is what I'm writing"
595
+ }
596
+ assert_equal("This is what I'm writing",
597
+ zf.file.read("test_open_write_entry"))
598
+
599
+ # Test with existing entry
600
+ zf.file.open("file1", "w") {
601
+ |f|
602
+ blockCalled = true
603
+ f.write "This is what I'm writing too"
604
+ }
605
+ assert_equal("This is what I'm writing too",
606
+ zf.file.read("file1"))
607
+ }
608
+ end
609
+
610
+ def test_rename
611
+ Ole::Storage.open(@io) {
612
+ |zf|
613
+ assert_raise(Errno::ENOENT, "") {
614
+ zf.file.rename("NoSuchFile", "bimse")
615
+ }
616
+ zf.file.rename("file1", "newNameForFile1")
617
+ # lets also try moving a file to a different directory,
618
+ # and renaming a directory
619
+ zf.file.rename('/dir1/file11', '/dir1/dir11/file111')
620
+ zf.file.rename('dir1', 'dir9')
621
+ }
622
+
623
+ Ole::Storage.open(@io) {
624
+ |zf|
625
+ assert(! zf.file.exists?("file1"))
626
+ assert(zf.file.exists?("newNameForFile1"))
627
+ assert(zf.file.exists?("dir9/dir11/file111"))
628
+ }
629
+ end
630
+
631
+ def do_test_delete_or_unlink(symbol)
632
+ Ole::Storage.open(@io) {
633
+ |zf|
634
+ assert(zf.file.exists?("dir2/dir21/dir221/file2221"))
635
+ zf.file.send(symbol, "dir2/dir21/dir221/file2221")
636
+ assert(! zf.file.exists?("dir2/dir21/dir221/file2221"))
637
+
638
+ assert(zf.file.exists?("dir1/file11"))
639
+ assert(zf.file.exists?("dir1/file12"))
640
+ zf.file.send(symbol, "dir1/file11", "dir1/file12")
641
+ assert(! zf.file.exists?("dir1/file11"))
642
+ assert(! zf.file.exists?("dir1/file12"))
643
+
644
+ assert_raise(Errno::ENOENT) { zf.file.send(symbol, "noSuchFile") }
645
+ assert_raise(Errno::EISDIR) { zf.file.send(symbol, "dir1/dir11") }
646
+ assert_raise(Errno::EISDIR) { zf.file.send(symbol, "dir1/dir11/") }
647
+ }
648
+
649
+ Ole::Storage.open(@io) {
650
+ |zf|
651
+ assert(! zf.file.exists?("dir2/dir21/dir221/file2221"))
652
+ assert(! zf.file.exists?("dir1/file11"))
653
+ assert(! zf.file.exists?("dir1/file12"))
654
+
655
+ assert(zf.file.exists?("dir1/dir11"))
656
+ assert(zf.file.exists?("dir1/dir11/"))
657
+ }
658
+ end
659
+
660
+ end
661
+
662
+ class ZipFsDirectoryTest < Test::Unit::TestCase
663
+ TEST_ZIP = "zipWithDirs_copy.zip"
664
+
665
+ def setup
666
+ # we use an in memory copy of the file instead of the original
667
+ # file based.
668
+ @io = StringIO.new File.read(TEST_DIR + '/oleWithDirs.ole')
669
+ end
670
+
671
+ def teardown
672
+ @io.close if @io
673
+ end
674
+
675
+ def test_delete
676
+ Ole::Storage.open(@io) {
677
+ |zf|
678
+ assert_raise(Errno::ENOENT, "No such file or directory - NoSuchFile.txt") {
679
+ zf.dir.delete("NoSuchFile.txt")
680
+ }
681
+ # see explanation below, touch a && ruby -e 'Dir.delete "a"' gives ENOTDIR not EINVAL
682
+ assert_raise(Errno::ENOTDIR, "Invalid argument - file1") {
683
+ zf.dir.delete("file1")
684
+ }
685
+ assert(zf.file.exists?("dir1"))
686
+ #zf.dir.delete("dir1")
687
+ #assert(! zf.file.exists?("dir1"))
688
+ # ^ this was allowed in zipfilesystem, but my code follows Dir.delete, and requires that
689
+ # the directory be empty first. need to delete recursively if you want other behaviour.
690
+ assert_raises(Errno::ENOTEMPTY) { zf.dir.delete('dir1') }
691
+ }
692
+ end
693
+
694
+ def test_mkdir
695
+ Ole::Storage.open(@io) {
696
+ |zf|
697
+ assert_raise(Errno::EEXIST, "File exists - dir1") {
698
+ zf.dir.mkdir("file1")
699
+ }
700
+ assert_raise(Errno::EEXIST, "File exists - dir1") {
701
+ zf.dir.mkdir("dir1")
702
+ }
703
+ assert(!zf.file.exists?("newDir"))
704
+ zf.dir.mkdir("newDir")
705
+ assert(zf.file.directory?("newDir"))
706
+ assert(!zf.file.exists?("newDir2"))
707
+ # FIXME - mode not supported yet
708
+ #zf.dir.mkdir("newDir2", 3485)
709
+ #assert(zf.file.directory?("newDir2"))
710
+ zf.dir.rmdir 'newDir'
711
+ assert(!zf.file.exists?("newDir"))
712
+ }
713
+ end
714
+
715
+ def test_pwd_chdir_entries
716
+ Ole::Storage.open(@io) {
717
+ |zf|
718
+ assert_equal("/", zf.dir.pwd)
719
+
720
+ assert_raise(Errno::ENOENT, "No such file or directory - no such dir") {
721
+ zf.dir.chdir "no such dir"
722
+ }
723
+
724
+ # changed this to ENOTDIR, which is what touch a; ruby -e "Dir.chdir('a')" gives you.
725
+ assert_raise(Errno::ENOTDIR, "Invalid argument - file1") {
726
+ zf.dir.chdir "file1"
727
+ }
728
+
729
+ assert_equal(['.', '..', "dir1", "dir2", "file1"].sort, zf.dir.entries(".").sort)
730
+ zf.dir.chdir "dir1"
731
+ assert_equal("/dir1", zf.dir.pwd)
732
+ zf.dir.chdir('dir11') { assert_equal '/dir1/dir11', zf.dir.pwd }
733
+ assert_equal '/dir1', zf.dir.pwd
734
+ assert_equal(['.', '..', "dir11", "file11", "file12"], zf.dir.entries(".").sort)
735
+
736
+ zf.dir.chdir "../dir2/dir21"
737
+ assert_equal("/dir2/dir21", zf.dir.pwd)
738
+ assert_equal(['.', '..', "dir221"].sort, zf.dir.entries(".").sort)
739
+ }
740
+ end
741
+
742
+ # results here are a bit different from zip/zipfilesystem, as i've chosen to fake '.'
743
+ # and '..'
744
+ def test_foreach
745
+ Ole::Storage.open(@io) {
746
+ |zf|
747
+
748
+ blockCalled = false
749
+ assert_raise(Errno::ENOENT, "No such file or directory - noSuchDir") {
750
+ zf.dir.foreach("noSuchDir") { |e| blockCalled = true }
751
+ }
752
+ assert(! blockCalled)
753
+
754
+ assert_raise(Errno::ENOTDIR, "Not a directory - file1") {
755
+ zf.dir.foreach("file1") { |e| blockCalled = true }
756
+ }
757
+ assert(! blockCalled)
758
+
759
+ entries = []
760
+ zf.dir.foreach(".") { |e| entries << e }
761
+ assert_equal(['.', '..', "dir1", "dir2", "file1"].sort, entries.sort)
762
+
763
+ entries = []
764
+ zf.dir.foreach("dir1") { |e| entries << e }
765
+ assert_equal(['.', '..', "dir11", "file11", "file12"], entries.sort)
766
+ }
767
+ end
768
+
769
+ =begin
770
+ # i've gone for NoMethodError instead.
771
+ def test_chroot
772
+ Ole::Storage.open(@io) {
773
+ |zf|
774
+ assert_raise(NotImplementedError) {
775
+ zf.dir.chroot
776
+ }
777
+ }
778
+ end
779
+ =end
780
+
781
+ # Globbing not supported yet
782
+ #def test_glob
783
+ # # test alias []-operator too
784
+ # fail "implement test"
785
+ #end
786
+
787
+ def test_open_new
788
+ Ole::Storage.open(@io) {
789
+ |zf|
790
+
791
+ assert_raise(Errno::ENOTDIR, "Not a directory - file1") {
792
+ zf.dir.new("file1")
793
+ }
794
+
795
+ assert_raise(Errno::ENOENT, "No such file or directory - noSuchFile") {
796
+ zf.dir.new("noSuchFile")
797
+ }
798
+
799
+ d = zf.dir.new(".")
800
+ assert_equal(['.', '..', "file1", "dir1", "dir2"].sort, d.entries.sort)
801
+ d.close
802
+
803
+ zf.dir.open("dir1") {
804
+ |d|
805
+ assert_equal(['.', '..', "dir11", "file11", "file12"].sort, d.entries.sort)
806
+ }
807
+ }
808
+ end
809
+
810
+ end
811
+
812
+ =begin
813
+ class ZipFsDirIteratorTest < Test::Unit::TestCase
814
+
815
+ FILENAME_ARRAY = [ "f1", "f2", "f3", "f4", "f5", "f6" ]
816
+
817
+ def setup
818
+ @dirIt = ZipFileSystem::ZipFsDirIterator.new(FILENAME_ARRAY)
819
+ end
820
+
821
+ def test_close
822
+ @dirIt.close
823
+ assert_raise(IOError, "closed directory") {
824
+ @dirIt.each { |e| p e }
825
+ }
826
+ assert_raise(IOError, "closed directory") {
827
+ @dirIt.read
828
+ }
829
+ assert_raise(IOError, "closed directory") {
830
+ @dirIt.rewind
831
+ }
832
+ assert_raise(IOError, "closed directory") {
833
+ @dirIt.seek(0)
834
+ }
835
+ assert_raise(IOError, "closed directory") {
836
+ @dirIt.tell
837
+ }
838
+
839
+ end
840
+
841
+ def test_each
842
+ # Tested through Enumerable.entries
843
+ assert_equal(FILENAME_ARRAY, @dirIt.entries)
844
+ end
845
+
846
+ def test_read
847
+ FILENAME_ARRAY.size.times {
848
+ |i|
849
+ assert_equal(FILENAME_ARRAY[i], @dirIt.read)
850
+ }
851
+ end
852
+
853
+ def test_rewind
854
+ @dirIt.read
855
+ @dirIt.read
856
+ assert_equal(FILENAME_ARRAY[2], @dirIt.read)
857
+ @dirIt.rewind
858
+ assert_equal(FILENAME_ARRAY[0], @dirIt.read)
859
+ end
860
+
861
+ def test_tell_seek
862
+ @dirIt.read
863
+ @dirIt.read
864
+ pos = @dirIt.tell
865
+ valAtPos = @dirIt.read
866
+ @dirIt.read
867
+ @dirIt.seek(pos)
868
+ assert_equal(valAtPos, @dirIt.read)
869
+ end
870
+
871
+ end
872
+
873
+
874
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
875
+ # rubyzip is free software; you can redistribute it and/or
876
+ # modify it under the terms of the ruby license.
877
+ =end