rfusefs 1.0.2 → 1.0.3

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