sass 3.7.4 → 4.0.0.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -0,0 +1,258 @@
1
+ require 'spec_helper'
2
+
3
+ describe Listen::Listener do
4
+ let(:adapter) { double(Listen::Adapter, :start => true).as_null_object }
5
+ let(:watched_directories) { [File.dirname(__FILE__), File.expand_path('../..', __FILE__)] }
6
+
7
+ before do
8
+ Listen::Adapter.stub(:select_and_initialize) { adapter }
9
+ # Don't build a record of the files inside the base directory.
10
+ Listen::DirectoryRecord.any_instance.stub(:build)
11
+ Kernel.stub(:warn)
12
+ end
13
+ subject { described_class.new(watched_directories) }
14
+
15
+ it_should_behave_like 'a listener to changes on a file-system'
16
+
17
+ describe '#initialize' do
18
+ context 'listening to a single directory' do
19
+ let(:watched_directory) { File.dirname(__FILE__) }
20
+ let(:watched_directories) { nil }
21
+ subject { described_class.new(watched_directory) }
22
+
23
+ it 'sets the directories' do
24
+ subject.directories.should eq [watched_directory]
25
+ end
26
+
27
+ context 'with no options' do
28
+ it 'sets the option for using relative paths in the callback to false' do
29
+ subject.instance_variable_get(:@use_relative_paths).should eq false
30
+ end
31
+ end
32
+
33
+ context 'with :relative_paths => false' do
34
+ it 'sets the option for using relative paths in the callback to false' do
35
+ listener = described_class.new(watched_directory, :relative_paths => false)
36
+ listener.instance_variable_get(:@use_relative_paths).should eq false
37
+ end
38
+ end
39
+
40
+ context 'with :relative_paths => true' do
41
+ it 'sets the option for using relative paths in the callback to true' do
42
+ listener = described_class.new(watched_directory, :relative_paths => true)
43
+ listener.instance_variable_get(:@use_relative_paths).should eq true
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'listening to multiple directories' do
49
+ subject { described_class.new(watched_directories) }
50
+
51
+ it 'sets the directories' do
52
+ subject.directories.should eq watched_directories
53
+ end
54
+
55
+ context 'with no options' do
56
+ it 'sets the option for using relative paths in the callback to false' do
57
+ subject.instance_variable_get(:@use_relative_paths).should eq false
58
+ end
59
+ end
60
+
61
+ context 'with :relative_paths => false' do
62
+ it 'sets the option for using relative paths in the callback to false' do
63
+ listener = described_class.new(watched_directories, :relative_paths => false)
64
+ listener.instance_variable_get(:@use_relative_paths).should eq false
65
+ end
66
+ end
67
+
68
+ context 'with :relative_paths => true' do
69
+ it 'warns' do
70
+ Kernel.should_receive(:warn).with("[Listen warning]: #{Listen::Listener::RELATIVE_PATHS_WITH_MULTIPLE_DIRECTORIES_WARNING_MESSAGE}")
71
+ listener = described_class.new(watched_directories, :relative_paths => true)
72
+ end
73
+
74
+ it 'sets the option for using relative paths in the callback to false' do
75
+ listener = described_class.new(watched_directories, :relative_paths => true)
76
+ listener.instance_variable_get(:@use_relative_paths).should eq false
77
+ end
78
+ end
79
+ end
80
+
81
+ context 'with a directory' do
82
+ let(:watched_directory) { File.dirname(__FILE__) }
83
+ it 'converts the passed path into an absolute path - #21' do
84
+ described_class.new(File.join(watched_directory, '..')).directories.should eq [File.expand_path('..', watched_directory)]
85
+ end
86
+ end
87
+
88
+ context 'with custom options' do
89
+ let(:watched_directory) { File.dirname(__FILE__) }
90
+ let(:adapter_class) { double('adapter class') }
91
+
92
+ let(:options) do
93
+ {
94
+ :ignore => /\.ssh/, :filter => [/.*\.rb/, /.*\.md/],
95
+ :latency => 0.5, :force_polling => true, :relative_paths => true,
96
+ :force_adapter => adapter_class
97
+ }
98
+ end
99
+ subject { described_class.new(watched_directory, options) }
100
+
101
+ it 'passes the custom ignored paths to the directory record' do
102
+ subject.directories_records.each do |directory_record|
103
+ directory_record.ignoring_patterns.should include /\.ssh/
104
+ end
105
+ end
106
+
107
+ it 'passes the custom filters to the directory record' do
108
+ subject.directories_records.each do |directory_record|
109
+ directory_record.filtering_patterns.should =~ [/.*\.rb/,/.*\.md/]
110
+ end
111
+ end
112
+
113
+ it 'sets adapter_options' do
114
+ subject.instance_variable_get(:@adapter_options).should eq(:latency => 0.5, :force_polling => true, :force_adapter => adapter_class)
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#start' do
120
+ it 'selects and initializes an adapter' do
121
+ Listen::Adapter.should_receive(:select_and_initialize).with(watched_directories, {}) { adapter }
122
+ subject.start
123
+ end
124
+
125
+ it 'builds the directory record' do
126
+ subject.directories_records.each do |directory_record|
127
+ directory_record.should_receive(:build)
128
+ end
129
+ subject.start
130
+ end
131
+ end
132
+
133
+ context 'with a started listener' do
134
+ before do
135
+ subject.stub(:initialize_adapter) { adapter }
136
+ subject.start
137
+ end
138
+
139
+ describe '#unpause' do
140
+ it 'rebuilds the directory record' do
141
+ subject.directories_records.each do |directory_record|
142
+ directory_record.should_receive(:build)
143
+ end
144
+ subject.unpause
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#ignore'do
150
+ it 'delegates the work to the directory record' do
151
+ subject.directories_records.each do |directory_record|
152
+ directory_record.should_receive(:ignore).with 'some_directory'
153
+ end
154
+ subject.ignore 'some_directory'
155
+ end
156
+ end
157
+
158
+ describe '#ignore!'do
159
+ it 'delegates the work to the directory record' do
160
+ subject.directories_records.each do |directory_record|
161
+ directory_record.should_receive(:ignore!).with 'some_directory'
162
+ end
163
+ subject.ignore! 'some_directory'
164
+ end
165
+ end
166
+
167
+ describe '#filter' do
168
+ it 'delegates the work to the directory record' do
169
+ subject.directories_records.each do |directory_record|
170
+ directory_record.should_receive(:filter).with /\.txt$/
171
+ end
172
+ subject.filter /\.txt$/
173
+ end
174
+ end
175
+
176
+ describe '#filter!' do
177
+ it 'delegates the work to the directory record' do
178
+ subject.directories_records.each do |directory_record|
179
+ directory_record.should_receive(:filter!).with /\.txt$/
180
+ end
181
+ subject.filter! /\.txt$/
182
+ end
183
+ end
184
+
185
+ describe '#on_change' do
186
+ let(:directories) { %w{dir1 dir2 dir3} }
187
+ let(:changes) { {:modified => [], :added => [], :removed => []} }
188
+ let(:callback) { Proc.new { @called = true } }
189
+
190
+ before do
191
+ @called = false
192
+ subject.stub(:fetch_records_changes => changes)
193
+ end
194
+
195
+ it 'fetches the changes of all directories records' do
196
+ subject.unstub(:fetch_records_changes)
197
+
198
+ subject.directories_records.each do |record|
199
+ record.should_receive(:fetch_changes).with(directories, an_instance_of(Hash)).and_return(changes)
200
+ end
201
+ subject.on_change(directories)
202
+ end
203
+
204
+ context "with a callback raising an exception" do
205
+ let(:callback) { Proc.new { raise 'foo' } }
206
+
207
+ before do
208
+ subject.change(&callback)
209
+ subject.stub(:fetch_records_changes => { :modified => ['foo'], :added => [], :removed => [] } )
210
+ end
211
+
212
+ it "stops the adapter and warns" do
213
+ Kernel.should_receive(:warn).with("[Listen warning]: Change block raise an execption: foo")
214
+ Kernel.should_receive(:warn).with(/^Backtrace:.*/)
215
+ subject.on_change(directories)
216
+ end
217
+
218
+ end
219
+
220
+ context 'with no changes to report' do
221
+ if RUBY_VERSION[/^1.8/]
222
+ it 'does not run the callback' do
223
+ subject.change(&callback)
224
+ subject.on_change(directories)
225
+ @called.should be_false
226
+ end
227
+ else
228
+ it 'does not run the callback' do
229
+ callback.should_not_receive(:call)
230
+ subject.change(&callback)
231
+ subject.on_change(directories)
232
+ end
233
+ end
234
+ end
235
+
236
+ context 'with changes to report' do
237
+ let(:changes) do
238
+ {
239
+ :modified => %w{path1}, :added => [], :removed => %w{path2}
240
+ }
241
+ end
242
+
243
+ if RUBY_VERSION[/^1.8/]
244
+ it 'runs the callback passing it the changes' do
245
+ subject.change(&callback)
246
+ subject.on_change(directories)
247
+ @called.should be_true
248
+ end
249
+ else
250
+ it 'runs the callback passing it the changes' do
251
+ callback.should_receive(:call).with(changes[:modified], changes[:added], changes[:removed])
252
+ subject.change(&callback)
253
+ subject.on_change(directories)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ def run_in_two_threads(proc1, proc2)
4
+ t1 = Thread.new &proc1
5
+ sleep test_latency # t1 must run before t2
6
+ t2 = Thread.new { proc2.call; Thread.kill t1 }
7
+ t2.join(test_latency * 2)
8
+ ensure
9
+ Thread.kill t1 if t1
10
+ Thread.kill t2 if t2
11
+ end
12
+
13
+ describe Listen::Turnstile do
14
+ before { @called = false }
15
+
16
+ describe '#wait' do
17
+ context 'without a signal' do
18
+ it 'blocks one thread indefinitely' do
19
+ run_in_two_threads lambda {
20
+ subject.wait
21
+ @called = true
22
+ }, lambda {
23
+ sleep test_latency
24
+ }
25
+ @called.should be_false
26
+ end
27
+ end
28
+
29
+ context 'with a signal' do
30
+ it 'blocks one thread until it recieves a signal from another thread' do
31
+ run_in_two_threads lambda {
32
+ subject.wait
33
+ @called = true
34
+ }, lambda {
35
+ subject.signal
36
+ sleep test_latency
37
+ }
38
+ @called.should be_true
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#signal' do
44
+ context 'without a wait-call before' do
45
+ it 'does nothing' do
46
+ run_in_two_threads lambda {
47
+ subject.signal
48
+ @called = true
49
+ }, lambda {
50
+ sleep test_latency
51
+ }
52
+ @called.should be_true
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Listen do
4
+ describe '#to' do
5
+ let(:listener) { double(Listen::Listener) }
6
+ let(:listener_class) { Listen::Listener }
7
+ before { listener_class.stub(:new => listener) }
8
+
9
+ context 'with one path to listen to' do
10
+ context 'without options' do
11
+ it 'creates an instance of Listener' do
12
+ listener_class.should_receive(:new).with('/path')
13
+ described_class.to('/path')
14
+ end
15
+ end
16
+
17
+ context 'with options' do
18
+ it 'creates an instance of Listener with the passed params' do
19
+ listener_class.should_receive(:new).with('/path', :filter => '**/*')
20
+ described_class.to('/path', :filter => '**/*')
21
+ end
22
+ end
23
+
24
+ context 'without a block' do
25
+ it 'returns the listener' do
26
+ described_class.to('/path', :filter => '**/*').should eq listener
27
+ end
28
+ end
29
+
30
+ context 'with a block' do
31
+ it 'starts the listener after creating it' do
32
+ listener.should_receive(:start)
33
+ described_class.to('/path', :filter => '**/*') { |modified, added, removed| }
34
+ end
35
+ end
36
+ end
37
+
38
+ context 'with multiple paths to listen to' do
39
+ context 'without options' do
40
+ it 'creates an instance of Listener' do
41
+ listener_class.should_receive(:new).with('path1', 'path2')
42
+ described_class.to('path1', 'path2')
43
+ end
44
+ end
45
+
46
+ context 'with options' do
47
+ it 'creates an instance of Listener with the passed params' do
48
+ listener_class.should_receive(:new).with('path1', 'path2', :filter => '**/*')
49
+ described_class.to('path1', 'path2', :filter => '**/*')
50
+ end
51
+ end
52
+
53
+ context 'without a block' do
54
+ it 'returns a Listener instance created with the passed params' do
55
+ described_class.to('path1', 'path2', :filter => '**/*').should eq listener
56
+ end
57
+ end
58
+
59
+ context 'with a block' do
60
+ it 'starts a Listener instance after creating it with the passed params' do
61
+ listener.should_receive(:start)
62
+ described_class.to('path1', 'path2', :filter => '**/*') { |modified, added, removed| }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+
5
+ require 'listen'
6
+
7
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
8
+
9
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
10
+ RSpec.configure do |config|
11
+ config.color_enabled = true
12
+ config.order = :random
13
+ config.filter_run :focus => true
14
+ config.treat_symbols_as_metadata_keys_with_true_values = true
15
+ config.run_all_when_everything_filtered = true
16
+ config.filter_run_excluding :broken => true
17
+ config.fail_fast = true
18
+ end
19
+
20
+ def test_latency
21
+ 0.1
22
+ end
23
+
24
+ # Crash loud in tests!
25
+ Thread.abort_on_exception = true
@@ -0,0 +1,666 @@
1
+ # Adapter watch
2
+ #
3
+ # @param [Listen::Listener] listener the adapter listener
4
+ # @param [String] path the path to watch
5
+ #
6
+ def watch(listener, expected_changes, *paths)
7
+ sleep 0.05 # allow file/creation to be done (!)
8
+
9
+ callback = lambda do |changed_directories, options|
10
+ @called = true
11
+ listener.on_change(changed_directories)
12
+ end
13
+ @adapter = Listen::Adapter.select_and_initialize(paths, { :latency => test_latency }, &callback)
14
+ @adapter.stub(:start_poller) { nil }
15
+
16
+ forced_stop = false
17
+ prevent_deadlock = lambda do
18
+ sleep(10)
19
+ puts 'Forcing stop'
20
+ @adapter.stop
21
+ forced_stop = true
22
+ end
23
+
24
+ @adapter.start
25
+
26
+ yield
27
+
28
+ t = Thread.new(&prevent_deadlock)
29
+ @adapter.wait_for_changes(expected_changes)
30
+
31
+ unless forced_stop
32
+ Thread.kill(t)
33
+ @adapter.report_changes
34
+ end
35
+ ensure
36
+ unless forced_stop
37
+ Thread.kill(t) if t
38
+ @adapter.stop
39
+ end
40
+ end
41
+
42
+ shared_examples_for 'a filesystem adapter' do
43
+ subject { described_class.new(File.dirname(__FILE__), &Proc.new {}) }
44
+
45
+ describe '#start' do
46
+ before { Kernel.stub(:warn) }
47
+ after { subject.stop }
48
+
49
+ it 'do not block the current thread after starting the workers' do
50
+ @called = false
51
+ t = Thread.new { subject.start; @called = true }
52
+ sleep(test_latency * 3)
53
+ Thread.kill(t) if t
54
+ @called.should be_true
55
+ end
56
+
57
+ context 'with the blocking hash option set to false' do
58
+ subject { described_class.new(File.dirname(__FILE__), { :blocking => false }, &Proc.new {}) }
59
+
60
+ it 'does not block the current thread after starting the workers' do
61
+ @called = false
62
+ t = Thread.new { subject.start; @called = true }
63
+ sleep(test_latency * 3)
64
+ Thread.kill(t) if t
65
+ @called.should be_true
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#start!' do
71
+ before { Kernel.stub(:warn) }
72
+ after { subject.stop }
73
+
74
+ it 'blocks the current thread after starting the workers' do
75
+ @called = false
76
+ t = Thread.new { subject.start!; @called = true }
77
+ sleep(test_latency * 3)
78
+ Thread.kill(t) if t
79
+ @called.should be_false
80
+ end
81
+
82
+ context 'with the blocking hash option set to false' do
83
+ subject { described_class.new(File.dirname(__FILE__), { :blocking => true }, &Proc.new {}) }
84
+
85
+ it 'blocks the current thread after starting the workers' do
86
+ @called = false
87
+ t = Thread.new { subject.start!; @called = true }
88
+ sleep(test_latency * 3)
89
+ Thread.kill(t) if t
90
+ @called.should be_false
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#started?' do
96
+ context 'with a new adapter' do
97
+ it 'returns false' do
98
+ subject.should_not be_started
99
+ end
100
+ end
101
+
102
+ context 'with a stopped adapter' do
103
+ before { subject.start; subject.stop }
104
+
105
+ it 'returns false' do
106
+ subject.should_not be_started
107
+ end
108
+ end
109
+
110
+ context 'with a started adapter' do
111
+ before { subject.start }
112
+ after { subject.stop }
113
+
114
+ it 'returns true' do
115
+ subject.should be_started
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ shared_examples_for 'an adapter that call properly listener#on_change' do |*args|
122
+ options = (args.first && args.first.is_a?(Hash)) ? args.first : {}
123
+ let(:listener) { double(Listen::Listener) }
124
+ before { described_class.stub(:works?) { true } }
125
+
126
+ context 'single file operations' do
127
+ context 'when a file is created' do
128
+ it 'detects the added file' do
129
+ fixtures do |path|
130
+ listener.should_receive(:on_change).once.with do |array|
131
+ array.should include(path)
132
+ end
133
+
134
+ watch(listener, 1, path) do
135
+ touch 'new_file.rb'
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'given a symlink', :unless => windows? do
141
+ it 'detects the added file' do
142
+ fixtures do |path|
143
+ listener.should_receive(:on_change).once.with do |array|
144
+ array.should include(path)
145
+ end
146
+
147
+ touch 'new_file.rb'
148
+
149
+ watch(listener, 1, path) do
150
+ ln_s 'new_file.rb', 'new_file_symlink.rb'
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'given a new created directory' do
157
+ it 'detects the added file' do
158
+ fixtures do |path|
159
+ listener.should_receive(:on_change).once.with do |array|
160
+ array.should include(path, "#{path}/a_directory")
161
+ end
162
+
163
+ watch(listener, 2, path) do
164
+ mkdir 'a_directory'
165
+ # Needed for INotify, because of :recursive rb-inotify custom flag?
166
+ sleep 0.05
167
+ touch 'a_directory/new_file.rb'
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'given an existing directory' do
174
+ it 'detects the added file' do
175
+ fixtures do |path|
176
+ listener.should_receive(:on_change).once.with do |array|
177
+ array.should include("#{path}/a_directory")
178
+ end
179
+
180
+ mkdir 'a_directory'
181
+
182
+ watch(listener, 1, path) do
183
+ touch 'a_directory/new_file.rb'
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ context 'given a directory with subdirectories' do
190
+ it 'detects the added file' do
191
+ fixtures do |path|
192
+ listener.should_receive(:on_change).once.with do |array|
193
+ array.should include("#{path}/a_directory/subdirectory")
194
+ end
195
+
196
+ mkdir_p 'a_directory/subdirectory'
197
+
198
+ watch(listener, 1, path) do
199
+ touch 'a_directory/subdirectory/new_file.rb'
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ context 'when a file is modified' do
207
+ it 'detects the modified file' do
208
+ fixtures do |path|
209
+ listener.should_receive(:on_change).once.with do |array|
210
+ array.should include(path)
211
+ end
212
+
213
+ touch 'existing_file.txt'
214
+
215
+ watch(listener, 1, path) do
216
+ touch 'existing_file.txt'
217
+ end
218
+ end
219
+ end
220
+
221
+ context 'given a symlink', :unless => windows? do
222
+ it 'detects the modified file' do
223
+ fixtures do |path|
224
+ listener.should_receive(:on_change).once.with do |array|
225
+ array.should include(path)
226
+ end
227
+
228
+ touch 'existing_file.rb'
229
+ ln_s 'existing_file.rb', 'existing_file_symlink.rb'
230
+
231
+ watch(listener, 1, path) do
232
+ touch 'existing_file.rb'
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ context 'given a hidden file' do
239
+ it 'detects the modified file' do
240
+ fixtures do |path|
241
+ listener.should_receive(:on_change).once.with do |array|
242
+ array.should include(path)
243
+ end
244
+
245
+ touch '.hidden'
246
+
247
+ watch(listener, 1, path) do
248
+ touch '.hidden'
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ context 'given a file mode change', :unless => windows? do
255
+ it 'does not detect the mode change' do
256
+ fixtures do |path|
257
+ listener.should_receive(:on_change).once.with do |array|
258
+ array.should include(path)
259
+ end
260
+
261
+ touch 'run.rb'
262
+
263
+ watch(listener, 1, path) do
264
+ chmod 0777, 'run.rb'
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ context 'given an existing directory' do
271
+ it 'detects the modified file' do
272
+ fixtures do |path|
273
+ listener.should_receive(:on_change).once.with do |array|
274
+ array.should include("#{path}/a_directory")
275
+ end
276
+
277
+ mkdir 'a_directory'
278
+ touch 'a_directory/existing_file.txt'
279
+
280
+ watch(listener, 1, path) do
281
+ touch 'a_directory/existing_file.txt'
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ context 'given a directory with subdirectories' do
288
+ it 'detects the modified file' do
289
+ fixtures do |path|
290
+ listener.should_receive(:on_change).once.with do |array|
291
+ array.should include("#{path}/a_directory/subdirectory")
292
+ end
293
+
294
+ mkdir_p 'a_directory/subdirectory'
295
+ touch 'a_directory/subdirectory/existing_file.txt'
296
+
297
+ watch(listener, 1, path) do
298
+ touch 'a_directory/subdirectory/new_file.rb'
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ context 'when a file is moved' do
306
+ it 'detects the file move' do
307
+ fixtures do |path|
308
+ listener.should_receive(:on_change).once.with do |array|
309
+ array.should include(path)
310
+ end
311
+
312
+ touch 'move_me.txt'
313
+
314
+ watch(listener, 1, path) do
315
+ mv 'move_me.txt', 'new_name.txt'
316
+ end
317
+ end
318
+ end
319
+
320
+ context 'given a symlink', :unless => windows? do
321
+ it 'detects the file move' do
322
+ fixtures do |path|
323
+ listener.should_receive(:on_change).once.with do |array|
324
+ array.should include(path)
325
+ end
326
+
327
+ touch 'move_me.rb'
328
+ ln_s 'move_me.rb', 'move_me_symlink.rb'
329
+
330
+ watch(listener, 1, path) do
331
+ mv 'move_me_symlink.rb', 'new_symlink.rb'
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ context 'given an existing directory' do
338
+ it 'detects the file move into the directory' do
339
+ fixtures do |path|
340
+ listener.should_receive(:on_change).once.with do |array|
341
+ array.should include(path, "#{path}/a_directory")
342
+ end
343
+
344
+ mkdir 'a_directory'
345
+ touch 'move_me.txt'
346
+
347
+ watch(listener, 2, path) do
348
+ mv 'move_me.txt', 'a_directory/move_me.txt'
349
+ end
350
+ end
351
+ end
352
+
353
+ it 'detects a file move out of the directory' do
354
+ fixtures do |path|
355
+ listener.should_receive(:on_change).once.with do |array|
356
+ array.should include(path, "#{path}/a_directory")
357
+ end
358
+
359
+ mkdir 'a_directory'
360
+ touch 'a_directory/move_me.txt'
361
+
362
+ watch(listener, 2, path) do
363
+ mv 'a_directory/move_me.txt', 'i_am_here.txt'
364
+ end
365
+ end
366
+ end
367
+
368
+ it 'detects a file move between two directories' do
369
+ fixtures do |path|
370
+ listener.should_receive(:on_change).once.with do |array|
371
+ array.should include("#{path}/from_directory", "#{path}/to_directory")
372
+ end
373
+
374
+ mkdir 'from_directory'
375
+ touch 'from_directory/move_me.txt'
376
+ mkdir 'to_directory'
377
+
378
+ watch(listener, 2, path) do
379
+ mv 'from_directory/move_me.txt', 'to_directory/move_me.txt'
380
+ end
381
+ end
382
+ end
383
+ end
384
+
385
+ context 'given a directory with subdirectories' do
386
+ it 'detects files movements between subdirectories' do
387
+ fixtures do |path|
388
+ listener.should_receive(:on_change).once.with do |array|
389
+ array.should include("#{path}/a_directory/subdirectory", "#{path}/b_directory/subdirectory")
390
+ end
391
+
392
+ mkdir_p 'a_directory/subdirectory'
393
+ mkdir_p 'b_directory/subdirectory'
394
+ touch 'a_directory/subdirectory/move_me.txt'
395
+
396
+ watch(listener, 2, path) do
397
+ mv 'a_directory/subdirectory/move_me.txt', 'b_directory/subdirectory'
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
403
+
404
+ context 'when a file is deleted' do
405
+ it 'detects the file removal' do
406
+ fixtures do |path|
407
+ listener.should_receive(:on_change).once.with do |array|
408
+ array.should include(path)
409
+ end
410
+
411
+ touch 'unnecessary.txt'
412
+
413
+ watch(listener, 1, path) do
414
+ rm 'unnecessary.txt'
415
+ end
416
+ end
417
+ end
418
+
419
+ context 'given a symlink', :unless => windows? do
420
+ it 'detects the file removal' do
421
+ fixtures do |path|
422
+ listener.should_receive(:on_change).once.with do |array|
423
+ array.should include(path)
424
+ end
425
+
426
+ touch 'unnecessary.rb'
427
+ ln_s 'unnecessary.rb', 'unnecessary_symlink.rb'
428
+
429
+ watch(listener, 1, path) do
430
+ rm 'unnecessary_symlink.rb'
431
+ end
432
+ end
433
+ end
434
+ end
435
+
436
+ context 'given an existing directory' do
437
+ it 'detects the file removal' do
438
+ fixtures do |path|
439
+ listener.should_receive(:on_change).once.with do |array|
440
+ array.should include("#{path}/a_directory")
441
+ end
442
+
443
+ mkdir 'a_directory'
444
+ touch 'a_directory/do_not_use.rb'
445
+
446
+ watch(listener, 1, path) do
447
+ rm 'a_directory/do_not_use.rb'
448
+ end
449
+ end
450
+ end
451
+ end
452
+
453
+ context 'given a directory with subdirectories' do
454
+ it 'detects the file removal' do
455
+ fixtures do |path|
456
+ listener.should_receive(:on_change).once.with do |array|
457
+ array.should include("#{path}/a_directory/subdirectory")
458
+ end
459
+
460
+ mkdir_p 'a_directory/subdirectory'
461
+ touch 'a_directory/subdirectory/do_not_use.rb'
462
+
463
+ watch(listener, 1, path) do
464
+ rm 'a_directory/subdirectory/do_not_use.rb'
465
+ end
466
+ end
467
+ end
468
+ end
469
+ end
470
+ end
471
+
472
+ context 'multiple file operations' do
473
+ it 'detects the added files' do
474
+ fixtures do |path|
475
+ listener.should_receive(:on_change).once.with do |array|
476
+ array.should include(path, "#{path}/a_directory")
477
+ end
478
+
479
+ watch(listener, 2, path) do
480
+ touch 'a_file.rb'
481
+ touch 'b_file.rb'
482
+ mkdir 'a_directory'
483
+ # Needed for INotify, because of :recursive rb-inotify custom flag?
484
+ # Also needed for the osx adapter
485
+ sleep 0.05
486
+ touch 'a_directory/a_file.rb'
487
+ touch 'a_directory/b_file.rb'
488
+ end
489
+ end
490
+ end
491
+
492
+ it 'detects the modified files' do
493
+ fixtures do |path|
494
+ listener.should_receive(:on_change).once.with do |array|
495
+ array.should include(path, "#{path}/a_directory")
496
+ end
497
+
498
+ touch 'a_file.rb'
499
+ touch 'b_file.rb'
500
+ mkdir 'a_directory'
501
+ touch 'a_directory/a_file.rb'
502
+ touch 'a_directory/b_file.rb'
503
+
504
+ watch(listener, 2, path) do
505
+ touch 'b_file.rb'
506
+ touch 'a_directory/a_file.rb'
507
+ end
508
+ end
509
+ end
510
+
511
+ it 'detects the removed files' do
512
+ fixtures do |path|
513
+ listener.should_receive(:on_change).once.with do |array|
514
+ array.should include(path, "#{path}/a_directory")
515
+ end
516
+
517
+ touch 'a_file.rb'
518
+ touch 'b_file.rb'
519
+ mkdir 'a_directory'
520
+ touch 'a_directory/a_file.rb'
521
+ touch 'a_directory/b_file.rb'
522
+
523
+ watch(listener, 2, path) do
524
+ rm 'b_file.rb'
525
+ rm 'a_directory/a_file.rb'
526
+ end
527
+ end
528
+ end
529
+ end
530
+
531
+ context 'single directory operations' do
532
+ it 'detects a moved directory' do
533
+ fixtures do |path|
534
+ listener.should_receive(:on_change).once.with do |array|
535
+ array.should include(path)
536
+ end
537
+
538
+ mkdir 'a_directory'
539
+ touch 'a_directory/a_file.rb'
540
+ touch 'a_directory/b_file.rb'
541
+
542
+ watch(listener, 1, path) do
543
+ mv 'a_directory', 'renamed'
544
+ end
545
+ end
546
+ end
547
+
548
+ it 'detects a removed directory' do
549
+ fixtures do |path|
550
+ listener.should_receive(:on_change).once.with do |array|
551
+ array.should include(path, "#{path}/a_directory")
552
+ end
553
+
554
+ mkdir 'a_directory'
555
+ touch 'a_directory/a_file.rb'
556
+ touch 'a_directory/b_file.rb'
557
+
558
+ watch(listener, 2, path) do
559
+ rm_rf 'a_directory'
560
+ end
561
+ end
562
+ end
563
+ end
564
+
565
+ context "paused adapter" do
566
+ context 'when a file is created' do
567
+ it "doesn't detects the added file" do
568
+ fixtures do |path|
569
+ watch(listener, 1, path) do # The expected changes param is set to one!
570
+ @adapter.paused = true
571
+ touch 'new_file.rb'
572
+ end
573
+ @called.should be_nil
574
+ end
575
+ end
576
+ end
577
+ end
578
+
579
+ context "when multiple directories are listened to" do
580
+ context 'when files are added to one of multiple directories' do
581
+ it 'detects added files' do
582
+ fixtures(2) do |path1, path2|
583
+ listener.should_receive(:on_change).once.with do |array|
584
+ array.should include(path2)
585
+ end
586
+
587
+ watch(listener, 1, path1, path2) do
588
+ touch "#{path2}/new_file.rb"
589
+ end
590
+ end
591
+ end
592
+ end
593
+
594
+ context 'when files are added to multiple directories' do
595
+ it 'detects added files' do
596
+ fixtures(2) do |path1, path2|
597
+ listener.should_receive(:on_change).once.with do |array|
598
+ array.should include(path1, path2)
599
+ end
600
+
601
+ watch(listener, 2, path1, path2) do
602
+ touch "#{path1}/new_file.rb"
603
+ touch "#{path2}/new_file.rb"
604
+ end
605
+ end
606
+ end
607
+ end
608
+
609
+ context 'given a new and an existing directory on multiple directories' do
610
+ it 'detects the added file' do
611
+ fixtures(2) do |path1, path2|
612
+ listener.should_receive(:on_change).once.with do |array|
613
+ array.should include(path2, "#{path1}/a_directory", "#{path2}/b_directory")
614
+ end
615
+
616
+ mkdir "#{path1}/a_directory"
617
+
618
+ watch(listener, 3, path1, path2) do
619
+ mkdir "#{path2}/b_directory"
620
+ # Needed for INotify
621
+ sleep 0.05
622
+ touch "#{path1}/a_directory/new_file.rb"
623
+ touch "#{path2}/b_directory/new_file.rb"
624
+ end
625
+ end
626
+ end
627
+ end
628
+
629
+ context 'when a file is moved between the multiple watched directories' do
630
+ it 'detects the movements of the file' do
631
+ fixtures(3) do |path1, path2, path3|
632
+ listener.should_receive(:on_change).once.with do |array|
633
+ array.should include("#{path1}/from_directory", path2, "#{path3}/to_directory")
634
+ end
635
+
636
+ mkdir "#{path1}/from_directory"
637
+ touch "#{path1}/from_directory/move_me.txt"
638
+ mkdir "#{path3}/to_directory"
639
+
640
+ watch(listener, 3, path1, path2, path3) do
641
+ mv "#{path1}/from_directory/move_me.txt", "#{path2}/move_me.txt"
642
+ mv "#{path2}/move_me.txt", "#{path3}/to_directory/move_me.txt"
643
+ end
644
+ end
645
+ end
646
+ end
647
+
648
+ context 'when files are deleted from the multiple watched directories' do
649
+ it 'detects the files removal' do
650
+ fixtures(2) do |path1, path2|
651
+ listener.should_receive(:on_change).once.with do |array|
652
+ array.should include(path1, path2)
653
+ end
654
+
655
+ touch "#{path1}/unnecessary.txt"
656
+ touch "#{path2}/unnecessary.txt"
657
+
658
+ watch(listener, 2, path1, path2) do
659
+ rm "#{path1}/unnecessary.txt"
660
+ rm "#{path2}/unnecessary.txt"
661
+ end
662
+ end
663
+ end
664
+ end
665
+ end
666
+ end