fssm 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 2
3
- :build:
4
2
  :major: 0
5
3
  :minor: 2
4
+ :patch: 3
5
+ :build:
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{fssm}
8
- s.version = "0.2.2"
8
+ s.version = "0.2.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Travis Tilley"]
12
- s.date = %q{2010-12-11}
12
+ s.date = %q{2010-12-18}
13
13
  s.description = %q{file system state monitor}
14
14
  s.email = %q{ttilley@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -42,6 +42,7 @@ Gem::Specification.new do |s|
42
42
  "profile/prof-pathname.rb",
43
43
  "profile/prof-plain-pathname.html",
44
44
  "profile/prof.html",
45
+ "spec/count_down_latch.rb",
45
46
  "spec/monitor_spec.rb",
46
47
  "spec/path_spec.rb",
47
48
  "spec/root/duck/quack.txt",
@@ -56,6 +57,7 @@ Gem::Specification.new do |s|
56
57
  s.rubygems_version = %q{1.3.7}
57
58
  s.summary = %q{file system state monitor}
58
59
  s.test_files = [
60
+ "spec/count_down_latch.rb",
59
61
  "spec/monitor_spec.rb",
60
62
  "spec/path_spec.rb",
61
63
  "spec/root/file.rb",
@@ -8,7 +8,7 @@ module FSSM::State
8
8
 
9
9
  def refresh(base=nil, skip_callbacks=false)
10
10
  base ||= @path.to_pathname
11
- used_to_exist, @exists = @exists, base.exists?
11
+ used_to_exist, @exists = @exists, base.exist?
12
12
  # this handles bad symlinks without failing. why handle bad symlinks at
13
13
  # all? well, we could still be interested in their creation and deletion.
14
14
  old_mtime, @mtime = @mtime, base.symlink? ? Time.at(0) : base.mtime if @exists
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'thread'
5
+
6
+ class CountDownLatch
7
+ attr_reader :count
8
+
9
+ def initialize(to)
10
+ @count = to.to_i
11
+ raise ArgumentError, "cannot count down from negative integer" unless @count >= 0
12
+ @lock = Mutex.new
13
+ @condition = ConditionVariable.new
14
+ end
15
+
16
+ def count_down
17
+ @lock.synchronize do
18
+ @count -= 1 if @count > 0
19
+ @condition.broadcast if @count == 0
20
+ end
21
+ end
22
+
23
+ def wait
24
+ @lock.synchronize do
25
+ @condition.wait(@lock) while @count > 0
26
+ end
27
+ end
28
+ end
29
+
30
+ if $0 == __FILE__
31
+ require 'test/unit'
32
+
33
+ class CountDownLatchTest < Test::Unit::TestCase
34
+ def test_requires_positive_count
35
+ assert_raise(ArgumentError) { CountDownLatch.new(-1) }
36
+ end
37
+
38
+ def test_basic_latch_usage
39
+ latch = CountDownLatch.new(1)
40
+ name = "foo"
41
+ Thread.new do
42
+ name = "bar"
43
+ latch.count_down
44
+ end
45
+ latch.wait
46
+ assert_equal(0, latch.count)
47
+ assert_equal("bar", name)
48
+ end
49
+
50
+ def test_basic_latch_usage_inverted
51
+ latch = CountDownLatch.new(1)
52
+ name = "foo"
53
+ Thread.new do
54
+ latch.wait
55
+ assert_equal(0, latch.count)
56
+ assert_equal("bar", name)
57
+ end
58
+ name = "bar"
59
+ latch.count_down
60
+ end
61
+
62
+ def test_count_down_from_zero_skips_wait
63
+ latch = CountDownLatch.new(0)
64
+ latch.wait
65
+ assert_equal(0, latch.count)
66
+ end
67
+
68
+ def test_count_down_twice_with_thread
69
+ latch = CountDownLatch.new(2)
70
+ name = "foo"
71
+ Thread.new do
72
+ latch.count_down
73
+ name = "bar"
74
+ latch.count_down
75
+ end
76
+ latch.wait
77
+ assert_equal(0, latch.count)
78
+ assert_equal("bar", name)
79
+ end
80
+
81
+ def test_count_down_twice_with_two_parallel_threads
82
+ latch = CountDownLatch.new(2)
83
+ name = "foo"
84
+ Thread.new { latch.count_down }
85
+ Thread.new do
86
+ name = "bar"
87
+ latch.count_down
88
+ end
89
+ latch.wait
90
+ assert_equal(0, latch.count)
91
+ assert_equal("bar", name)
92
+ end
93
+
94
+ def test_count_down_twice_with_two_chained_threads
95
+ latch = CountDownLatch.new(2)
96
+ name = "foo"
97
+ Thread.new do
98
+ latch.count_down
99
+ Thread.new do
100
+ name = "bar"
101
+ latch.count_down
102
+ end
103
+ end
104
+ latch.wait
105
+ assert_equal(0, latch.count)
106
+ assert_equal("bar", name)
107
+ end
108
+
109
+ def test_count_down_with_multiple_waiters
110
+ proceed_latch = CountDownLatch.new(2)
111
+ check_latch = CountDownLatch.new(2)
112
+ results = {}
113
+ Thread.new do
114
+ proceed_latch.wait
115
+ results[:first] = 1
116
+ check_latch.count_down
117
+ end
118
+ Thread.new do
119
+ proceed_latch.wait
120
+ results[:second] = 2
121
+ check_latch.count_down
122
+ end
123
+ assert_equal({}, results)
124
+ proceed_latch.count_down
125
+ proceed_latch.count_down
126
+ check_latch.wait
127
+ assert_equal(0, proceed_latch.count)
128
+ assert_equal(0, check_latch.count)
129
+ assert_equal({:first => 1, :second => 2}, results)
130
+ end
131
+
132
+ def test_interleaved_latches
133
+ change_1_latch = CountDownLatch.new(1)
134
+ check_latch = CountDownLatch.new(1)
135
+ change_2_latch = CountDownLatch.new(1)
136
+ name = "foo"
137
+ Thread.new do
138
+ name = "bar"
139
+ change_1_latch.count_down
140
+ check_latch.wait
141
+ name = "man"
142
+ change_2_latch.count_down
143
+ end
144
+ change_1_latch.wait
145
+ assert_equal("bar", name)
146
+ check_latch.count_down
147
+ change_2_latch.wait
148
+ assert_equal("man", name)
149
+ end
150
+ end
151
+ end
@@ -1,7 +1,8 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
- require "fileutils"
4
- require "tempfile"
3
+ require 'count_down_latch'
4
+ require 'fileutils'
5
+ require 'tempfile'
5
6
 
6
7
  module FSSM::MonitorSpecHelpers
7
8
  def create_tmp_dir
@@ -16,24 +17,28 @@ module FSSM::MonitorSpecHelpers
16
17
  FileUtils.remove_entry_secure @tmp_dir
17
18
  end
18
19
 
19
- def create_handler(type)
20
- lambda {|*args| @handler_results[type] << args}
20
+ def create_handler(type, latch)
21
+ lambda do |*args|
22
+ @handler_results[type] << args
23
+ latch.count_down
24
+ end
21
25
  end
22
26
 
23
- def create_monitor(options={})
27
+ def run_monitor(num_events_to_expect=0, options={})
28
+ event_latch = CountDownLatch.new(num_events_to_expect)
24
29
  @handler_results = Hash.new {|hash, key| hash[key] = []}
25
- @monitor = FSSM::Monitor.new(options)
26
- @monitor.path(@tmp_dir) do |p|
27
- p.create(&create_handler(:create))
28
- p.update(&create_handler(:update))
29
- p.delete(&create_handler(:delete))
30
+ thread = Thread.new do
31
+ monitor = FSSM::Monitor.new(options)
32
+ monitor.path(@tmp_dir) do |p|
33
+ p.create(&create_handler(:create, event_latch))
34
+ p.update(&create_handler(:update, event_latch))
35
+ p.delete(&create_handler(:delete, event_latch))
36
+ end
37
+ monitor.run
30
38
  end
31
- sleep 1 # give time for monitor to preload
32
- end
33
-
34
- def run_monitor
35
- thread = Thread.new {@monitor.run}
36
- sleep 2 # give time for monitor to see changes
39
+ sleep 1 # give time for monitor to start up
40
+ yield if block_given?
41
+ event_latch.wait
37
42
  thread.kill
38
43
  end
39
44
  end
@@ -51,136 +56,144 @@ describe "The File System State Monitor" do
51
56
  end
52
57
 
53
58
  describe "with default options" do
54
- before do
55
- create_monitor
56
- end
57
-
58
59
  it "should call create callback upon file creation" do
59
- file = @tmp_dir + "/newfile.rb"
60
- File.exists?(file).should be_false
61
- FileUtils.touch file
62
- run_monitor
60
+ run_monitor(1) do
61
+ file = @tmp_dir + "/newfile.rb"
62
+ File.exists?(file).should be_false
63
+ FileUtils.touch file
64
+ end
63
65
  @handler_results[:create].should == [[@tmp_dir, 'newfile.rb']]
64
66
  end
65
67
 
66
68
  it "should call update callback upon file modification" do
67
- FileUtils.touch @tmp_dir + '/root/file.rb'
68
- run_monitor
69
+ run_monitor(1) do
70
+ FileUtils.touch @tmp_dir + '/root/file.rb'
71
+ end
69
72
  @handler_results[:update].should == [[@tmp_dir, 'root/file.rb']]
70
73
  end
71
74
 
72
75
  it "should call delete callback upon file deletion" do
73
- FileUtils.rm @tmp_dir + "/root/file.rb"
74
- run_monitor
76
+ run_monitor(1) do
77
+ FileUtils.rm @tmp_dir + "/root/file.rb"
78
+ end
75
79
  @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
76
80
  end
77
81
 
78
82
  it "should call create and delete callbacks upon file renaming in the same directory" do
79
- FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
80
- run_monitor
83
+ run_monitor(2) do
84
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
85
+ end
81
86
  @handler_results[:create].should == [[@tmp_dir, 'root/old_file.rb']]
82
87
  @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
83
88
  @handler_results[:update].should == []
84
89
  end
85
90
 
86
91
  it "should call create and delete callbacks upon file moving to another directory" do
87
- FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
88
- run_monitor
92
+ run_monitor(2) do
93
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
94
+ end
89
95
  @handler_results[:create].should == [[@tmp_dir, 'old_file.rb']]
90
96
  @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']]
91
97
  @handler_results[:update].should == []
92
98
  end
93
99
 
94
100
  it "should not call callbacks upon directory operations" do
95
- FileUtils.mkdir @tmp_dir + "/another_yawn"
96
- FileUtils.rmdir @tmp_dir + "/root/yawn"
97
- run_monitor
101
+ run_monitor do
102
+ FileUtils.mkdir @tmp_dir + "/another_yawn"
103
+ FileUtils.rmdir @tmp_dir + "/root/yawn"
104
+ end
98
105
  @handler_results[:create].should == []
99
106
  @handler_results[:delete].should == []
100
107
  end
101
108
  end
102
109
 
103
110
  describe "when configured to consider files and directories" do
104
- before do
105
- create_monitor(:directories => true)
106
- end
107
-
108
111
  it "should call create callback upon directory creation" do
109
- FileUtils.mkdir @tmp_dir + "/another_yawn"
110
- run_monitor
112
+ run_monitor(1, :directories => true) do
113
+ FileUtils.mkdir @tmp_dir + "/another_yawn"
114
+ end
111
115
  @handler_results[:create].should == [[@tmp_dir, 'another_yawn', :directory]]
112
116
  end
113
117
 
114
118
  it "should call delete callback upon directory deletion" do
115
- FileUtils.rmdir @tmp_dir + "/root/yawn"
116
- run_monitor
119
+ run_monitor(1, :directories => true) do
120
+ FileUtils.rmdir @tmp_dir + "/root/yawn"
121
+ end
117
122
  @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
118
123
  end
119
124
 
120
125
  it "should call create, update, and delete callbacks upon directory renaming in the same directory" do
121
- FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/root/old_yawn"
122
- run_monitor
126
+ run_monitor(3, :directories => true) do
127
+ FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/root/old_yawn"
128
+ end
123
129
  @handler_results[:create].should == [[@tmp_dir, 'root/old_yawn', :directory]]
124
130
  @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
125
131
  @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
126
132
  end
127
133
 
128
134
  it "should call create, update, and delete callbacks upon directory moving to another directory" do
129
- FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/old_yawn"
130
- run_monitor
135
+ run_monitor(3, :directories => true) do
136
+ FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/old_yawn"
137
+ end
131
138
  @handler_results[:create].should == [[@tmp_dir, 'old_yawn', :directory]]
132
139
  @handler_results[:delete].should == [[@tmp_dir, 'root/yawn', :directory]]
133
140
  @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
134
141
  end
135
142
 
136
143
  it "should call create, update, and delete callbacks upon file renaming in the same directory" do
137
- FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
138
- run_monitor
144
+ run_monitor(3, :directories => true) do
145
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb"
146
+ end
139
147
  @handler_results[:create].should == [[@tmp_dir, 'root/old_file.rb', :file]]
140
148
  @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb', :file]]
141
149
  @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
142
150
  end
143
151
 
144
152
  it "should call create, update, and delete callbacks upon file moving to another directory" do
145
- FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
146
- run_monitor
153
+ run_monitor(3, :directories => true) do
154
+ FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb"
155
+ end
147
156
  @handler_results[:create].should == [[@tmp_dir, 'old_file.rb', :file]]
148
157
  @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb', :file]]
149
158
  @handler_results[:update].should == [[@tmp_dir, 'root', :directory]]
150
159
  end
151
160
 
152
161
  it "should call delete callbacks upon directory structure deletion, in reverse order" do
153
- FileUtils.rm_rf @tmp_dir + '/.'
154
- run_monitor
162
+ expected_delete_events = [
163
+ ['root/yawn', :directory],
164
+ ['root/moo/cow.txt', :file],
165
+ ['root/moo', :directory],
166
+ ['root/file.yml', :file],
167
+ ['root/file.rb', :file],
168
+ ['root/file.css', :file],
169
+ ['root/duck/quack.txt', :file],
170
+ ['root/duck', :directory],
171
+ ['root', :directory]
172
+ ]
173
+ run_monitor(expected_delete_events.size, :directories => true) do
174
+ FileUtils.rm_rf @tmp_dir + '/.'
175
+ end
155
176
  @handler_results[:create].should == []
156
- @handler_results[:delete].should == [
157
- ['root/yawn', :directory],
158
- ['root/moo/cow.txt', :file],
159
- ['root/moo', :directory],
160
- ['root/file.yml', :file],
161
- ['root/file.rb', :file],
162
- ['root/file.css', :file],
163
- ['root/duck/quack.txt', :file],
164
- ['root/duck', :directory],
165
- ['root', :directory]
166
- ].map {|(file, type)| [@tmp_dir, file, type]}
177
+ @handler_results[:delete].should == expected_delete_events.map {|(file, type)| [@tmp_dir, file, type]}
167
178
  @handler_results[:update].should == []
168
179
  end
169
180
 
170
181
  it "should call create callbacks upon directory structure creation, in order" do
171
- FileUtils.cp_r @tmp_dir + '/root/.', @tmp_dir + '/new_root'
172
- run_monitor
173
- @handler_results[:create].should == [
174
- ['new_root', :directory],
175
- ['new_root/duck', :directory],
176
- ['new_root/duck/quack.txt', :file],
177
- ['new_root/file.css', :file],
178
- ['new_root/file.rb', :file],
179
- ['new_root/file.yml', :file],
180
- ['new_root/moo', :directory],
181
- ['new_root/moo/cow.txt', :file],
182
- ['new_root/yawn', :directory]
183
- ].map {|(file, type)| [@tmp_dir, file, type]}
182
+ expected_create_events = [
183
+ ['new_root', :directory],
184
+ ['new_root/duck', :directory],
185
+ ['new_root/duck/quack.txt', :file],
186
+ ['new_root/file.css', :file],
187
+ ['new_root/file.rb', :file],
188
+ ['new_root/file.yml', :file],
189
+ ['new_root/moo', :directory],
190
+ ['new_root/moo/cow.txt', :file],
191
+ ['new_root/yawn', :directory]
192
+ ]
193
+ run_monitor(expected_create_events.size, :directories => true) do
194
+ FileUtils.cp_r @tmp_dir + '/root/.', @tmp_dir + '/new_root'
195
+ end
196
+ @handler_results[:create].should == expected_create_events.map {|(file, type)| [@tmp_dir, file, type]}
184
197
  @handler_results[:delete].should == []
185
198
  @handler_results[:update].should == []
186
199
  end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fssm
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 2
9
- - 2
10
- version: 0.2.2
8
+ - 3
9
+ version: 0.2.3
11
10
  platform: ruby
12
11
  authors:
13
12
  - Travis Tilley
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-12-11 00:00:00 -05:00
17
+ date: 2010-12-18 00:00:00 -05:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 3
30
28
  segments:
31
29
  - 0
32
30
  version: "0"
@@ -67,6 +65,7 @@ files:
67
65
  - profile/prof-pathname.rb
68
66
  - profile/prof-plain-pathname.html
69
67
  - profile/prof.html
68
+ - spec/count_down_latch.rb
70
69
  - spec/monitor_spec.rb
71
70
  - spec/path_spec.rb
72
71
  - spec/root/duck/quack.txt
@@ -89,7 +88,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
88
  requirements:
90
89
  - - ">="
91
90
  - !ruby/object:Gem::Version
92
- hash: 3
93
91
  segments:
94
92
  - 0
95
93
  version: "0"
@@ -98,7 +96,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
96
  requirements:
99
97
  - - ">="
100
98
  - !ruby/object:Gem::Version
101
- hash: 3
102
99
  segments:
103
100
  - 0
104
101
  version: "0"
@@ -110,6 +107,7 @@ signing_key:
110
107
  specification_version: 3
111
108
  summary: file system state monitor
112
109
  test_files:
110
+ - spec/count_down_latch.rb
113
111
  - spec/monitor_spec.rb
114
112
  - spec/path_spec.rb
115
113
  - spec/root/file.rb