nestor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +34 -0
  5. data/Rakefile +76 -0
  6. data/VERSION +1 -0
  7. data/bin/nestor +3 -0
  8. data/doc/.gitignore +3 -0
  9. data/doc/state-diagram.graffle +3870 -0
  10. data/doc/state-diagram.png +0 -0
  11. data/lib/nestor/cli.rb +52 -0
  12. data/lib/nestor/machine.rb +161 -0
  13. data/lib/nestor/strategies/test/unit.rb +116 -0
  14. data/lib/nestor/strategies.rb +18 -0
  15. data/lib/nestor/watchers/rails.rb +56 -0
  16. data/lib/nestor/watchers/rails_script.rb +83 -0
  17. data/lib/nestor/watchers.rb +1 -0
  18. data/lib/nestor.rb +11 -0
  19. data/spec/machine_spec.rb +56 -0
  20. data/spec/spec_helper.rb +9 -0
  21. data/vendor/watchr-0.5.7/.gitignore +5 -0
  22. data/vendor/watchr-0.5.7/History.txt +32 -0
  23. data/vendor/watchr-0.5.7/LICENSE +19 -0
  24. data/vendor/watchr-0.5.7/Manifest +27 -0
  25. data/vendor/watchr-0.5.7/README.rdoc +108 -0
  26. data/vendor/watchr-0.5.7/Rakefile +49 -0
  27. data/vendor/watchr-0.5.7/TODO.txt +40 -0
  28. data/vendor/watchr-0.5.7/bin/watchr +77 -0
  29. data/vendor/watchr-0.5.7/docs.watchr +26 -0
  30. data/vendor/watchr-0.5.7/gem.watchr +32 -0
  31. data/vendor/watchr-0.5.7/lib/watchr/controller.rb +81 -0
  32. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/base.rb +48 -0
  33. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/portable.rb +55 -0
  34. data/vendor/watchr-0.5.7/lib/watchr/event_handlers/unix.rb +97 -0
  35. data/vendor/watchr-0.5.7/lib/watchr/script.rb +203 -0
  36. data/vendor/watchr-0.5.7/lib/watchr.rb +113 -0
  37. data/vendor/watchr-0.5.7/manifest.watchr +70 -0
  38. data/vendor/watchr-0.5.7/specs.watchr +38 -0
  39. data/vendor/watchr-0.5.7/test/README +11 -0
  40. data/vendor/watchr-0.5.7/test/event_handlers/test_base.rb +24 -0
  41. data/vendor/watchr-0.5.7/test/event_handlers/test_portable.rb +58 -0
  42. data/vendor/watchr-0.5.7/test/event_handlers/test_unix.rb +162 -0
  43. data/vendor/watchr-0.5.7/test/test_controller.rb +103 -0
  44. data/vendor/watchr-0.5.7/test/test_helper.rb +52 -0
  45. data/vendor/watchr-0.5.7/test/test_script.rb +123 -0
  46. data/vendor/watchr-0.5.7/test/test_watchr.rb +60 -0
  47. data/vendor/watchr-0.5.7/watchr.gemspec +60 -0
  48. metadata +152 -0
@@ -0,0 +1,203 @@
1
+ module Watchr
2
+
3
+ # A script object wraps a script file, and is used by a controller.
4
+ #
5
+ # ===== Examples
6
+ #
7
+ # path = Pathname.new('specs.watchr')
8
+ # script = Watchr::Script.new(path)
9
+ #
10
+ class Script
11
+ DEFAULT_EVENT_TYPE = :modified
12
+
13
+ # Convenience type. Provides clearer and simpler access to rule properties.
14
+ #
15
+ # ===== Examples
16
+ #
17
+ # rule = script.watch('lib/.*\.rb') { 'ohaie' }
18
+ # rule.pattern #=> 'lib/.*\.rb'
19
+ # rule.action.call #=> 'ohaie'
20
+ #
21
+ Rule = Struct.new(:pattern, :event_type, :action)
22
+
23
+ # TODO eval context
24
+ class API #:nodoc:
25
+ end
26
+
27
+ # Creates a script object for <tt>path</tt>.
28
+ #
29
+ # Does not parse the script. The controller knows when to parse the script.
30
+ #
31
+ # ===== Parameters
32
+ # path<Pathname>:: the path to the script
33
+ #
34
+ def initialize(path)
35
+ @path = path
36
+ @rules = []
37
+ @default_action = lambda {}
38
+ end
39
+
40
+ # Main script API method. Builds a new rule, binding a pattern to an action.
41
+ #
42
+ # Whenever a file is saved that matches a rule's <tt>pattern</tt>, its
43
+ # corresponding <tt>action</tt> is triggered.
44
+ #
45
+ # Patterns can be either a Regexp or a string. Because they always
46
+ # represent paths however, it's simpler to use strings. But remember to use
47
+ # single quotes (not double quotes), otherwise escape sequences will be
48
+ # parsed (for example "foo/bar\.rb" #=> "foo/bar.rb", notice "\." becomes
49
+ # "."), and won't be interpreted as the regexp you expect.
50
+ #
51
+ # Also note that patterns will be matched against relative paths (relative
52
+ # from current working directory).
53
+ #
54
+ # Actions, the blocks passed to <tt>watch</tt>, receive a MatchData object
55
+ # as argument. It will be populated with the whole matched string (md[0])
56
+ # as well as individual backreferences (md[1..n]). See MatchData#[]
57
+ # documentation for more details.
58
+ #
59
+ # ===== Examples
60
+ #
61
+ # # in script file
62
+ # watch( 'test/test_.*\.rb' ) {|md| system("ruby #{md[0]}") }
63
+ # watch( 'lib/(.*)\.rb' ) {|md| system("ruby test/test_#{md[1]}.rb") }
64
+ #
65
+ # With these two rules, watchr will run any test file whenever it is itself
66
+ # changed (first rule), and will also run a corresponding test file
67
+ # whenever a lib file is changed (second rule).
68
+ #
69
+ # ===== Parameters
70
+ # pattern<~#match>:: pattern to match targetted paths
71
+ # event_type<Symbol>::
72
+ # Rule will only match events of this type. Accepted types are :accessed,
73
+ # :modified, :changed, :delete and nil (any), where the first three
74
+ # correspond to atime, mtime and ctime respectively. Defaults to
75
+ # :modified.
76
+ # action<Block>:: action to trigger
77
+ #
78
+ # ===== Returns
79
+ # rule<Rule>:: rule created by the method
80
+ #
81
+ def watch(pattern, event_type = DEFAULT_EVENT_TYPE, &action)
82
+ @rules << Rule.new(pattern, event_type, action || @default_action)
83
+ @rules.last
84
+ end
85
+
86
+ # Convenience method. Define a default action to be triggered when a rule
87
+ # has none specified.
88
+ #
89
+ # ===== Examples
90
+ #
91
+ # # in script file
92
+ #
93
+ # default_action { system('rake --silent rdoc') }
94
+ #
95
+ # watch( 'lib/.*\.rb' )
96
+ # watch( 'README.rdoc' )
97
+ # watch( 'TODO.txt' )
98
+ # watch( 'LICENSE' )
99
+ #
100
+ # # equivalent to:
101
+ #
102
+ # watch( 'lib/.*\.rb' ) { system('rake --silent rdoc') }
103
+ # watch( 'README.rdoc' ) { system('rake --silent rdoc') }
104
+ # watch( 'TODO.txt' ) { system('rake --silent rdoc') }
105
+ # watch( 'LICENSE' ) { system('rake --silent rdoc') }
106
+ #
107
+ def default_action(&action)
108
+ @default_action = action
109
+ end
110
+
111
+ # Eval content of script file.
112
+ #--
113
+ # TODO fix script file not found error
114
+ def parse!
115
+ Watchr.debug('loading script file %s' % @path.to_s.inspect)
116
+
117
+ reset
118
+ instance_eval(@path.read)
119
+
120
+ rescue Errno::ENOENT
121
+ # TODO figure out why this is happening. still can't reproduce
122
+ Watchr.debug('script file "not found". wth')
123
+ sleep(0.3) #enough?
124
+ instance_eval(@path.read)
125
+ end
126
+
127
+ # Find an action corresponding to a path and event type. The returned
128
+ # action is actually a wrapper around the rule's action, with the
129
+ # match_data prepopulated.
130
+ #
131
+ # ===== Params
132
+ # path<Pathnane,String>:: Find action that correspond to this path.
133
+ # event_type<Symbol>:: Find action only if rule's event if of this type.
134
+ #
135
+ # ===== Examples
136
+ #
137
+ # script.watch( 'test/test_.*\.rb' ) {|md| "ruby #{md[0]}" }
138
+ # script.action_for('test/test_watchr.rb').call #=> "ruby test/test_watchr.rb"
139
+ #
140
+ def action_for(path, event_type = DEFAULT_EVENT_TYPE)
141
+ path = rel_path(path).to_s
142
+ rule = rules_for(path).detect {|rule| rule.event_type.nil? || rule.event_type == event_type }
143
+ if rule
144
+ data = path.match(rule.pattern)
145
+ lambda { rule.action.call(data) }
146
+ else
147
+ lambda {}
148
+ end
149
+ end
150
+
151
+ # Collection of all patterns defined in script.
152
+ #
153
+ # ===== Returns
154
+ # patterns<String, Regexp>:: all patterns
155
+ #
156
+ def patterns
157
+ #@rules.every.pattern
158
+ @rules.map {|r| r.pattern }
159
+ end
160
+
161
+ # Path to the script file
162
+ #
163
+ # ===== Returns
164
+ # path<Pathname>:: path to script file
165
+ #
166
+ def path
167
+ Pathname(@path.respond_to?(:to_path) ? @path.to_path : @path.to_s).expand_path
168
+ end
169
+
170
+ private
171
+
172
+ # Rules corresponding to a given path, in reversed order of precedence
173
+ # (latest one is most inportant).
174
+ #
175
+ # ===== Parameters
176
+ # path<Pathname, String>:: path to look up rule for
177
+ #
178
+ # ===== Returns
179
+ # rules<Array(Rule)>:: rules corresponding to <tt>path</tt>
180
+ #
181
+ def rules_for(path)
182
+ @rules.reverse.select {|rule| path.match(rule.pattern) }
183
+ end
184
+
185
+ # Make a path relative to current working directory.
186
+ #
187
+ # ===== Parameters
188
+ # path<Pathname, String>:: absolute or relative path
189
+ #
190
+ # ===== Returns
191
+ # path<Pathname>:: relative path, from current working directory.
192
+ #
193
+ def rel_path(path)
194
+ Pathname(path).expand_path.relative_path_from(Pathname(Dir.pwd))
195
+ end
196
+
197
+ # Reset script state
198
+ def reset
199
+ @default_action = lambda {}
200
+ @rules.clear
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,113 @@
1
+ require 'pathname'
2
+ require 'rbconfig'
3
+
4
+ # Agile development tool that monitors a directory recursively, and triggers a
5
+ # user defined action whenever an observed file is modified. Its most typical
6
+ # use is continuous testing.
7
+ #
8
+ # Usage:
9
+ #
10
+ # # on command line, from project's root dir
11
+ # $ watchr path/to/script
12
+ #
13
+ # See README for more details
14
+ #
15
+ module Watchr
16
+ VERSION = '0.5.7'
17
+
18
+ begin
19
+ require 'rev'
20
+ HAVE_REV = true
21
+ rescue LoadError, RuntimeError
22
+ HAVE_REV = false
23
+ end
24
+
25
+ autoload :Script, 'watchr/script'
26
+ autoload :Controller, 'watchr/controller'
27
+
28
+ module EventHandler
29
+ autoload :Base, 'watchr/event_handlers/base'
30
+ autoload :Unix, 'watchr/event_handlers/unix' if ::Watchr::HAVE_REV
31
+ autoload :Portable, 'watchr/event_handlers/portable'
32
+ end
33
+
34
+ class << self
35
+ attr_accessor :options
36
+ attr_accessor :handler
37
+
38
+ # backwards compatibility
39
+ def version #:nodoc:
40
+ Watchr::VERSION
41
+ end
42
+
43
+ # Options proxy.
44
+ #
45
+ # Currently supported options:
46
+ # * debug<Boolean> Debugging state. More verbose.
47
+ #
48
+ # ===== Examples
49
+ #
50
+ # Watchr.options.debug #=> false
51
+ # Watchr.options.debug = true
52
+ #
53
+ # ===== Returns
54
+ # options<Struct>:: options proxy.
55
+ #
56
+ #--
57
+ # On first use, initialize the options struct and default option values.
58
+ def options
59
+ @options ||= Struct.new(:debug).new
60
+ @options.debug ||= false
61
+ @options
62
+ end
63
+
64
+ # Outputs formatted debug statement to stdout, only if ::options.debug is true
65
+ #
66
+ # ===== Examples
67
+ #
68
+ # Watchr.options.debug = true
69
+ # Watchr.debug('im in ur codes, notifayinin u')
70
+ #
71
+ # outputs: "[watchr debug] im in ur codes, notifayinin u"
72
+ #
73
+ def debug(str)
74
+ puts "[watchr debug] #{str}" if options.debug
75
+ end
76
+
77
+ # Detect current OS and return appropriate handler.
78
+ #
79
+ # ===== Examples
80
+ #
81
+ # Config::CONFIG['host_os'] #=> 'linux-gnu'
82
+ # Watchr.handler #=> Watchr::EventHandler::Unix
83
+ #
84
+ # Config::CONFIG['host_os'] #=> 'cygwin'
85
+ # Watchr.handler #=> Watchr::EventHandler::Portable
86
+ #
87
+ # ENV['HANDLER'] #=> 'unix'
88
+ # Watchr.handler #=> Watchr::EventHandler::Unix
89
+ #
90
+ # ENV['HANDLER'] #=> 'portable'
91
+ # Watchr.handler #=> Watchr::EventHandler::Portable
92
+ #
93
+ # ===== Returns
94
+ # handler<Class>:: handler class for current architecture
95
+ #
96
+ def handler
97
+ @handler ||=
98
+ case ENV['HANDLER'] || Config::CONFIG['host_os']
99
+ when /mswin|windows|cygwin/i
100
+ Watchr::EventHandler::Portable
101
+ when /sunos|solaris|darwin|mach|osx|bsd|linux/i, 'unix'
102
+ if ::Watchr::HAVE_REV
103
+ Watchr::EventHandler::Unix
104
+ else
105
+ Watchr.debug "rev not found. `gem install rev` to get evented handler"
106
+ Watchr::EventHandler::Portable
107
+ end
108
+ else
109
+ Watchr::EventHandler::Portable
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,70 @@
1
+ # Run me with:
2
+ #
3
+ # $ watchr manifest.watchr
4
+ #
5
+ # This script will remove a file from from the Manifest when it gets deleted,
6
+ # and will rebuild the Manifest on Ctrl-\
7
+ #
8
+ # Mostly serves as a demo for the :delete event type (and eventually for the
9
+ # :added event type). In reality this is much better implemented as a git
10
+ # post-commit script.
11
+ #
12
+
13
+ require 'pathname'
14
+ # --------------------------------------------------
15
+ # Helpers
16
+ # --------------------------------------------------
17
+ module Project
18
+ extend self
19
+ def files
20
+ `git ls-files --full-name`.strip.split($/).sort
21
+ end
22
+ end
23
+
24
+ class Manifest
25
+ attr_accessor :path
26
+
27
+ def initialize(path)
28
+ @path = Pathname(path).expand_path
29
+ create!
30
+ end
31
+
32
+ def remove(path)
33
+ paths = @path.read.strip.split($/)
34
+ @path.open('w') {|f| f << (paths - [path]).join("\n") }
35
+ end
36
+
37
+ def add(path)
38
+ paths = @path.read.strip.split($/)
39
+ @path.open('w') {|f| f << paths.push(path).sort.join("\n") }
40
+ end
41
+
42
+ private
43
+ def create!
44
+ File.open(@path.to_s, 'w') {} unless @path.exist?
45
+ end
46
+ end
47
+
48
+
49
+ @manifest = Manifest.new('Manifest')
50
+
51
+ # --------------------------------------------------
52
+ # Watchr Rules
53
+ # --------------------------------------------------
54
+ watch('.*', :deleted ) do |md|
55
+ @manifest.remove(md[0])
56
+ puts "removed #{md[0].inspect} from Manifest"
57
+ end
58
+
59
+ # --------------------------------------------------
60
+ # Signal Handling
61
+ # --------------------------------------------------
62
+ # Ctrl-\
63
+ Signal.trap('QUIT') do
64
+ puts " --- Updated Manifest ---\n"
65
+ @manifest.path.open('w') {|m| m << Project.files.join("\n").strip }
66
+ end
67
+
68
+ # Ctrl-C
69
+ Signal.trap('INT') { abort("\n") }
70
+
@@ -0,0 +1,38 @@
1
+ # Run me with:
2
+ #
3
+ # $ watchr specs.watchr
4
+
5
+ # --------------------------------------------------
6
+ # Convenience Methods
7
+ # --------------------------------------------------
8
+ def run(cmd)
9
+ puts(cmd)
10
+ system(cmd)
11
+ end
12
+
13
+ def run_all_tests
14
+ # see Rakefile for the definition of the test:all task
15
+ system( "rake -s test:all VERBOSE=true" )
16
+ end
17
+
18
+ # --------------------------------------------------
19
+ # Watchr Rules
20
+ # --------------------------------------------------
21
+ watch( '^test.*/test_.*\.rb' ) { |m| run( "ruby -rubygems %s" % m[0] ) }
22
+ watch( '^lib/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
23
+ watch( '^lib/watchr/(.*)\.rb' ) { |m| run( "ruby -rubygems test/test_%s.rb" % m[1] ) }
24
+ watch( '^lib/watchr/event_handlers/(.*)\.rb' ) { |m| run( "ruby -rubygems test/event_handlers/test_%s.rb" % m[1] ) }
25
+ watch( '^test/test_helper\.rb' ) { run_all_tests }
26
+
27
+ # --------------------------------------------------
28
+ # Signal Handling
29
+ # --------------------------------------------------
30
+ # Ctrl-\
31
+ Signal.trap('QUIT') do
32
+ puts " --- Running all tests ---\n\n"
33
+ run_all_tests
34
+ end
35
+
36
+ # Ctrl-C
37
+ Signal.trap('INT') { abort("\n") }
38
+
@@ -0,0 +1,11 @@
1
+
2
+ To use local watchr executable for dev work:
3
+
4
+ $ ruby -rubygems -Ilib ./bin/watchr -d specs.watchr
5
+
6
+ To force a specific handler:
7
+
8
+ $ HANDLER=protable watchr -d specs.watchr
9
+ $ HANDLER=unix watchr -d specs.watchr
10
+
11
+ (see Watchr.handler)
@@ -0,0 +1,24 @@
1
+ require 'test/test_helper'
2
+
3
+ class BaseEventHandlerTest < Test::Unit::TestCase
4
+
5
+ class Handler
6
+ include Watchr::EventHandler::Base
7
+ end
8
+
9
+ def setup
10
+ @handler = Handler.new
11
+ end
12
+
13
+ test "api" do
14
+ @handler.should respond_to(:notify)
15
+ @handler.should respond_to(:listen)
16
+ @handler.should respond_to(:refresh)
17
+ @handler.class.ancestors.should include(Observable)
18
+ end
19
+
20
+ test "notifies observers" do
21
+ @handler.expects(:notify_observers).with('foo/bar', nil)
22
+ @handler.notify('foo/bar', nil)
23
+ end
24
+ end
@@ -0,0 +1,58 @@
1
+ require 'test/test_helper'
2
+
3
+ class PortableEventHandlerTest < Test::Unit::TestCase
4
+ include Watchr
5
+
6
+ def setup
7
+ @handler = EventHandler::Portable.new
8
+ @handler.stubs(:loop)
9
+
10
+ @foo = Pathname('foo').expand_path
11
+ @bar = Pathname('bar').expand_path
12
+ @baz = Pathname('baz').expand_path
13
+ @bax = Pathname('bax').expand_path
14
+
15
+ @foo.stubs(:mtime).returns(Time.now - 100)
16
+ @bar.stubs(:mtime).returns(Time.now - 100)
17
+ @baz.stubs(:mtime).returns(Time.now - 100)
18
+ @bax.stubs(:mtime).returns(Time.now - 100)
19
+ end
20
+
21
+ test "triggers listening state" do
22
+ @handler.expects(:loop)
23
+ @handler.listen([])
24
+ end
25
+
26
+ ## monitoring file events
27
+
28
+ test "listens for events on monitored files" do
29
+ @handler.listen [ @foo, @bar ]
30
+ @handler.monitored_paths.should include(@foo)
31
+ @handler.monitored_paths.should include(@bar)
32
+ end
33
+
34
+ test "notifies observers on file event" do
35
+ @foo.stubs(:mtime).returns(Time.now + 100) # fake event
36
+
37
+ @handler.listen [ @foo, @bar ]
38
+ @handler.expects(:notify).with(@foo, :modified)
39
+ @handler.trigger
40
+ end
41
+
42
+ test "doesn't trigger on start" do
43
+ end
44
+
45
+ ## on the fly updates of monitored files list
46
+
47
+ test "reattaches to new monitored files" do
48
+ @handler.listen [ @foo, @bar ]
49
+ @handler.monitored_paths.should include(@foo)
50
+ @handler.monitored_paths.should include(@bar)
51
+
52
+ @handler.refresh [ @baz, @bax ]
53
+ @handler.monitored_paths.should include(@baz)
54
+ @handler.monitored_paths.should include(@bax)
55
+ @handler.monitored_paths.should exclude(@foo)
56
+ @handler.monitored_paths.should exclude(@bar)
57
+ end
58
+ end
@@ -0,0 +1,162 @@
1
+ require 'test/test_helper'
2
+
3
+ if Watchr::HAVE_REV
4
+
5
+ class Watchr::EventHandler::Unix::SingleFileWatcher
6
+ public :type
7
+ end
8
+
9
+ class UnixEventHandlerTest < Test::Unit::TestCase
10
+ include Watchr
11
+
12
+ SingleFileWatcher = EventHandler::Unix::SingleFileWatcher
13
+
14
+ def setup
15
+ @now = Time.now
16
+ pathname = Pathname.new('foo/bar')
17
+ pathname.stubs(:atime ).returns(@now)
18
+ pathname.stubs(:mtime ).returns(@now)
19
+ pathname.stubs(:ctime ).returns(@now)
20
+ pathname.stubs(:exist?).returns(true)
21
+ SingleFileWatcher.any_instance.stubs(:pathname).returns(pathname)
22
+
23
+ @loop = Rev::Loop.default
24
+ @handler = EventHandler::Unix.new
25
+ @watcher = SingleFileWatcher.new('foo/bar')
26
+ @loop.stubs(:run)
27
+ end
28
+
29
+ def teardown
30
+ SingleFileWatcher.handler = nil
31
+ Rev::Loop.default.watchers.every.detach
32
+ end
33
+
34
+ test "triggers listening state" do
35
+ @loop.expects(:run)
36
+ @handler.listen([])
37
+ end
38
+
39
+ ## SingleFileWatcher
40
+
41
+ test "watcher pathname" do
42
+ @watcher.pathname.should be_kind_of(Pathname)
43
+ @watcher.pathname.to_s.should be(@watcher.path)
44
+ end
45
+
46
+ test "stores reference times" do
47
+ @watcher.pathname.stubs(:atime).returns(:time)
48
+ @watcher.pathname.stubs(:mtime).returns(:time)
49
+ @watcher.pathname.stubs(:ctime).returns(:time)
50
+
51
+ @watcher.send(:update_reference_times)
52
+ @watcher.instance_variable_get(:@reference_atime).should be(:time)
53
+ @watcher.instance_variable_get(:@reference_mtime).should be(:time)
54
+ @watcher.instance_variable_get(:@reference_ctime).should be(:time)
55
+ end
56
+
57
+ test "stores initial reference times" do
58
+ SingleFileWatcher.any_instance.expects(:update_reference_times)
59
+ SingleFileWatcher.new('foo')
60
+ end
61
+
62
+ test "updates reference times on change" do
63
+ @watcher.expects(:update_reference_times)
64
+ @watcher.on_change
65
+ end
66
+
67
+ test "detects event type" do
68
+ trigger_event @watcher, @now, :atime
69
+ @watcher.type.should be(:accessed)
70
+
71
+ trigger_event @watcher, @now, :mtime
72
+ @watcher.type.should be(:modified)
73
+
74
+ trigger_event @watcher, @now, :ctime
75
+ @watcher.type.should be(:changed)
76
+
77
+ trigger_event @watcher, @now, :atime, :mtime
78
+ @watcher.type.should be(:modified)
79
+
80
+ trigger_event @watcher, @now, :mtime, :ctime
81
+ @watcher.type.should be(:modified)
82
+
83
+ trigger_event @watcher, @now, :atime, :ctime
84
+ @watcher.type.should be(:accessed)
85
+
86
+ trigger_event @watcher, @now, :atime, :mtime, :ctime
87
+ @watcher.type.should be(:modified)
88
+
89
+ @watcher.pathname.stubs(:exist?).returns(false)
90
+ @watcher.type.should be(:deleted)
91
+ end
92
+
93
+ ## monitoring file events
94
+
95
+ test "listens for events on monitored files" do
96
+ @handler.listen %w( foo bar )
97
+ @loop.watchers.size.should be(2)
98
+ @loop.watchers.every.path.should include('foo', 'bar')
99
+ @loop.watchers.every.class.uniq.should be([SingleFileWatcher])
100
+ end
101
+
102
+ test "notifies observers on file event" do
103
+ @watcher.stubs(:path).returns('foo')
104
+ @handler.expects(:notify).with('foo', anything)
105
+ @watcher.on_change
106
+ end
107
+
108
+ test "notifies observers of event type" do
109
+ trigger_event @watcher, @now, :atime
110
+ @handler.expects(:notify).with('foo/bar', :accessed)
111
+ @watcher.on_change
112
+
113
+ trigger_event @watcher, @now, :mtime
114
+ @handler.expects(:notify).with('foo/bar', :modified)
115
+ @watcher.on_change
116
+
117
+ trigger_event @watcher, @now, :ctime
118
+ @handler.expects(:notify).with('foo/bar', :changed)
119
+ @watcher.on_change
120
+
121
+ trigger_event @watcher, @now, :atime, :mtime, :ctime
122
+ @handler.expects(:notify).with('foo/bar', :modified)
123
+ @watcher.on_change
124
+
125
+ @watcher.pathname.stubs(:exist?).returns(false)
126
+ @handler.expects(:notify).with('foo/bar', :deleted)
127
+ @watcher.on_change
128
+ end
129
+
130
+ ## on the fly updates of monitored files list
131
+
132
+ test "reattaches to new monitored files" do
133
+ @handler.listen %w( foo bar )
134
+ @loop.watchers.size.should be(2)
135
+ @loop.watchers.every.path.should include('foo')
136
+ @loop.watchers.every.path.should include('bar')
137
+
138
+ @handler.refresh %w( baz bax )
139
+ @loop.watchers.size.should be(2)
140
+ @loop.watchers.every.path.should include('baz')
141
+ @loop.watchers.every.path.should include('bax')
142
+ @loop.watchers.every.path.should exclude('foo')
143
+ @loop.watchers.every.path.should exclude('bar')
144
+ end
145
+
146
+ private
147
+
148
+ def trigger_event(watcher, now, *types)
149
+ watcher.pathname.stubs(:atime).returns(now)
150
+ watcher.pathname.stubs(:mtime).returns(now)
151
+ watcher.pathname.stubs(:ctime).returns(now)
152
+ watcher.instance_variable_set(:@reference_atime, now)
153
+ watcher.instance_variable_set(:@reference_mtime, now)
154
+ watcher.instance_variable_set(:@reference_ctime, now)
155
+
156
+ types.each do |type|
157
+ watcher.pathname.stubs(type).returns(now+10)
158
+ end
159
+ end
160
+ end
161
+
162
+ end # if Watchr::HAVE_REV