rfuse 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+
4
+ describe RFuse::Fuse do
5
+
6
+ let(:dir_stat) { RFuse::Stat.directory(0444) }
7
+ let(:file_stat) { RFuse::Stat.file(0444) }
8
+ let!(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil); m }
9
+ let(:mountpoint) { tempmount() }
10
+
11
+ context "mount options" do
12
+ it "should handle -h" do
13
+ fuse = RFuse::FuseDelegator.new(mockfs,mountpoint,"-h")
14
+ fuse.mounted?.should be_false
15
+ lambda { fuse.loop }.should raise_error(RFuse::Error)
16
+ end
17
+
18
+ it "should behave sensibly for bad mountpoint" do
19
+ fuse = RFuse::FuseDelegator.new(mockfs,"bad/mount/point")
20
+ fuse.mounted?.should be_false
21
+ lambda { fuse.loop }.should raise_error(RFuse::Error)
22
+ end
23
+
24
+ it "should behave sensibly for bad options" do
25
+ fuse = RFuse::FuseDelegator.new(mockfs,mountpoint,"-eviloption")
26
+ fuse.mounted?.should be_false
27
+ lambda { fuse.loop }.should raise_error(RFuse::Error)
28
+ end
29
+
30
+ it "should handle a Pathname as a mountpoint" do
31
+ fuse = RFuse::FuseDelegator.new(mockfs,Pathname.new(mountpoint))
32
+ fuse.mounted?.should be_true
33
+ fuse.unmount()
34
+ end
35
+ end
36
+
37
+ context "links" do
38
+ it "should create and resolve symbolic links"
39
+
40
+ it "should create and resolve hard links"
41
+
42
+ end
43
+
44
+ context "directories" do
45
+ it "should make directories" do
46
+
47
+ mockfs.stub(:getattr).and_return(nil)
48
+ mockfs.stub(:getattr).with(anything(),"/aDirectory").and_return(nil,dir_stat)
49
+ mockfs.should_receive(:mkdir).with(anything(),"/aDirectory",anything())
50
+
51
+ with_fuse(mountpoint,mockfs) do
52
+ Dir.mkdir("#{mountpoint}/aDirectory")
53
+ end
54
+ end
55
+
56
+ it "should list directories" do
57
+
58
+ mockfs.should_receive(:readdir) do | ctx, path, filler,offset,ffi |
59
+ filler.push("hello",nil,0)
60
+ filler.push("world",nil,0)
61
+ end
62
+
63
+ with_fuse(mountpoint,mockfs) do
64
+ entries = Dir.entries(mountpoint)
65
+ entries.size.should == 2
66
+ entries.should include("hello")
67
+ entries.should include("world")
68
+ end
69
+ end
70
+ end
71
+
72
+ context "permissions" do
73
+ it "should process chmod" do
74
+ mockfs.stub(:getattr).with(anything(),"/myPerms").and_return(file_stat)
75
+
76
+ mockfs.should_receive(:chmod).with(anything(),"/myPerms",file_mode(0644))
77
+
78
+ with_fuse(mountpoint,mockfs) do
79
+ File.chmod(0644,"#{mountpoint}/myPerms").should == 1
80
+ end
81
+ end
82
+ end
83
+
84
+ context "timestamps" do
85
+
86
+ it "should support stat with subsecond resolution" do
87
+ begin
88
+ atime = Time.now() + 60
89
+ sleep(0.001)
90
+ end until atime.usec != 0
91
+
92
+ begin
93
+ mtime = Time.now() + 600
94
+ sleep(0.001)
95
+ end until mtime.usec != 0
96
+
97
+ begin
98
+ ctime = Time.now() + 3600
99
+ sleep(0.001)
100
+ end until ctime.usec != 0
101
+
102
+ file_stat.atime = atime
103
+ file_stat.mtime = mtime
104
+ file_stat.ctime = ctime
105
+
106
+
107
+ # ruby can't set file times with ns res, o we are limited to usecs
108
+ mockfs.stub(:getattr).with(anything(),"/nanos").and_return(file_stat)
109
+
110
+ with_fuse(mountpoint,mockfs) do
111
+ stat = File.stat("#{mountpoint}/nanos")
112
+ stat.atime.should == atime
113
+ stat.ctime.should == ctime
114
+ stat.mtime.should == mtime
115
+ end
116
+ end
117
+
118
+ it "should set file access and modification times" do
119
+
120
+ atime = Time.now()
121
+ mtime = atime + 1
122
+
123
+ mockfs.stub(:getattr).with(anything(),"/times").and_return(file_stat)
124
+ mockfs.should_receive(:utime).with(anything(),"/times",atime.to_i,mtime.to_i)
125
+
126
+ with_fuse(mountpoint,mockfs) do
127
+ File.utime(atime,mtime,"#{mountpoint}/times").should == 1
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ context "file io" do
134
+
135
+ it "should create files" do
136
+
137
+ mockfs.stub(:getattr).with(anything(),"/newfile").and_return(nil,file_stat)
138
+ mockfs.should_receive(:mknod).with(anything(),"/newfile",file_mode(0644),0,0)
139
+
140
+ with_fuse(mountpoint,mockfs) do
141
+ File.open("#{mountpoint}/newfile","w",0644) { |f| }
142
+ end
143
+ end
144
+
145
+ # ruby doesn't seem to have a native method to create these
146
+ # maybe try ruby-mkfifo
147
+ it "should create special device files"
148
+
149
+ it "should read files" do
150
+
151
+ file_stat.size = 11
152
+ mockfs.stub(:getattr) { | ctx, path|
153
+ case path
154
+ when "/test"
155
+ file_stat
156
+ else
157
+ raise Errno::ENOENT
158
+ end
159
+
160
+ }
161
+
162
+ reads = 0
163
+ mockfs.stub(:read) { |ctx,path,size,offset,ffi|
164
+ reads += 2
165
+ "hello world"[offset,reads]
166
+ }
167
+
168
+ with_fuse(mountpoint,mockfs) do
169
+ File.open("#{mountpoint}/test") do |f|
170
+ val = f.gets
171
+ val.should == "hello world"
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe RFuse::Fuse do
4
+ context "file handles" do
5
+ it "should retain file handles over GC" do
6
+ mockfs = mock("fuse")
7
+
8
+ file_stat = RFuse::Stat.file(0444,:size => 11)
9
+
10
+ mockfs.stub(:getattr) { | ctx, path|
11
+ case path
12
+ when "/one","/two"
13
+ file_stat
14
+ else
15
+ nil
16
+ end
17
+
18
+ }
19
+
20
+ open_files = {}
21
+ mockfs.stub(:open) { |ctx,path,ffi|
22
+ ffi.fh = Object.new()
23
+ open_files[path] = ffi.fh.object_id
24
+ }
25
+
26
+ mockfs.stub(:read) { |ctx,path,size,offset,ffi|
27
+ GC.start()
28
+ open_files[path].should == ffi.fh.object_id
29
+ "hello world"
30
+ }
31
+
32
+ mountpoint = tempmount()
33
+ with_fuse(mountpoint,mockfs) do
34
+ f1 = File.new("#{mountpoint}/one")
35
+ f2 = File.new("#{mountpoint}/two")
36
+
37
+ val = f1.gets
38
+ val.should == "hello world"
39
+ f2.gets.should == "hello world"
40
+
41
+ f1.close()
42
+ f2.close()
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe RFuse do
4
+
5
+ let(:mockfs) { m = mock("fuse"); m.stub(:getattr).and_return(nil) ; m }
6
+ let(:mountpoint) { tempmount() }
7
+
8
+ context "ruby loop" do
9
+ it "should exit from another thread and allow multiple loops" do
10
+
11
+ fuse = RFuse::FuseDelegator.new(mockfs,mountpoint)
12
+ t = Thread.new { sleep 0.5; fuse.exit }
13
+ fuse.loop()
14
+ t.join
15
+ t = Thread.new { sleep 0.5; fuse.exit }
16
+ fuse.loop
17
+ t.join
18
+ fuse.unmount
19
+ fuse.mounted?.should be_false
20
+ end
21
+
22
+
23
+ # This will never work!
24
+ #it "should allow threads to operate on the filesystem" do
25
+ #
26
+ # mockfs = mock("fuse")
27
+ # mockfs.stub(:getattr).and_return(nil)
28
+
29
+ # fuse = RFuse::FuseDelegator.new(mockfs,"/tmp/rfuse-spec")
30
+
31
+ # t = Thread.new { sleep 0.5 ; File.stat("/tmp/rfuse-spec/thread") ; fuse.exit }
32
+
33
+ # fuse.loop()
34
+ # fuse.unmount()
35
+ #end
36
+
37
+ it "should allow other threads to be scheduled" do
38
+
39
+ file_stat = RFuse::Stat.file(0444)
40
+
41
+ thread_ran = false
42
+
43
+ mockfs.stub(:getattr).with(anything(),"/before") {
44
+ puts "before"
45
+ thread_ran.should be_false
46
+ file_stat
47
+ }
48
+
49
+ mockfs.stub(:getattr).with(anything(),"/missing") {
50
+ puts "missing"
51
+ GC.start()
52
+ thread_ran.should be_true
53
+ file_stat
54
+ }
55
+
56
+ t = Thread.new() { sleep 1.5 ; thread_ran = true }
57
+ with_fuse(mountpoint,mockfs) do
58
+ File.stat("#{mountpoint}/before");
59
+ sleep 2;
60
+ File.stat("#{mountpoint}/missing");
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,51 @@
1
+ require 'rfuse'
2
+ require 'tmpdir'
3
+
4
+ class FileModeMatcher
5
+ def initialize(expected)
6
+ @expected = expected
7
+ end
8
+
9
+ def description
10
+ "file mode #{@expected}"
11
+ end
12
+
13
+ def ==(actual)
14
+ (actual | RFuse::Stat::S_IFMT) == (@expected | RFuse::Stat::S_IFMT)
15
+ end
16
+ end
17
+
18
+ module RFuseHelper
19
+ # Runs the single threaded fuse loop
20
+ # on a pre configured mock fuse filesystem
21
+ # Executes fork block in a separate process
22
+ # that is expected to return success
23
+ def with_fuse(mnt,mockfs,*options,&fork_block)
24
+
25
+ fpid = Kernel.fork() {
26
+ sleep 0.5
27
+ begin
28
+ fork_block.call() if fork_block
29
+ ensure
30
+ sleep 0.3
31
+ system("fusermount -u #{mnt}")
32
+ end
33
+ }
34
+
35
+ fuse = RFuse::FuseDelegator.new(mockfs,mnt,*options)
36
+ fuse.loop
37
+ pid,result = Process.waitpid2(fpid)
38
+ result.should be_success
39
+ fuse.open_files.should be_empty()
40
+ fuse.mounted?.should be_false
41
+ end
42
+
43
+ def file_mode(mode)
44
+ FileModeMatcher.new(mode)
45
+ end
46
+
47
+ def tempmount()
48
+ Dir.mktmpdir("rfuse-spec")
49
+ end
50
+ end
51
+ include RFuseHelper
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'ffi-xattr'
3
+
4
+ describe RFuse::Fuse do
5
+ it "should handle extended attributes" do
6
+ mockfs = mock("fuse")
7
+
8
+ file_stat = RFuse::Stat.file(0444,:size => 11)
9
+
10
+ mockfs.stub(:getattr).and_return(file_stat)
11
+ mockfs.stub(:getxattr) { |ctx,path,name|
12
+ case name
13
+ when "user.one"
14
+ "1"
15
+ when "user.two"
16
+ "2"
17
+ end
18
+ }
19
+
20
+ mockfs.stub(:listxattr).with(anything(),"/myfile").and_return([ "user.one","user.two" ])
21
+ mockfs.should_receive(:setxattr).with(anything(),"/myfile","user.three","updated",anything())
22
+ mockfs.should_receive(:removexattr).with(anything(),"/myfile","user.one")
23
+
24
+ mountpoint = tempmount()
25
+
26
+ with_fuse(mountpoint,mockfs) do
27
+ xattr = Xattr.new("#{mountpoint}/myfile")
28
+ xattr.list.should include("user.one")
29
+ xattr.list.should include("user.two")
30
+ xattr.list.size.should == 2
31
+
32
+ xattr['user.one'].should == "1"
33
+ xattr['user.two'].should == "2"
34
+ xattr['user.three']= "updated"
35
+ xattr.remove('user.one')
36
+ end
37
+ end
38
+
39
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rfuse
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Grant Gardner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redcarpet
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: ffi-xattr
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Ruby language binding for FUSE. It was forked from rfuse and modified
95
+ for Ruby 1.9.2.
96
+ email:
97
+ - grant@lastweekend.com.au
98
+ executables: []
99
+ extensions:
100
+ - ext/rfuse/extconf.rb
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - CHANGES.md
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - ext/.gitignore
110
+ - ext/rfuse/bufferwrapper.c
111
+ - ext/rfuse/bufferwrapper.h
112
+ - ext/rfuse/context.c
113
+ - ext/rfuse/context.h
114
+ - ext/rfuse/extconf.rb
115
+ - ext/rfuse/file_info.c
116
+ - ext/rfuse/file_info.h
117
+ - ext/rfuse/filler.c
118
+ - ext/rfuse/filler.h
119
+ - ext/rfuse/helper.c
120
+ - ext/rfuse/helper.h
121
+ - ext/rfuse/intern_rfuse.c
122
+ - ext/rfuse/intern_rfuse.h
123
+ - ext/rfuse/pollhandle.c
124
+ - ext/rfuse/pollhandle.h
125
+ - ext/rfuse/rfuse.c
126
+ - ext/rfuse/rfuse.h
127
+ - ext/rfuse/rfuse_mod.c
128
+ - lib/rfuse-ng.rb
129
+ - lib/rfuse.rb
130
+ - lib/rfuse/version.rb
131
+ - lib/rfuse_ng.rb
132
+ - rfuse.gemspec
133
+ - sample/test-ruby.rb
134
+ - spec/basic_spec.rb
135
+ - spec/fuse_file_info_spec.rb
136
+ - spec/ruby_loop_spec.rb
137
+ - spec/spec_helper.rb
138
+ - spec/xattr_spec.rb
139
+ homepage: http://rubygems.org/gems/rfuse
140
+ licenses: []
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 1.8.24
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: Ruby language binding for FUSE
163
+ test_files: []
164
+ has_rdoc: