rfusefs 1.0.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +0,0 @@
1
- require 'spec_helper'
2
- require 'rfusefs'
3
- require 'tmpdir'
4
- require 'pathname'
5
-
6
- describe "a mounted FuseFS" do
7
- let(:mountpoint) { Pathname.new(Dir.mktmpdir("rfusefs_mount_unmount")) }
8
-
9
- after(:each) { FileUtils.rmdir mountpoint }
10
-
11
- it "should get mounted and unmounted callbacks" do
12
- mock_fs = FuseFS::FuseDir.new()
13
- mock_fs.should_receive(:mounted)
14
- mock_fs.should_receive(:unmounted)
15
-
16
- t = Thread.new { sleep 0.5 ; puts "exiting" ; FuseFS.exit }
17
- FuseFS.start(mock_fs,mountpoint)
18
- t.join
19
- end
20
- end
21
-
@@ -1,417 +0,0 @@
1
- #Pathmapper is hard to test because it is difficult to mock Dir/Pathname/File etc...
2
- require "spec_helper"
3
- require "fusefs/pathmapper"
4
- require 'tmpdir'
5
- require 'pathname'
6
- require 'sys/filesystem'
7
- require 'ffi-xattr/extensions'
8
-
9
- class PMFixture
10
- attr_reader :tmpdir
11
-
12
- def initialize()
13
- #Note - these names define the filesystem stats so if you change them those tests will break
14
- end
15
-
16
- def tmpdir
17
- @tmpdir ||= Pathname.new(Dir.mktmpdir("rfusefs_pathmapper"))
18
- end
19
-
20
- def real_path(file)
21
- tmpdir + file
22
- end
23
-
24
- def pathmap(file,mapped_path, content = mapped_path, options = {})
25
- real_file = tmpdir + file
26
- real_file.open("w") do |f|
27
- f << content
28
- end
29
- fs.map_file(real_file,mapped_path,options)
30
- end
31
-
32
- def fs
33
- @fs ||= FuseFS::PathMapperFS.new()
34
- end
35
-
36
- def mount()
37
- return @mountpoint if @mountpoint
38
- @mountpoint = Pathname.new(Dir.mktmpdir("rfusefs_pathmapper_mnt"))
39
- FuseFS.mount(fs,@mountpoint)
40
- sleep(0.5)
41
- @mountpoint
42
- end
43
-
44
- def cleanup
45
- if @mountpoint
46
- FuseFS.unmount(@mountpoint)
47
- sleep(0.5)
48
- FileUtils.rmdir(@mountpoint)
49
- end
50
- FileUtils.rm_r(@tmpdir)
51
- end
52
- end
53
-
54
-
55
- describe FuseFS::PathMapperFS do
56
-
57
- let(:fixture) { PMFixture.new() }
58
- let(:pathmap_fs) { fixture.fs }
59
- let(:tmpdir) { fixture.tmpdir }
60
- let(:mountpoint) { fixture.mount }
61
-
62
- before(:each) do
63
- fixture.pathmap("hello.txt","/textfiles/hello")
64
- fixture.pathmap("mysong.mp3","/artist/album/mysong.mp3")
65
- fixture.pathmap("apicture.jpeg","/pictures/201103/apicture.jpg")
66
- end
67
-
68
- after(:each) do
69
- fixture.cleanup
70
- end
71
-
72
- context "fusefs api" do
73
-
74
- it "maps files and directories" do
75
- pathmap_fs.directory?("/").should be_true
76
- pathmap_fs.directory?("/textfiles").should be_true
77
- pathmap_fs.directory?("/pictures/201103").should be_true
78
- pathmap_fs.file?("/textfiles/hello").should be_true
79
- pathmap_fs.directory?("/textfiles/hello").should be_false
80
- pathmap_fs.file?("/artist/album/mysong.mp3").should be_true
81
- pathmap_fs.directory?("/artist/album/mysong.mp3").should be_false
82
- pathmap_fs.file?("/some/unknown/path").should be_false
83
- pathmap_fs.directory?("/some/unknown/path").should be_false
84
- end
85
-
86
- it "lists the mapped contents of directories" do
87
- pathmap_fs.contents("/").should =~ [ "textfiles","artist","pictures" ]
88
- pathmap_fs.contents("/artist").should =~ [ "album" ]
89
- pathmap_fs.contents("/textfiles").should =~ [ "hello" ]
90
- end
91
-
92
- it "reports the size of a file" do
93
- pathmap_fs.size("/textfiles/hello").should == 16
94
- end
95
-
96
- it "reads the contents of a file" do
97
- pathmap_fs.read_file("/textfiles/hello").should == "/textfiles/hello"
98
- end
99
-
100
- it "does not allow writes" do
101
- pathmap_fs.can_write?("/textfiles/hello").should be_false
102
- end
103
-
104
- it "reports the atime,mtime and ctime of the mapped file" do
105
- atime,mtime,ctime = pathmap_fs.times("/pictures/201103/apicture.jpg")
106
- picture = tmpdir + "apicture.jpeg"
107
- atime.should == picture.atime()
108
- mtime.should == picture.mtime()
109
- ctime.should == picture.ctime()
110
- end
111
-
112
- context "with extended attributes" do
113
- let (:hello_realpath) {fixture.real_path("hello.txt")}
114
- let (:hello_xattr) { Xattr.new(hello_realpath) }
115
- let (:pm_file_xattr) { pathmap_fs.xattr("/textfiles/hello") }
116
- let (:pm_dir_xattr) { pathmap_fs.xattr("/textfiles") }
117
-
118
- before(:each) do
119
- # attribute set on real file
120
- hello_xattr["user.file_attr"] = "fileValue"
121
-
122
- # additional attribute on file
123
- pathmap_fs.node("/textfiles/hello")[:xattr] =
124
- { "user.add_attr" => "addValue" }
125
-
126
- # additional attribute on directory
127
- pathmap_fs.node("/textfiles")[:xattr] =
128
- { "user.dir_attr" => "dirValue" }
129
-
130
- end
131
-
132
- it "should list extended attributes" do
133
- pm_file_xattr.keys.should include("user.file_attr")
134
- pm_file_xattr.keys.should include("user.add_attr")
135
- pm_file_xattr.keys.size.should == 2
136
- end
137
-
138
- it "should read extended attributes from underlying file" do
139
- pm_file_xattr["user.file_attr"].should == "fileValue"
140
- end
141
-
142
- it "should read additional attributes" do
143
- # make sure additional attributes are not on the real file
144
- hello_xattr.list.should_not include("user.add_attr")
145
-
146
- pm_file_xattr["user.add_attr"].should == "addValue"
147
- end
148
-
149
- it "should write extended attributes to the underlying file" do
150
- pm_file_xattr["user.file_attr"] = "written"
151
- hello_xattr["user.file_attr"].should == "written"
152
- end
153
-
154
- it "should remove extended attributes from the underlying file" do
155
- pm_file_xattr.delete("user.file_attr")
156
- hello_xattr.list.should_not include("user.file_attr")
157
- end
158
-
159
- it "raise EACCES when writing to additional attributes" do
160
- lambda {pm_file_xattr["user.add_attr"] = "newValue"}.should raise_error(Errno::EACCES)
161
- end
162
-
163
- it "raise EACCES when removing additional attributes" do
164
- lambda {pm_file_xattr.delete("user.add_attr")}.should raise_error(Errno::EACCES)
165
- end
166
-
167
- it "should list additional attributes from virtual directories" do
168
- pm_dir_xattr.keys.should include("user.dir_attr")
169
- pm_dir_xattr.keys.size.should == 1
170
- end
171
-
172
- it "should read additional attributes from virtual directories" do
173
- pm_dir_xattr["user.dir_attr"].should == "dirValue"
174
-
175
- end
176
-
177
- it "should raise EACCES when writing additional attributes on virtual directories" do
178
- lambda {pm_dir_xattr["user.dir_attr"] = "newValue"}.should raise_error(Errno::EACCES)
179
- end
180
-
181
- it "should raise EACCES when deleting additional attributes on virtual directories" do
182
- lambda {pm_dir_xattr.delete("user.dir_attr")}.should raise_error(Errno::EACCES)
183
- end
184
-
185
- it "should accept xattr as option to #map_file" do
186
- fixture.pathmap("mapped_xattr.txt","/textfiles/mapped_xattr","content",
187
- :xattr => { "user.xattr" => "map_file" })
188
- pathmap_fs.xattr("/textfiles/mapped_xattr")["user.xattr"].should == "map_file"
189
- end
190
- end
191
-
192
- context "filesystem statistics" do
193
-
194
- it "reports accumulated stats about mapped files" do
195
- used_space, used_nodes, max_space, max_nodes = pathmap_fs.statistics("/pictures/201103/apicture.jpg")
196
- used_space.should == 69
197
- used_nodes.should == 9
198
- max_space.should be_nil
199
- max_nodes.should be_nil
200
- end
201
- end
202
-
203
- context "writing to a pathmapped FS" do
204
- before(:each) do
205
- pathmap_fs.allow_write=true
206
- pathmap_fs.write_to("textfiles/hello","updated content")
207
- end
208
-
209
- it "updates the contents of the real file" do
210
- hello_path = tmpdir + "hello.txt"
211
- hello_path.read.should == "updated content"
212
- end
213
-
214
- it "updates the contents of the mapped file" do
215
- pathmap_fs.read_file("textfiles/hello").should == "updated content"
216
- end
217
-
218
- it "changes the reported file size" do
219
- pathmap_fs.size("textfiles/hello").should == 15
220
- end
221
-
222
- it "changes the filesystem statistics" do
223
- used_space, used_nodes, max_space, max_nodes = pathmap_fs.statistics("/pictures/201103/apicture.jpg")
224
- used_space.should == 68
225
- end
226
- end
227
-
228
- end
229
-
230
- context "a real Fuse mounted filesystem" do
231
- before(:each) do
232
- pathmap_fs.allow_write=true
233
- end
234
-
235
- it "maps files and directories" do
236
- (mountpoint + "textfiles").directory?.should be_true
237
- (mountpoint + "textfiles/hello").file?.should be_true
238
- end
239
-
240
- it "lists the mapped contents of directories" do
241
- (mountpoint + "textfiles").entries.should =~ pathnames(".","..","hello")
242
- end
243
-
244
- it "represents the stat information of the underlying files" do
245
- hellopath=(mountpoint + "textfiles/hello")
246
- realpath=(tmpdir + "hello.txt")
247
- mappedstat = hellopath.stat
248
- realstat = realpath.stat
249
- mappedstat.size.should == realstat.size
250
- mappedstat.atime.should == realstat.atime
251
- mappedstat.mtime.should == realstat.mtime
252
- mappedstat.ctime.should == realstat.ctime
253
- end
254
-
255
- it "reads the files" do
256
- hellopath= mountpoint + "textfiles/hello"
257
- hellopath.read.should == "/textfiles/hello"
258
- end
259
-
260
- it "writes the files" do
261
- hellopath= mountpoint + "textfiles/hello"
262
- real_path = tmpdir + "hello.txt"
263
- hellopath.open("w") do |f|
264
- f.print "updated content"
265
- end
266
- hellopath.read.should == "updated content"
267
- real_path.read.should == "updated content"
268
- end
269
-
270
- context "extended attributes" do
271
- let (:hello_realpath) {fixture.real_path("hello.txt")}
272
- let (:hello_xattr) { hello_realpath.xattr }
273
- let (:file_xattr) { (mountpoint + "textfiles/hello").xattr }
274
- let (:dir_xattr) { (mountpoint + "textfiles").xattr }
275
-
276
- before(:each) do
277
- # attribute set on real file
278
- hello_xattr["user.file_attr"] = "fileValue"
279
-
280
- # additional attribute on file
281
- pathmap_fs.node("/textfiles/hello")[:xattr] =
282
- { "user.add_attr" => "addValue" }
283
-
284
- # additional attribute on directory
285
- pathmap_fs.node("/textfiles")[:xattr] =
286
- { "user.dir_attr" => "dirValue" }
287
-
288
- end
289
-
290
- it "should list extended attributes" do
291
- file_xattr.list.should include("user.file_attr")
292
- file_xattr.list.should include("user.add_attr")
293
- file_xattr.list.size.should == 2
294
- end
295
-
296
- it "should read extended attributes from underlying file" do
297
- file_xattr["user.file_attr"].should == "fileValue"
298
- end
299
-
300
- it "should read additional attributes" do
301
- file_xattr["user.add_attr"].should == "addValue"
302
- end
303
-
304
- it "should write extended attributes to the underlying file" do
305
- file_xattr["user.file_attr"] = "written"
306
- hello_xattr["user.file_attr"].should == "written"
307
- end
308
-
309
- it "should remove extended attributes from the underlying file" do
310
- file_xattr.remove("user.file_attr")
311
- hello_xattr.list.should_not include("user.file_attr")
312
- end
313
-
314
- it "raise EACCES when writing to additional attributes" do
315
- lambda {file_xattr["user.add_attr"] = "newValue"}.should raise_error(Errno::EACCES)
316
- end
317
-
318
- it "raise EACCES when removing additional attributes" do
319
- lambda {file_xattr.remove("user.add_attr")}.should raise_error(Errno::EACCES)
320
- end
321
-
322
- it "should list additional attributes from virtual directories" do
323
- dir_xattr.list.should include("user.dir_attr")
324
- dir_xattr.list.size.should == 1
325
- end
326
-
327
- it "should read additional attributes from virtual directories" do
328
- dir_xattr["user.dir_attr"].should == "dirValue"
329
- end
330
-
331
- it "should raise EACCES when writing additional attributes on virtual directories" do
332
- lambda {dir_xattr["user.dir_attr"] = "newValue"}.should raise_error(Errno::EACCES)
333
- end
334
-
335
- it "should raise EACCES when deleting additional attributes on virtual directories" do
336
- lambda {dir_xattr.remove("user.dir_attr")}.should raise_error(Errno::EACCES)
337
- end
338
-
339
- end
340
-
341
- context "filesystem statistics" do
342
- before(:each) do
343
- fixture.pathmap("bigfile.txt","/textfiles/bigfile","x" * 2048)
344
- end
345
-
346
- it "reports stats for files" do
347
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
348
- # These are fixed
349
- statfs.block_size.should == 1024
350
- statfs.fragment_size.should == 1024
351
-
352
- # These are dependant on the tests above creating files/directories
353
- statfs.files.should == 10
354
- statfs.files_available == 10
355
-
356
- # assume test are less than 1 block, so dependant on bigfile above
357
- statfs.blocks.should == 2
358
- statfs.blocks_available.should == 0
359
- statfs.blocks_free.should == 0
360
- end
361
-
362
- it "reports stats for files after writing" do
363
-
364
- (mountpoint + "textfiles/bigfile").open("w") { |f| f.print("y" * 4096) }
365
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
366
- statfs.files.should == 10
367
- statfs.blocks.should == 4
368
-
369
- end
370
-
371
- end
372
- end
373
-
374
- context "a real Fuse mount with raw file access" do
375
-
376
- before(:each) do
377
- pathmap_fs.use_raw_file_access = true
378
- pathmap_fs.allow_write = true
379
- end
380
-
381
- it "reads files" do
382
- hello_path = (mountpoint + "textfiles/hello")
383
- hello_path.open do |f|
384
- f.seek(2)
385
- f.read(3).should == "ext"
386
- end
387
-
388
- hello_path.sysopen do |f|
389
- f.sysseek(1)
390
- f.sysread(3).should == "tex"
391
- end
392
- end
393
-
394
- it "writes files" do
395
- hello_path = (mountpoint + "textfiles/hello")
396
- real_path = tmpdir + "hello.txt"
397
- hello_path.open("r+") do |f|
398
- f.sysseek(2)
399
- f.syswrite("zzz")
400
- f.sysseek(0)
401
- f.sysread(6).should == "/tzzzf"
402
- end
403
-
404
- real_path.read.should == "/tzzzfiles/hello"
405
- end
406
-
407
- it "reports filesystem statistics after raw write" do
408
- hello_path = (mountpoint + "textfiles/hello")
409
- hello_path.open("w") do |f|
410
- f.syswrite("z" * 2048)
411
- end
412
-
413
- statfs = Sys::Filesystem.stat(mountpoint.to_path)
414
- statfs.blocks.should == 2
415
- end
416
- end
417
- end
@@ -1,497 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe FuseFS do
4
-
5
- TEST_FILE = "/aPath/aFile"
6
- TEST_DIR = "/aPath"
7
- ROOT_PATH = "/"
8
- Struct.new("FuseFileInfo",:flags,:fh)
9
-
10
- describe "an empty FuseFS object" do
11
- before(:each) do
12
- @fuse = FuseFS::RFuseFS.new(Object.new())
13
- end
14
-
15
- it "should return an appropriate Stat for the root directory" do
16
- stat = @fuse.getattr(nil,ROOT_PATH)
17
- stat.should respond_to(:dev)
18
- (stat.mode & RFuse::Stat::S_IFDIR).should_not == 0
19
- (stat.mode & RFuse::Stat::S_IFREG).should == 0
20
- permissions(stat.mode).should == 0555
21
- end
22
-
23
- it "should have an empty root directory" do
24
- filler = mock("entries_filler")
25
- filler.should_receive(:push).with(".",nil,0)
26
- filler.should_receive(:push).with("..",nil,0)
27
- @fuse.readdir(nil,"/",filler,nil,nil)
28
- end
29
-
30
- it "should raise ENOENT for other paths" do
31
- lambda { @fuse.getattr(nil,"/somepath") }.should raise_error(Errno::ENOENT)
32
- end
33
-
34
- it "should not allow new files or directories" do
35
- lambda { @fuse.mknod(nil,"/afile",0100644,0,0) }.should raise_error(Errno::EACCES)
36
- lambda { @fuse.mkdir(nil,"/adir",0040555) }.should raise_error(Errno::EACCES)
37
- end
38
- end
39
-
40
- describe "a FuseFS filesystem" do
41
- before(:each) do
42
- @mock_fuse = FuseFS::FuseDir.new()
43
- @fuse = FuseFS::RFuseFS.new(@mock_fuse)
44
- end
45
-
46
- describe :readdir do
47
- before(:each) do
48
- @mock_fuse.should_receive(:contents).with("/apath").and_return(["afile"])
49
- end
50
-
51
- it "should add . and .. to the results of :contents when listing a directory" do
52
- filler = mock("entries_filler")
53
- filler.should_receive(:push).with(".",nil,0)
54
- filler.should_receive(:push).with("..",nil,0)
55
- filler.should_receive(:push).with("afile",nil,0)
56
- @fuse.readdir(nil,"/apath",filler,nil,nil)
57
- end
58
-
59
- end
60
-
61
- describe :getattr do
62
-
63
- #Root directory is special (ish) so we need to run these specs twice.
64
- [ROOT_PATH,TEST_DIR].each do |dir|
65
-
66
- context "of a directory #{ dir }" do
67
-
68
- before(:each) do
69
- @mock_fuse.stub!(:file?).and_return(false)
70
- @mock_fuse.should_receive(:directory?).with(dir).at_most(:once).and_return(true)
71
- @checkfile = (dir == "/" ? "" : dir ) + FuseFS::RFuseFS::CHECK_FILE
72
- end
73
-
74
- it "should return a Stat like object representing a directory" do
75
- @mock_fuse.should_receive(:can_write?).with(@checkfile).at_most(:once).and_return(false)
76
- @mock_fuse.should_receive(:can_mkdir?).with(@checkfile).at_most(:once).and_return(false)
77
- stat = @fuse.getattr(nil,dir)
78
- #Apparently find relies on nlink accurately listing the number of files/directories or nlink being 1
79
- stat.nlink.should == 1
80
- filetype(stat.mode).should == RFuse::Stat::S_IFDIR
81
- permissions(stat.mode).should == 0555
82
- end
83
-
84
-
85
- it "should return writable mode if can_mkdir?" do
86
- @mock_fuse.should_receive(:can_mkdir?).with(@checkfile).at_most(:once).and_return(true)
87
-
88
- stat = @fuse.getattr(nil,dir)
89
- permissions(stat.mode).should == 0777
90
- end
91
-
92
- it "should return writable mode if can_write?" do
93
- @mock_fuse.should_receive(:can_write?).with(@checkfile).at_most(:once).and_return(true)
94
-
95
- stat = @fuse.getattr(nil,dir)
96
- permissions(stat.mode).should == 0777
97
-
98
- end
99
-
100
- it "should return times in the result if available" do
101
- @mock_fuse.should_receive(:times).with(dir).and_return([10,20,30])
102
- stat = @fuse.getattr(nil,dir)
103
- stat.atime.should == 10
104
- stat.mtime.should == 20
105
- stat.ctime.should == 30
106
- end
107
- end
108
- end
109
-
110
- describe "a file" do
111
-
112
- before(:each) do
113
- @file="/aPath/aFile"
114
- @mock_fuse.stub!(:directory?).and_return(false)
115
- @mock_fuse.should_receive(:file?).with(@file).at_most(:once).and_return(true)
116
- end
117
-
118
-
119
- it "should return a Stat like object representing a file" do
120
- stat = @fuse.getattr(nil,@file)
121
- (stat.mode & RFuse::Stat::S_IFDIR).should == 0
122
- (stat.mode & RFuse::Stat::S_IFREG).should_not == 0
123
- permissions(stat.mode).should == 0444
124
- end
125
-
126
- it "should indicate executable mode if executable?" do
127
- @mock_fuse.should_receive(:executable?).with(@file).and_return(true)
128
- stat = @fuse.getattr(nil,@file)
129
- permissions(stat.mode).should == 0555
130
- end
131
-
132
- it "should indicate writable mode if can_write?" do
133
- @mock_fuse.should_receive(:can_write?).with(@file).and_return(true)
134
- stat = @fuse.getattr(nil,@file)
135
- permissions(stat.mode).should == 0666
136
- end
137
-
138
- it "should by 777 mode if can_write? and exectuable?" do
139
- @mock_fuse.should_receive(:can_write?).with(@file).and_return(true)
140
- @mock_fuse.should_receive(:executable?).with(@file).and_return(true)
141
- stat = @fuse.getattr(nil,@file)
142
- permissions(stat.mode).should == 0777
143
- end
144
-
145
- it "should include size in the result if available" do
146
- @mock_fuse.should_receive(:size).with(@file).and_return(234)
147
- stat = @fuse.getattr(nil,@file)
148
- stat.size.should == 234
149
- end
150
-
151
- it "should include times in the result if available" do
152
- @mock_fuse.should_receive(:times).with(@file).and_return([22,33,44])
153
- stat = @fuse.getattr(nil,@file)
154
- stat.atime.should == 22
155
- stat.mtime.should == 33
156
- stat.ctime.should == 44
157
- end
158
- end
159
-
160
- it "should raise ENOENT for a path that does not exist" do
161
- @mock_fuse.should_receive(:file?).with(TEST_FILE).and_return(false)
162
- @mock_fuse.should_receive(:directory?).with(TEST_FILE).and_return(false)
163
- lambda{stat = @fuse.getattr(nil,TEST_FILE) }.should raise_error(Errno::ENOENT)
164
- end
165
- end
166
-
167
- context "creating files and directories" do
168
-
169
- it ":mknod should raise EACCES unless :can_write?" do
170
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
171
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
172
- @mock_fuse.should_receive(:can_write?).with(TEST_FILE).and_return(false)
173
- lambda{@fuse.mknod(nil,TEST_FILE,0100644,0,0)}.should raise_error(Errno::EACCES)
174
- end
175
-
176
- it ":mkdir should raise EACCES unless :can_mkdir?" do
177
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
178
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
179
- @mock_fuse.should_receive(:can_mkdir?).with(TEST_FILE).and_return(false)
180
- lambda{@fuse.mkdir(nil,TEST_FILE,004555)}.should raise_error(Errno::EACCES)
181
- end
182
-
183
- it ":mknod should raise EACCES unless mode requests a regular file" do
184
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
185
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
186
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
187
- lambda{@fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFLNK | 0644,0,0)}.should raise_error(Errno::EACCES)
188
- end
189
-
190
- it ":mknod should result in getattr returning a Stat like object representing an empty file" do
191
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
192
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
193
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
194
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
195
-
196
- stat = @fuse.getattr(nil,TEST_FILE)
197
- filetype(stat.mode).should == RFuse::Stat::S_IFREG
198
- stat.size.should == 0
199
- end
200
-
201
- it "should create zero length files" do
202
- ffi = Struct::FuseFileInfo.new()
203
- ffi.flags = Fcntl::O_WRONLY
204
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
205
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
206
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
207
- @mock_fuse.should_receive(:write_to).once.with(TEST_FILE,"")
208
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
209
- @fuse.open(nil,TEST_FILE,ffi)
210
- @fuse.flush(nil,TEST_FILE,ffi)
211
- @fuse.release(nil,TEST_FILE,ffi)
212
- end
213
-
214
- it ":mkdir should not raise error if can_mkdir?" do
215
- @mock_fuse.should_receive(:can_mkdir?).with(TEST_FILE).and_return(true)
216
- @fuse.mkdir(nil,TEST_FILE,004555)
217
- end
218
-
219
- end
220
-
221
- context "reading files" do
222
- it "should read the contents of a file" do
223
- ffi = Struct::FuseFileInfo.new()
224
- ffi.flags = Fcntl::O_RDONLY
225
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(true)
226
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("Hello World\n")
227
- @fuse.open(nil,TEST_FILE,ffi)
228
- #to me fuse is backwards -- size, offset!
229
- @fuse.read(nil,TEST_FILE,5,0,ffi).should == "Hello"
230
- @fuse.read(nil,TEST_FILE,4,6,ffi).should == "Worl"
231
- @fuse.read(nil,TEST_FILE,10,8,ffi).should == "rld\n"
232
- @fuse.flush(nil,TEST_FILE,ffi)
233
- @fuse.release(nil,TEST_FILE,ffi)
234
- end
235
- end
236
-
237
- context "writing files" do
238
- it "should overwrite a file opened WR_ONLY" do
239
- ffi = Struct::FuseFileInfo.new()
240
- ffi.flags = Fcntl::O_WRONLY
241
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
242
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("I'm writing a file\n")
243
- @mock_fuse.should_receive(:write_to).once().with(TEST_FILE,"My new contents\n")
244
- @fuse.open(nil,TEST_FILE,ffi)
245
- @fuse.ftruncate(nil,TEST_FILE,0,ffi)
246
- @fuse.write(nil,TEST_FILE,"My new c",0,ffi)
247
- @fuse.write(nil,TEST_FILE,"ontents\n",8,ffi)
248
- @fuse.flush(nil,TEST_FILE,ffi)
249
- #that's right flush can be called more than once.
250
- @fuse.flush(nil,TEST_FILE,ffi)
251
- #but then we can write some more and flush again
252
- @fuse.release(nil,TEST_FILE,ffi)
253
- end
254
-
255
- it "should append to a file opened WR_ONLY | APPEND" do
256
- ffi = Struct::FuseFileInfo.new()
257
- ffi.flags = Fcntl::O_WRONLY | Fcntl::O_APPEND
258
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
259
- @mock_fuse.stub!(:read_file).with(TEST_FILE).and_return("I'm writing a file\n")
260
- @mock_fuse.should_receive(:write_to).once().with(TEST_FILE,"I'm writing a file\nMy new contents\n")
261
- @fuse.open(nil,TEST_FILE,ffi)
262
- @fuse.write(nil,TEST_FILE,"My new c",0,ffi)
263
- @fuse.write(nil,TEST_FILE,"ontents\n",8,ffi)
264
- @fuse.flush(nil,TEST_FILE,ffi)
265
- #that's right flush can be called more than once. But we should only write-to the first time
266
- @fuse.flush(nil,TEST_FILE,ffi)
267
- @fuse.release(nil,TEST_FILE,ffi)
268
-
269
- end
270
-
271
- it "should do sensible things for files opened RDWR"
272
-
273
- it "should pass on buffered data when requested (fsync)"
274
-
275
- end
276
-
277
- context "raw reading" do
278
-
279
- it "should call the raw_read/raw_close if raw_open returns true" do
280
- ffi = Struct::FuseFileInfo.new()
281
- ffi.flags = Fcntl::O_RDONLY
282
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
283
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"r",true).and_return("raw")
284
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,0,"raw").and_return("12345")
285
- @mock_fuse.should_receive(:raw_read).with(TEST_FILE,5,5,"raw").and_return("67890")
286
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,"raw")
287
- @fuse.open(nil,TEST_FILE,ffi)
288
- @fuse.read(nil,TEST_FILE,0,5,ffi).should == "12345"
289
- @fuse.read(nil,TEST_FILE,5,5,ffi).should == "67890"
290
- @fuse.flush(nil,TEST_FILE,ffi)
291
- @fuse.release(nil,TEST_FILE,ffi)
292
- end
293
-
294
- end
295
-
296
- context "raw writing" do
297
- it "should call other raw_* methods if raw_open returns true" do
298
- ffi = Struct::FuseFileInfo.new()
299
- ffi.flags = Fcntl::O_WRONLY
300
- raw = Object.new()
301
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
302
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"w",true).and_return(raw)
303
- @mock_fuse.should_receive(:raw_truncate).with(TEST_FILE,0,raw)
304
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,0,5,"12345",raw).once().and_return(5)
305
- @mock_fuse.should_receive(:raw_write).with(TEST_FILE,5,5,"67890",raw).once().and_return(5)
306
- @mock_fuse.should_receive(:raw_sync).with(TEST_FILE, false, raw)
307
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,raw)
308
- @fuse.open(nil,TEST_FILE,ffi)
309
- @fuse.ftruncate(nil,TEST_FILE,0,ffi)
310
- @fuse.write(nil,TEST_FILE,"12345",0,ffi).should == 5
311
- @fuse.fsync(nil,TEST_FILE,0,ffi)
312
- @fuse.write(nil,TEST_FILE,"67890",5,ffi).should == 5
313
- @fuse.flush(nil,TEST_FILE,ffi)
314
- @fuse.release(nil,TEST_FILE,ffi)
315
- end
316
-
317
- it "should clean up created files" do
318
- ffi = Struct::FuseFileInfo.new()
319
- ffi.flags = Fcntl::O_WRONLY
320
- raw = Object.new()
321
- @mock_fuse.stub!(:directory?).and_return(false)
322
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false,true)
323
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
324
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"w",true).and_return(raw)
325
- @mock_fuse.should_receive(:raw_close).with(TEST_FILE,raw)
326
- @mock_fuse.should_receive(:size).with(TEST_FILE).and_return(25)
327
-
328
- lambda { @fuse.getattr(nil,TEST_FILE) }.should raise_error(Errno::ENOENT)
329
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
330
- @fuse.open(nil,TEST_FILE,ffi)
331
- @fuse.flush(nil,TEST_FILE,ffi)
332
- @fuse.release(nil,TEST_FILE,ffi)
333
- stat = @fuse.getattr(nil,TEST_FILE)
334
- stat.size = 25
335
- end
336
-
337
- it "should pass 'wa' to raw_open if fuse sends WRONLY | APPEND" do
338
- ffi = Struct::FuseFileInfo.new()
339
- ffi.flags = Fcntl::O_WRONLY | Fcntl::O_APPEND
340
- raw = Object.new()
341
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
342
- @mock_fuse.should_receive(:raw_open).with(TEST_FILE,"wa",true).and_return(raw)
343
- @fuse.open(nil,TEST_FILE,ffi)
344
- end
345
- end
346
-
347
- context "deleting files" do
348
- it "should raise EACCES unless :can_delete?" do
349
- @mock_fuse.should_receive(:can_delete?).with(TEST_FILE).and_return(false)
350
- lambda {@fuse.unlink(nil,TEST_FILE)}.should raise_error(Errno::EACCES)
351
- end
352
-
353
- it "should :delete without error if :can_delete?" do
354
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
355
- @mock_fuse.should_receive(:delete).with(TEST_FILE)
356
- @fuse.unlink(nil,TEST_FILE)
357
- end
358
-
359
- it "should remove entries created with mknod that have never been opened" do
360
- @mock_fuse.stub!(:file?).with(TEST_FILE).and_return(false)
361
- @mock_fuse.stub!(:directory?).with(TEST_FILE).and_return(false)
362
- @mock_fuse.stub!(:can_delete?).with(TEST_FILE).and_return(true)
363
- @mock_fuse.stub!(:can_write?).with(TEST_FILE).and_return(true)
364
- @fuse.mknod(nil,TEST_FILE,RFuse::Stat::S_IFREG | 0644,0,0)
365
-
366
- @fuse.unlink(nil,TEST_FILE)
367
- lambda {@fuse.getattr(nil,TEST_FILE)}.should raise_error(Errno::ENOENT)
368
- end
369
- end
370
-
371
- context "deleting directories" do
372
- it "should raise EACCES unless :can_rmdir?" do
373
- @mock_fuse.should_receive(:can_rmdir?).with(TEST_DIR).and_return(false)
374
- lambda{@fuse.rmdir(nil,TEST_DIR)}.should raise_error(Errno::EACCES)
375
- end
376
-
377
- it "should :rmdir without error if :can_rmdir?" do
378
- @mock_fuse.stub!(:can_rmdir?).with(TEST_DIR).and_return(true)
379
- @fuse.rmdir(nil,TEST_DIR)
380
- end
381
- end
382
-
383
- context "touching files" do
384
- it "should call :touch in response to utime" do
385
- @mock_fuse.should_receive(:touch).with(TEST_FILE,220)
386
- @fuse.utime(nil,TEST_FILE,100,220)
387
- end
388
- end
389
-
390
- context "renaming files" do
391
- before(:each) do
392
- @oldfile = "/aPath/oldFile"
393
- @newfile = "/aNewFile"
394
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(true)
395
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(false)
396
- end
397
- it "should raise EACCES unless :can_write? the new file" do
398
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
399
- @mock_fuse.should_receive(:can_write?).with(@newfile).and_return(false)
400
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
401
- end
402
-
403
- it "should raise EACCES unless :can_delete the old file" do
404
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
405
- @mock_fuse.should_receive(:can_delete?).with(@oldfile).and_return(false)
406
- lambda {@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
407
- end
408
-
409
- it "should copy and delete files" do
410
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
411
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
412
- @mock_fuse.should_receive(:read_file).with(@oldfile).and_return("some contents\n")
413
- @mock_fuse.should_receive(:write_to).with(@newfile,"some contents\n")
414
- @mock_fuse.should_receive(:delete).with(@oldfile)
415
- @fuse.rename(nil,@oldfile,@newfile)
416
- end
417
-
418
- it "should not copy and delete files if fs responds_to? :rename" do
419
- @mock_fuse.should_receive(:rename).with(@oldfile,@newfile).and_return(true)
420
- @fuse.rename(nil,@oldfile,@newfile)
421
- end
422
-
423
- it "should raise EACCES if moving a directory and rename not supported" do
424
- @mock_fuse.stub!(:file?).with(@oldfile).and_return(false)
425
- @mock_fuse.stub!(:directory?).with(@oldfile).and_return(true)
426
- @mock_fuse.stub!(:can_write?).with(@newfile).and_return(true)
427
- @mock_fuse.stub!(:can_delete?).with(@oldfile).and_return(true)
428
- lambda{@fuse.rename(nil,@oldfile,@newfile)}.should raise_error(Errno::EACCES)
429
- end
430
-
431
- end
432
- context "extended attributes" do
433
-
434
- let(:xattr) { mock(:xattr) }
435
- before(:each) { @mock_fuse.stub!(:xattr).with(TEST_FILE).and_return(xattr) }
436
-
437
- it "should list attributes via #keys on result of #xattr" do
438
- xattr.should_receive(:keys).and_return(["one","two"])
439
- @fuse.listxattr(nil,TEST_FILE).should == [ "one","two" ]
440
- end
441
-
442
- it "should get attributes via #xattr.[]" do
443
- xattr.should_receive(:[]).with("user.one").and_return("one")
444
-
445
- @fuse.getxattr(nil,TEST_FILE,"user.one").should == "one"
446
- end
447
-
448
- it "should set attributes via #xattr.[]=" do
449
- xattr.should_receive(:[]=).with("user.two","two")
450
-
451
- @fuse.setxattr(nil,TEST_FILE,"user.two","two",0)
452
- end
453
-
454
- it "should remove attributes via #xattr.delete" do
455
- xattr.should_receive(:delete).with("user.three")
456
-
457
- @fuse.removexattr(nil,TEST_FILE,"user.three")
458
- end
459
-
460
- it "should raise ENODATA when #xattr.[] returns nil" do
461
-
462
- xattr.should_receive(:[]).with("user.xxxx").and_return(nil)
463
- lambda{@fuse.getxattr(nil,TEST_FILE,"user.xxxx") }.should raise_error(Errno::ENODATA)
464
- end
465
- end
466
-
467
- context "#statfs" do
468
- # used space, used files, total_space, total_files
469
- let(:stats) { [ 1000 * 1024, 5, 1200 * 1024, 12 ] }
470
- it "should convert simple array into StatVfs" do
471
-
472
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(stats)
473
-
474
- result = @fuse.statfs(nil,TEST_FILE)
475
- result.should be_kind_of(RFuse::StatVfs)
476
- result.f_bsize.should == 1024
477
- result.f_blocks.should == 1200
478
- result.f_bavail.should == 200
479
- result.f_files.should == 12
480
- result.f_ffree.should == 7
481
- end
482
-
483
- it "passes on raw statistics" do
484
- statvfs = Object.new()
485
- @mock_fuse.should_receive(:statistics).with(TEST_FILE).and_return(statvfs)
486
-
487
- @fuse.statfs(nil,TEST_FILE).should equal(statvfs)
488
- end
489
-
490
- end
491
- end
492
-
493
- describe "a FuseFS filesystem with gid/uid specific behaviour" do
494
- it "should provide context uid and gid for all API methods"
495
- end
496
-
497
- end