logstash-input-file 4.1.3 → 4.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/JAR_VERSION +1 -1
  4. data/README.md +0 -3
  5. data/docs/index.asciidoc +26 -16
  6. data/lib/filewatch/bootstrap.rb +10 -21
  7. data/lib/filewatch/discoverer.rb +35 -28
  8. data/lib/filewatch/observing_base.rb +2 -1
  9. data/lib/filewatch/read_mode/handlers/base.rb +19 -6
  10. data/lib/filewatch/read_mode/handlers/read_file.rb +43 -32
  11. data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -3
  12. data/lib/filewatch/read_mode/processor.rb +8 -8
  13. data/lib/filewatch/settings.rb +3 -3
  14. data/lib/filewatch/sincedb_collection.rb +56 -42
  15. data/lib/filewatch/sincedb_value.rb +6 -0
  16. data/lib/filewatch/stat/generic.rb +34 -0
  17. data/lib/filewatch/stat/windows_path.rb +32 -0
  18. data/lib/filewatch/tail_mode/handlers/base.rb +40 -22
  19. data/lib/filewatch/tail_mode/handlers/create.rb +1 -2
  20. data/lib/filewatch/tail_mode/handlers/create_initial.rb +2 -1
  21. data/lib/filewatch/tail_mode/handlers/delete.rb +13 -1
  22. data/lib/filewatch/tail_mode/handlers/grow.rb +5 -2
  23. data/lib/filewatch/tail_mode/handlers/shrink.rb +7 -4
  24. data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -2
  25. data/lib/filewatch/tail_mode/processor.rb +147 -58
  26. data/lib/filewatch/watch.rb +15 -35
  27. data/lib/filewatch/watched_file.rb +237 -41
  28. data/lib/filewatch/watched_files_collection.rb +2 -2
  29. data/lib/filewatch/winhelper.rb +167 -25
  30. data/lib/jars/filewatch-1.0.1.jar +0 -0
  31. data/lib/logstash/inputs/file.rb +9 -2
  32. data/logstash-input-file.gemspec +9 -2
  33. data/spec/file_ext/file_ext_windows_spec.rb +36 -0
  34. data/spec/filewatch/read_mode_handlers_read_file_spec.rb +2 -2
  35. data/spec/filewatch/reading_spec.rb +100 -57
  36. data/spec/filewatch/rotate_spec.rb +451 -0
  37. data/spec/filewatch/spec_helper.rb +33 -10
  38. data/spec/filewatch/tailing_spec.rb +273 -153
  39. data/spec/filewatch/watched_file_spec.rb +3 -3
  40. data/spec/filewatch/watched_files_collection_spec.rb +3 -3
  41. data/spec/filewatch/winhelper_spec.rb +4 -5
  42. data/spec/helpers/logging_level_helper.rb +8 -0
  43. data/spec/helpers/rspec_wait_handler_helper.rb +38 -0
  44. data/spec/helpers/spec_helper.rb +7 -1
  45. data/spec/inputs/file_read_spec.rb +54 -24
  46. data/spec/inputs/file_tail_spec.rb +244 -284
  47. metadata +13 -3
  48. data/lib/jars/filewatch-1.0.0.jar +0 -0
@@ -1,7 +1,8 @@
1
1
  # encoding: utf-8
2
2
  require "rspec_sequencing"
3
- require 'rspec/wait'
3
+ # require 'rspec/wait'
4
4
  require "logstash/devutils/rspec/spec_helper"
5
+ require "concurrent"
5
6
  require "timecop"
6
7
 
7
8
  def formatted_puts(text)
@@ -24,9 +25,32 @@ unless RSpec::Matchers.method_defined?(:receive_call_and_args)
24
25
  end
25
26
  end
26
27
 
28
+ require_relative "../helpers/rspec_wait_handler_helper" unless defined? RSPEC_WAIT_HANDLER_PATCHED
29
+ require_relative "../helpers/logging_level_helper" unless defined? LOG_AT_HANDLED
30
+
27
31
  require 'filewatch/bootstrap'
28
32
 
29
33
  module FileWatch
34
+ class DummyIO
35
+ def stat
36
+ self
37
+ end
38
+ def ino
39
+ 23456
40
+ end
41
+ def size
42
+ 65535
43
+ end
44
+ def mtime
45
+ Time.now
46
+ end
47
+ def dev_major
48
+ 1
49
+ end
50
+ def dev_minor
51
+ 5
52
+ end
53
+ end
30
54
 
31
55
  class DummyFileReader
32
56
  def initialize(read_size, iterations)
@@ -34,6 +58,7 @@ module FileWatch
34
58
  @iterations = iterations
35
59
  @closed = false
36
60
  @accumulated = 0
61
+ @io = DummyIO.new
37
62
  end
38
63
  def file_seek(*)
39
64
  end
@@ -43,6 +68,9 @@ module FileWatch
43
68
  def closed?
44
69
  @closed
45
70
  end
71
+ def to_io
72
+ @io
73
+ end
46
74
  def sysread(amount)
47
75
  @accumulated += amount
48
76
  if @accumulated > @read_size * @iterations
@@ -67,7 +95,7 @@ module FileWatch
67
95
 
68
96
  class TracerBase
69
97
  def initialize
70
- @tracer = []
98
+ @tracer = Concurrent::Array.new
71
99
  end
72
100
 
73
101
  def trace_for(symbol)
@@ -91,8 +119,8 @@ module FileWatch
91
119
 
92
120
  def initialize(path)
93
121
  @path = path
94
- @lines = []
95
- @calls = []
122
+ @lines = Concurrent::Array.new
123
+ @calls = Concurrent::Array.new
96
124
  end
97
125
 
98
126
  def add_lines(lines)
@@ -134,7 +162,7 @@ module FileWatch
134
162
  else
135
163
  lambda{|k| Listener.new(k).add_lines(combined_lines) }
136
164
  end
137
- @listeners = Hash.new {|hash, key| hash[key] = listener_proc.call(key) }
165
+ @listeners = Concurrent::Hash.new {|hash, key| hash[key] = listener_proc.call(key) }
138
166
  end
139
167
 
140
168
  def listener_for(path)
@@ -145,8 +173,3 @@ module FileWatch
145
173
  @listeners.clear; end
146
174
  end
147
175
  end
148
-
149
- ENV["LOG_AT"].tap do |level|
150
- LogStash::Logging::Logger::configure_logging(level) unless level.nil?
151
- end
152
-
@@ -5,42 +5,45 @@ require 'filewatch/observing_tail'
5
5
 
6
6
  module FileWatch
7
7
  describe Watch do
8
- before(:all) do
9
- @thread_abort = Thread.abort_on_exception
10
- Thread.abort_on_exception = true
11
- end
12
-
13
- after(:all) do
14
- Thread.abort_on_exception = @thread_abort
15
- end
16
-
17
8
  let(:directory) { Stud::Temporary.directory }
18
- let(:watch_dir) { ::File.join(directory, "*.log") }
19
- let(:file_path) { ::File.join(directory, "1.log") }
9
+ let(:watch_dir) { ::File.join(directory, "*#{suffix}.log") }
10
+ let(:file_path) { ::File.join(directory, "1#{suffix}.log") }
11
+ let(:file_path2) { ::File.join(directory, "2#{suffix}.log") }
12
+ let(:file_path3) { ::File.join(directory, "3#{suffix}.log") }
20
13
  let(:max) { 4095 }
21
14
  let(:stat_interval) { 0.1 }
22
15
  let(:discover_interval) { 4 }
23
- let(:start_new_files_at) { :beginning }
16
+ let(:start_new_files_at) { :end }
24
17
  let(:sincedb_path) { ::File.join(directory, "tailing.sdb") }
25
18
  let(:opts) do
26
19
  {
27
- :stat_interval => stat_interval, :start_new_files_at => start_new_files_at, :max_active => max,
28
- :delimiter => "\n", :discover_interval => discover_interval, :sincedb_path => sincedb_path
20
+ :stat_interval => stat_interval, :start_new_files_at => start_new_files_at, :max_open_files => max,
21
+ :delimiter => "\n", :discover_interval => discover_interval, :sincedb_path => sincedb_path,
22
+ :file_sort_by => "path"
29
23
  }
30
24
  end
31
25
  let(:observer) { TestObserver.new }
26
+ let(:listener1) { observer.listener_for(file_path) }
27
+ let(:listener2) { observer.listener_for(file_path2) }
28
+ let(:listener3) { observer.listener_for(file_path3) }
32
29
  let(:tailing) { ObservingTail.new(opts) }
33
30
 
31
+ before do
32
+ directory
33
+ wait(1.0).for{Dir.exist?(directory)}.to eq(true)
34
+ end
35
+
34
36
  after do
35
37
  FileUtils.rm_rf(directory)
38
+ wait(1.0).for{Dir.exist?(directory)}.to eq(false)
36
39
  end
37
40
 
38
41
  describe "max open files (set to 1)" do
39
42
  let(:max) { 1 }
40
- let(:file_path2) { File.join(directory, "2.log") }
41
43
  let(:wait_before_quit) { 0.15 }
42
44
  let(:stat_interval) { 0.01 }
43
45
  let(:discover_interval) { 4 }
46
+ let(:start_new_files_at) { :beginning }
44
47
  let(:actions) do
45
48
  RSpec::Sequencing
46
49
  .run_after(wait_before_quit, "quit after a short time") do
@@ -51,94 +54,110 @@ module FileWatch
51
54
  before do
52
55
  ENV["FILEWATCH_MAX_FILES_WARN_INTERVAL"] = "0"
53
56
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
54
- File.open(file_path2, "wb") { |file| file.write("lineA\nlineB\n") }
57
+ File.open(file_path2, "wb") { |file| file.write("line-A\nline-B\n") }
55
58
  end
56
59
 
57
60
  context "when max_active is 1" do
61
+ let(:suffix) { "A" }
58
62
  it "without close_older set, opens only 1 file" do
59
- actions.activate
63
+ actions.activate_quietly
64
+ # create files before first discovery, they will be read from the end
60
65
  tailing.watch_this(watch_dir)
61
66
  tailing.subscribe(observer)
67
+ actions.assert_no_errors
62
68
  expect(tailing.settings.max_active).to eq(max)
63
- file1_calls = observer.listener_for(file_path).calls
64
- file2_calls = observer.listener_for(file_path2).calls
65
- # file glob order is OS dependent
66
- if file1_calls.empty?
67
- expect(observer.listener_for(file_path2).lines).to eq(["lineA", "lineB"])
68
- expect(file2_calls).to eq([:open, :accept, :accept])
69
- else
70
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
71
- expect(file1_calls).to eq([:open, :accept, :accept])
72
- expect(file2_calls).to be_empty
73
- end
69
+ expect(listener1.lines).to eq(["line1", "line2"])
70
+ expect(listener1.calls).to eq([:open, :accept, :accept])
71
+ expect(listener2.calls).to be_empty
74
72
  end
75
73
  end
76
74
 
77
75
  context "when close_older is set" do
78
76
  let(:wait_before_quit) { 0.8 }
79
- let(:opts) { super.merge(:close_older => 0.15, :max_active => 1, :stat_interval => 0.1) }
77
+ let(:opts) { super.merge(:close_older => 0.1, :max_open_files => 1, :stat_interval => 0.1) }
78
+ let(:suffix) { "B" }
80
79
  it "opens both files" do
81
- actions.activate
80
+ actions.activate_quietly
82
81
  tailing.watch_this(watch_dir)
83
82
  tailing.subscribe(observer)
83
+ actions.assert_no_errors
84
84
  expect(tailing.settings.max_active).to eq(1)
85
- filelistener_1 = observer.listener_for(file_path)
86
- filelistener_2 = observer.listener_for(file_path2)
87
- expect(filelistener_2.calls).to eq([:open, :accept, :accept, :timed_out])
88
- expect(filelistener_2.lines).to eq(["lineA", "lineB"])
89
- expect(filelistener_1.calls).to eq([:open, :accept, :accept, :timed_out])
90
- expect(filelistener_1.lines).to eq(["line1", "line2"])
85
+ expect(listener2.calls).to eq([:open, :accept, :accept, :timed_out])
86
+ expect(listener2.lines).to eq(["line-A", "line-B"])
87
+ expect(listener1.calls).to eq([:open, :accept, :accept, :timed_out])
88
+ expect(listener1.lines).to eq(["line1", "line2"])
91
89
  end
92
90
  end
93
91
  end
94
92
 
95
- context "when watching a directory with files" do
96
- let(:start_new_files_at) { :beginning }
93
+ context "when watching a directory with files, exisiting content is skipped" do
94
+ let(:suffix) { "C" }
97
95
  let(:actions) do
98
- RSpec::Sequencing.run_after(0.45, "quit after a short time") do
99
- tailing.quit
100
- end
96
+ RSpec::Sequencing
97
+ .run("create file") do
98
+ File.open(file_path, "wb") { |file| file.write("lineA\nlineB\n") }
99
+ end
100
+ .then_after(0.1, "begin watching") do
101
+ tailing.watch_this(watch_dir)
102
+ end
103
+ .then_after(0.2, "add content") do
104
+ File.open(file_path, "ab") { |file| file.write("line1\nline2\n") }
105
+ end
106
+ .then("wait") do
107
+ wait(0.75).for{listener1.lines.size}.to eq(2)
108
+ end
109
+ .then("quit") do
110
+ tailing.quit
111
+ end
101
112
  end
102
113
 
103
- it "the file is read" do
104
- File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
105
- actions.activate
114
+ it "only the new content is read" do
115
+ actions.activate_quietly
106
116
  tailing.watch_this(watch_dir)
107
117
  tailing.subscribe(observer)
108
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept])
109
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
118
+ actions.assert_no_errors
119
+ expect(listener1.calls).to eq([:open, :accept, :accept])
120
+ expect(listener1.lines).to eq(["line1", "line2"])
110
121
  end
111
122
  end
112
123
 
113
124
  context "when watching a directory without files and one is added" do
114
- let(:start_new_files_at) { :beginning }
115
- before do
116
- tailing.watch_this(watch_dir)
125
+ let(:suffix) { "D" }
126
+ let(:actions) do
117
127
  RSpec::Sequencing
118
- .run_after(0.25, "create file") do
128
+ .run("begin watching") do
129
+ tailing.watch_this(watch_dir)
130
+ end
131
+ .then_after(0.1, "create file") do
119
132
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
120
133
  end
121
- .then_after(0.45, "quit after a short time") do
134
+ .then("wait") do
135
+ wait(0.75).for{listener1.lines.size}.to eq(2)
136
+ end
137
+ .then("quit") do
122
138
  tailing.quit
123
139
  end
124
140
  end
125
141
 
126
- it "the file is read" do
142
+ it "the file is read from the beginning" do
143
+ actions.activate_quietly
127
144
  tailing.subscribe(observer)
128
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept])
129
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
145
+ actions.assert_no_errors
146
+ expect(listener1.calls).to eq([:open, :accept, :accept])
147
+ expect(listener1.lines).to eq(["line1", "line2"])
130
148
  end
131
149
  end
132
150
 
133
- describe "given a previously discovered file" do
151
+ context "given a previously discovered file" do
134
152
  # these tests rely on the fact that the 'filepath' does not exist on disk
135
153
  # it simulates that the user deleted the file
136
154
  # so when a stat is taken on the file an error is raised
155
+ let(:suffix) { "E" }
137
156
  let(:quit_after) { 0.2 }
138
- let(:stat) { double("stat", :size => 100, :ctime => Time.now, :mtime => Time.now, :ino => 234567, :dev_major => 3, :dev_minor => 2) }
157
+ let(:stat) { double("stat", :size => 100, :modified_at => Time.now.to_f, :identifier => nil, :inode => 234567, :inode_struct => InodeStruct.new("234567", 1, 5)) }
139
158
  let(:watched_file) { WatchedFile.new(file_path, stat, tailing.settings) }
140
-
141
159
  before do
160
+ allow(stat).to receive(:restat).and_raise(Errno::ENOENT)
142
161
  tailing.watch.watched_files_collection.add(watched_file)
143
162
  watched_file.initial_completed
144
163
  end
@@ -150,7 +169,7 @@ module FileWatch
150
169
  RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
151
170
  tailing.subscribe(observer)
152
171
  expect(tailing.watch.watched_files_collection).to be_empty
153
- expect(observer.listener_for(file_path).calls).to eq([:delete])
172
+ expect(listener1.calls).to eq([:delete])
154
173
  end
155
174
  end
156
175
 
@@ -160,7 +179,7 @@ module FileWatch
160
179
  RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
161
180
  tailing.subscribe(observer)
162
181
  expect(tailing.watch.watched_files_collection).to be_empty
163
- expect(observer.listener_for(file_path).calls).to eq([:delete])
182
+ expect(listener1.calls).to eq([:delete])
164
183
  end
165
184
  end
166
185
 
@@ -170,7 +189,7 @@ module FileWatch
170
189
  RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
171
190
  tailing.subscribe(observer)
172
191
  expect(tailing.watch.watched_files_collection).to be_empty
173
- expect(observer.listener_for(file_path).calls).to eq([:delete])
192
+ expect(listener1.calls).to eq([:delete])
174
193
  end
175
194
  end
176
195
 
@@ -180,172 +199,214 @@ module FileWatch
180
199
  RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
181
200
  tailing.subscribe(observer)
182
201
  expect(tailing.watch.watched_files_collection).to be_empty
183
- expect(observer.listener_for(file_path).calls).to eq([:delete])
202
+ expect(listener1.calls).to eq([:delete])
184
203
  end
185
204
  end
186
205
  end
187
206
 
188
207
  context "when a processed file shrinks" do
189
- let(:discover_interval) { 100 }
190
- before do
208
+ let(:discover_interval) { 1 }
209
+ let(:suffix) { "F" }
210
+ let(:actions) do
191
211
  RSpec::Sequencing
192
- .run("create file") do
212
+ .run_after(0.1, "start watching") do
213
+ tailing.watch_this(watch_dir)
214
+ end
215
+ .then_after(0.1, "create file") do
216
+ # create file after first discovery, will be read from the start
193
217
  File.open(file_path, "wb") { |file| file.write("line1\nline2\nline3\nline4\n") }
194
218
  end
195
- .then_after(0.25, "start watching after files are written") do
196
- tailing.watch_this(watch_dir)
219
+ .then("wait for initial lines to be read") do
220
+ wait(0.8).for{listener1.lines.size}.to eq(4), "listener1.lines.size not eq 4"
197
221
  end
198
222
  .then_after(0.25, "truncate file and write new content") do
199
223
  File.truncate(file_path, 0)
200
- File.open(file_path, "wb") { |file| file.write("lineA\nlineB\n") }
224
+ File.open(file_path, "ab") { |file| file.write("lineA\nlineB\n") }
225
+ wait(0.5).for{listener1.lines.size}.to eq(6), "listener1.lines.size not eq 6"
201
226
  end
202
- .then_after(0.25, "quit after a short time") do
227
+ .then("quit") do
203
228
  tailing.quit
204
229
  end
205
230
  end
206
231
 
207
232
  it "new changes to the shrunk file are read from the beginning" do
233
+ actions.activate_quietly
208
234
  tailing.subscribe(observer)
209
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :accept, :accept, :accept, :accept])
210
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2", "line3", "line4", "lineA", "lineB"])
235
+ actions.assert_no_errors
236
+ expect(listener1.calls).to eq([:open, :accept, :accept, :accept, :accept, :accept, :accept])
237
+ expect(listener1.lines).to eq(["line1", "line2", "line3", "line4", "lineA", "lineB"])
211
238
  end
212
239
  end
213
240
 
214
- context "when watching a directory with files and a file is renamed to not match glob" do
241
+ context "when watching a directory with files and a file is renamed to not match glob", :unix => true do
242
+ let(:suffix) { "G" }
215
243
  let(:new_file_path) { file_path + ".old" }
216
- before do
244
+ let(:new_file_listener) { observer.listener_for(new_file_path) }
245
+ let(:actions) do
217
246
  RSpec::Sequencing
218
- .run("create file") do
219
- File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
220
- end
221
- .then_after(0.25, "start watching after files are written") do
247
+ .run("start watching") do
222
248
  tailing.watch_this(watch_dir)
223
249
  end
250
+ .then_after(0.1, "create file") do
251
+ # create file after first discovery, will be read from the beginning
252
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
253
+ end
224
254
  .then_after(0.55, "rename file") do
225
255
  FileUtils.mv(file_path, new_file_path)
226
256
  end
227
257
  .then_after(0.55, "then write to renamed file") do
228
258
  File.open(new_file_path, "ab") { |file| file.write("line3\nline4\n") }
259
+ wait(0.5).for{listener1.lines.size}.to eq(2), "listener1.lines.size not eq(2)"
229
260
  end
230
- .then_after(0.45, "quit after a short time") do
261
+ .then_after(0.1, "quit") do
231
262
  tailing.quit
232
263
  end
233
264
  end
234
265
 
235
266
  it "changes to the renamed file are not read" do
267
+ actions.activate_quietly
236
268
  tailing.subscribe(observer)
237
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :delete])
238
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
239
- expect(observer.listener_for(new_file_path).calls).to eq([])
240
- expect(observer.listener_for(new_file_path).lines).to eq([])
269
+ actions.assert_no_errors
270
+ expect(listener1.calls).to eq([:open, :accept, :accept, :delete])
271
+ expect(listener1.lines).to eq(["line1", "line2"])
272
+ expect(new_file_listener.calls).to eq([])
273
+ expect(new_file_listener.lines).to eq([])
241
274
  end
242
275
  end
243
276
 
244
- context "when watching a directory with files and a file is renamed to match glob" do
245
- let(:new_file_path) { file_path + "2.log" }
277
+ context "when watching a directory with files and a file is renamed to match glob", :unix => true do
278
+ let(:suffix) { "H" }
246
279
  let(:opts) { super.merge(:close_older => 0) }
247
- before do
280
+ let(:listener2) { observer.listener_for(file_path2) }
281
+ let(:actions) do
248
282
  RSpec::Sequencing
249
- .run("create file") do
283
+ .run("file created") do
284
+ # create file before first discovery, will be read from the end
250
285
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
251
286
  end
252
287
  .then_after(0.15, "start watching after files are written") do
253
288
  tailing.watch_this(watch_dir)
254
289
  end
255
- .then_after(0.25, "rename file") do
256
- FileUtils.mv(file_path, new_file_path)
290
+ .then("wait") do
291
+ wait(0.5).for{listener1.calls.last}.to eq(:timed_out)
257
292
  end
258
- .then("then write to renamed file") do
259
- File.open(new_file_path, "ab") { |file| file.write("line3\nline4\n") }
293
+ .then("rename file") do
294
+ FileUtils.mv(file_path, file_path2)
295
+ end
296
+ .then_after(0.1, "then write to renamed file") do
297
+ File.open(file_path2, "ab") { |file| file.write("line3\nline4\n") }
260
298
  end
261
- .then_after(0.55, "quit after a short time") do
299
+ .then_after(0.1, "wait for lines") do
300
+ wait(0.5).for{listener2.lines.size}.to eq(2)
301
+ end
302
+ .then_after(0.1, "quit") do
262
303
  tailing.quit
263
304
  end
264
305
  end
265
306
 
266
307
  it "the first set of lines are not re-read" do
308
+ actions.activate_quietly
267
309
  tailing.subscribe(observer)
268
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
269
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :timed_out, :delete])
270
- expect(observer.listener_for(new_file_path).lines).to eq(["line3", "line4"])
271
- expect(observer.listener_for(new_file_path).calls).to eq([:open, :accept, :accept, :timed_out])
310
+ actions.assert_no_errors
311
+ expect(listener1.lines).to eq([])
312
+ expect(listener1.calls).to eq([:open, :timed_out, :delete])
313
+ expect(listener2.lines).to eq(["line3", "line4"])
314
+ expect(listener2.calls).to eq([:open, :accept, :accept, :timed_out])
272
315
  end
273
316
  end
274
317
 
275
318
  context "when watching a directory with files and data is appended" do
276
- before do
319
+ let(:suffix) { "I" }
320
+ let(:actions) do
277
321
  RSpec::Sequencing
278
- .run("create file") do
322
+ .run("file created") do
279
323
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
280
324
  end
281
- .then_after(0.25, "start watching after file is written") do
325
+ .then_after(0.15, "start watching after file is written") do
282
326
  tailing.watch_this(watch_dir)
283
327
  end
284
328
  .then_after(0.45, "append more lines to the file") do
285
329
  File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
330
+ wait(0.5).for{listener1.lines.size}.to eq(2)
286
331
  end
287
- .then_after(0.45, "quit after a short time") do
332
+ .then_after(0.1, "quit") do
288
333
  tailing.quit
289
334
  end
290
335
  end
291
336
 
292
- it "appended lines are read after an EOF" do
337
+ it "appended lines are read only" do
338
+ actions.activate_quietly
293
339
  tailing.subscribe(observer)
294
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :accept, :accept])
295
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2", "line3", "line4"])
340
+ actions.assert_no_errors
341
+ expect(listener1.calls).to eq([:open, :accept, :accept])
342
+ expect(listener1.lines).to eq(["line3", "line4"])
296
343
  end
297
344
  end
298
345
 
299
346
  context "when close older expiry is enabled" do
300
347
  let(:opts) { super.merge(:close_older => 1) }
301
- before do
302
- RSpec::Sequencing
303
- .run("create file") do
304
- File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
305
- end
306
- .then("start watching before file ages more than close_older") do
307
- tailing.watch_this(watch_dir)
308
- end
309
- .then_after(2.1, "quit after allowing time to close the file") do
310
- tailing.quit
311
- end
348
+ let(:suffix) { "J" }
349
+ let(:actions) do
350
+ RSpec::Sequencing.run("create file") do
351
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
352
+ end
353
+ .then("watch and wait") do
354
+ tailing.watch_this(watch_dir)
355
+ wait(1.25).for{listener1.calls}.to eq([:open, :timed_out])
356
+ end
357
+ .then("quit") do
358
+ tailing.quit
359
+ end
312
360
  end
313
361
 
314
- it "lines are read and the file times out" do
362
+ it "existing lines are not read and the file times out" do
363
+ actions.activate_quietly
315
364
  tailing.subscribe(observer)
316
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :timed_out])
317
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
365
+ actions.assert_no_errors
366
+ expect(listener1.lines).to eq([])
318
367
  end
319
368
  end
320
369
 
321
370
  context "when close older expiry is enabled and after timeout the file is appended-to" do
322
- let(:opts) { super.merge(:close_older => 1) }
323
- before do
371
+ let(:opts) { super.merge(:close_older => 0.5) }
372
+ let(:suffix) { "K" }
373
+ let(:actions) do
324
374
  RSpec::Sequencing
325
- .run("create file") do
375
+ .run("start watching") do
376
+ tailing.watch_this(watch_dir)
377
+ end
378
+ .then("create file") do
326
379
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
327
380
  end
328
- .then("start watching before file ages more than close_older") do
329
- tailing.watch_this(watch_dir)
381
+ .then("wait for file to be read") do
382
+ wait(0.5).for{listener1.calls}.to eq([:open, :accept, :accept]), "file is not read"
330
383
  end
331
- .then_after(2.1, "append more lines to file after file ages more than close_older") do
384
+ .then("wait for file to be read and time out") do
385
+ wait(0.75).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out]), "file did not timeout the first time"
386
+ end
387
+ .then("append more lines to file after file ages more than close_older") do
332
388
  File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
333
389
  end
334
- .then_after(2.1, "quit after allowing time to close the file") do
390
+ .then("wait for last timeout") do
391
+ wait(0.75).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out, :open, :accept, :accept, :timed_out]), "file did not timeout the second time"
392
+ end
393
+ .then("quit") do
335
394
  tailing.quit
336
395
  end
337
396
  end
338
397
 
339
398
  it "all lines are read" do
399
+ actions.activate_quietly
340
400
  tailing.subscribe(observer)
341
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :timed_out, :open, :accept, :accept, :timed_out])
342
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2", "line3", "line4"])
401
+ actions.assert_no_errors
402
+ expect(listener1.lines).to eq(["line1", "line2", "line3", "line4"])
343
403
  end
344
404
  end
345
405
 
346
406
  context "when ignore older expiry is enabled and all files are already expired" do
347
407
  let(:opts) { super.merge(:ignore_older => 1) }
348
- before do
408
+ let(:suffix) { "L" }
409
+ let(:actions) do
349
410
  RSpec::Sequencing
350
411
  .run("create file older than ignore_older and watch") do
351
412
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
@@ -358,60 +419,112 @@ module FileWatch
358
419
  end
359
420
 
360
421
  it "no files are read" do
422
+ actions.activate_quietly
423
+ tailing.subscribe(observer)
424
+ expect(listener1.calls).to eq([])
425
+ expect(listener1.lines).to eq([])
426
+ end
427
+ end
428
+
429
+ context "when a file is renamed before it gets activated", :unix => true do
430
+ let(:max) { 1 }
431
+ let(:opts) { super.merge(:file_chunk_count => 8, :file_chunk_size => 6, :close_older => 0.1, :discover_interval => 6) }
432
+ let(:suffix) { "M" }
433
+ let(:start_new_files_at) { :beginning } # we are creating files and sincedb record before hand
434
+ let(:actions) do
435
+ RSpec::Sequencing
436
+ .run("create files and sincedb record") do
437
+ File.open(file_path, "wb") { |file| 32.times{file.write("line1\n")} }
438
+ File.open(file_path2, "wb") { |file| file.write("line2\n") }
439
+ # synthesize a sincedb record
440
+ stat = File.stat(file_path2)
441
+ record = [stat.ino.to_s, stat.dev_major.to_s, stat.dev_minor.to_s, "0", "1526220348.083179", file_path2]
442
+ File.open(sincedb_path, "wb") { |file| file.puts(record.join(" ")) }
443
+ end
444
+ .then_after(0.2, "watch") do
445
+ tailing.watch_this(watch_dir)
446
+ end
447
+ .then_after(0.1, "rename file 2") do
448
+ FileUtils.mv(file_path2, file_path3)
449
+ end
450
+ .then("wait") do
451
+ wait(2).for do
452
+ listener1.lines.size == 32 && listener2.calls == [:delete] && listener3.calls == [:open, :accept, :timed_out]
453
+ end.to eq(true), "listener1.lines != 32 or listener2.calls != [:delete] or listener3.calls != [:open, :accept, :timed_out]"
454
+ end
455
+ .then("quit") do
456
+ tailing.quit
457
+ end
458
+ end
459
+
460
+ it "files are read correctly" do
461
+ actions.activate_quietly
361
462
  tailing.subscribe(observer)
362
- expect(observer.listener_for(file_path).calls).to eq([])
363
- expect(observer.listener_for(file_path).lines).to eq([])
463
+ actions.assert_no_errors
464
+ expect(listener2.lines).to eq([])
465
+ expect(listener3.lines).to eq(["line2"])
364
466
  end
365
467
  end
366
468
 
367
469
  context "when ignore_older is less than close_older and all files are not expired" do
368
- let(:opts) { super.merge(:ignore_older => 1, :close_older => 1.5) }
369
- before do
470
+ let(:opts) { super.merge(:ignore_older => 1, :close_older => 1.1) }
471
+ let(:suffix) { "N" }
472
+ let(:start_new_files_at) { :beginning }
473
+ let(:actions) do
370
474
  RSpec::Sequencing
371
- .run("create file") do
475
+ .run_after(0.1, "file created") do
372
476
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
373
477
  end
374
478
  .then("start watching before file age reaches ignore_older") do
375
479
  tailing.watch_this(watch_dir)
376
480
  end
377
- .then_after(1.75, "quit after allowing time to close the file") do
481
+ .then("wait for lines") do
482
+ wait(1.5).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out])
483
+ end
484
+ .then("quit") do
378
485
  tailing.quit
379
486
  end
380
487
  end
381
488
 
382
489
  it "reads lines normally" do
490
+ actions.activate_quietly
383
491
  tailing.subscribe(observer)
384
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :timed_out])
385
- expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
492
+ actions.assert_no_errors
493
+ expect(listener1.lines).to eq(["line1", "line2"])
386
494
  end
387
495
  end
388
496
 
389
497
  context "when ignore_older is less than close_older and all files are expired" do
390
498
  let(:opts) { super.merge(:ignore_older => 10, :close_older => 1) }
391
- before do
499
+ let(:suffix) { "P" }
500
+ let(:actions) do
392
501
  RSpec::Sequencing
393
- .run("create file older than ignore_older and watch") do
502
+ .run("creating file") do
394
503
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
504
+ end
505
+ .then("making it older by 15 seconds and watch") do
395
506
  FileWatch.make_file_older(file_path, 15)
396
507
  tailing.watch_this(watch_dir)
397
508
  end
398
- .then_after(1.5, "quit after allowing time to check the files") do
509
+ .then_after(0.75, "quit after allowing time to check the files") do
399
510
  tailing.quit
400
511
  end
401
512
  end
402
513
 
403
514
  it "no files are read" do
515
+ actions.activate_quietly
404
516
  tailing.subscribe(observer)
405
- expect(observer.listener_for(file_path).calls).to eq([])
406
- expect(observer.listener_for(file_path).lines).to eq([])
517
+ expect(listener1.calls).to eq([])
518
+ expect(listener1.lines).to eq([])
407
519
  end
408
520
  end
409
521
 
410
522
  context "when ignore older and close older expiry is enabled and after timeout the file is appended-to" do
411
- let(:opts) { super.merge(:ignore_older => 20, :close_older => 1) }
412
- before do
523
+ let(:opts) { super.merge(:ignore_older => 20, :close_older => 0.5) }
524
+ let(:suffix) { "Q" }
525
+ let(:actions) do
413
526
  RSpec::Sequencing
414
- .run("create file older than ignore_older and watch") do
527
+ .run("file older than ignore_older created and watching") do
415
528
  File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
416
529
  FileWatch.make_file_older(file_path, 25)
417
530
  tailing.watch_this(watch_dir)
@@ -419,37 +532,46 @@ module FileWatch
419
532
  .then_after(0.15, "append more lines to file after file ages more than ignore_older") do
420
533
  File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
421
534
  end
422
- .then_after(1.25, "quit after allowing time to close the file") do
535
+ .then("wait for lines") do
536
+ wait(2).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out])
537
+ end
538
+ .then_after(0.1, "quit after allowing time to close the file") do
423
539
  tailing.quit
424
540
  end
425
541
  end
426
542
 
427
543
  it "reads the added lines only" do
544
+ actions.activate_quietly
428
545
  tailing.subscribe(observer)
429
- expect(observer.listener_for(file_path).lines).to eq(["line3", "line4"])
430
- expect(observer.listener_for(file_path).calls).to eq([:open, :accept, :accept, :timed_out])
546
+ actions.assert_no_errors
547
+ expect(listener1.lines).to eq(["line3", "line4"])
431
548
  end
432
549
  end
433
550
 
434
551
  context "when a non default delimiter is specified and it is not in the content" do
435
552
  let(:opts) { super.merge(:ignore_older => 20, :close_older => 1, :delimiter => "\nø") }
436
- before do
553
+ let(:suffix) { "R" }
554
+ let(:actions) do
437
555
  RSpec::Sequencing
438
- .run("create file") do
556
+ .run("start watching") do
557
+ tailing.watch_this(watch_dir)
558
+ end
559
+ .then("creating file") do
439
560
  File.open(file_path, "wb") { |file| file.write("line1\nline2") }
440
561
  end
441
- .then("start watching before file ages more than close_older") do
442
- tailing.watch_this(watch_dir)
562
+ .then("wait for :timeout") do
563
+ wait(2).for{listener1.calls}.to eq([:open, :timed_out])
443
564
  end
444
- .then_after(2.1, "quit after allowing time to close the file") do
565
+ .then_after(0.75, "quit after allowing time to close the file") do
445
566
  tailing.quit
446
567
  end
447
568
  end
448
569
 
449
570
  it "the file is opened, data is read, but no lines are found, the file times out" do
571
+ actions.activate_quietly
450
572
  tailing.subscribe(observer)
451
- expect(observer.listener_for(file_path).calls).to eq([:open, :timed_out])
452
- expect(observer.listener_for(file_path).lines).to eq([])
573
+ actions.assert_no_errors
574
+ expect(listener1.lines).to eq([])
453
575
  sincedb_record_fields = File.read(sincedb_path).split(" ")
454
576
  position_field_index = 3
455
577
  # tailing, no delimiter, we are expecting one, if it grows we read from the start.
@@ -459,5 +581,3 @@ module FileWatch
459
581
  end
460
582
  end
461
583
  end
462
-
463
-