1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.io.input;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25
26 import java.io.BufferedOutputStream;
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.InputStreamReader;
32 import java.io.OutputStreamWriter;
33 import java.io.RandomAccessFile;
34 import java.io.Writer;
35 import java.nio.charset.Charset;
36 import java.nio.charset.StandardCharsets;
37 import java.nio.file.Files;
38 import java.nio.file.StandardOpenOption;
39 import java.nio.file.attribute.FileTime;
40 import java.time.Duration;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.Executor;
47 import java.util.concurrent.Executors;
48 import java.util.concurrent.ScheduledThreadPoolExecutor;
49 import java.util.concurrent.TimeUnit;
50
51 import org.apache.commons.io.FileUtils;
52 import org.apache.commons.io.IOUtils;
53 import org.apache.commons.io.RandomAccessFileMode;
54 import org.apache.commons.io.TestResources;
55 import org.apache.commons.io.test.TestUtils;
56 import org.apache.commons.lang3.SystemProperties;
57 import org.junit.jupiter.api.Test;
58 import org.junit.jupiter.api.io.TempDir;
59
60
61
62
63 class TailerTest {
64
65 private static final class NonStandardTailable implements Tailer.Tailable {
66
67 private final File file;
68
69 NonStandardTailable(final File file) {
70 this.file = file;
71 }
72
73 @Override
74 public Tailer.RandomAccessResourceBridge getRandomAccess(final String mode) throws FileNotFoundException {
75 return new Tailer.RandomAccessResourceBridge() {
76
77 private final RandomAccessFile randomAccessFile = new RandomAccessFile(file, mode);
78
79 @Override
80 public void close() throws IOException {
81 randomAccessFile.close();
82 }
83
84 @Override
85 public long getPointer() throws IOException {
86 return randomAccessFile.getFilePointer();
87 }
88
89 @Override
90 public int read(final byte[] b) throws IOException {
91 return randomAccessFile.read(b);
92 }
93
94 @Override
95 public void seek(final long position) throws IOException {
96 randomAccessFile.seek(position);
97 }
98 };
99 }
100
101 @Override
102 public boolean isNewer(final FileTime fileTime) throws IOException {
103 return FileUtils.isFileNewer(file, fileTime);
104 }
105
106 @Override
107 public FileTime lastModifiedFileTime() throws IOException {
108 return FileUtils.lastModifiedFileTime(file);
109 }
110
111 @Override
112 public long size() {
113 return file.length();
114 }
115 }
116
117
118
119
120 private static final class TestTailerListener extends TailerListenerAdapter {
121
122
123 private final List<String> lines = Collections.synchronizedList(new ArrayList<>());
124
125 private final CountDownLatch latch;
126
127 volatile Exception exception;
128
129 volatile int notFound;
130
131 volatile int rotated;
132
133 volatile int initialized;
134
135 volatile int reachedEndOfFile;
136
137 TestTailerListener() {
138 latch = new CountDownLatch(1);
139 }
140
141 TestTailerListener(final int expectedLines) {
142 latch = new CountDownLatch(expectedLines);
143 }
144
145 public boolean awaitExpectedLines(final long timeout, final TimeUnit timeUnit) throws InterruptedException {
146 return latch.await(timeout, timeUnit);
147 }
148
149 public void clear() {
150 lines.clear();
151 }
152
153 @Override
154 public void endOfFileReached() {
155 reachedEndOfFile++;
156 }
157
158 @Override
159 public void fileNotFound() {
160 notFound++;
161 }
162
163 @Override
164 public void fileRotated() {
165 rotated++;
166 }
167
168 public List<String> getLines() {
169 return lines;
170 }
171
172 @Override
173 public void handle(final Exception e) {
174 exception = e;
175 }
176
177 @Override
178 public void handle(final String line) {
179 lines.add(line);
180 latch.countDown();
181 }
182
183 @Override
184 public void init(final Tailer tailer) {
185 initialized++;
186 }
187 }
188
189 private static final int TEST_BUFFER_SIZE = 1024;
190
191 private static final int TEST_DELAY_MILLIS = 1500;
192
193 @TempDir
194 public static File temporaryFolder;
195
196 protected void createFile(final File file, final long size) throws IOException {
197 assertTrue(file.getParentFile().exists(), () -> "Cannot create file " + file + " as the parent directory does not exist");
198 try (BufferedOutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()))) {
199 TestUtils.generateTestData(output, size);
200 }
201
202
203 RandomAccessFile reader = null;
204 try {
205 while (reader == null) {
206 try {
207 reader = RandomAccessFileMode.READ_ONLY.create(file);
208 } catch (final FileNotFoundException ignore) {
209
210 }
211 TestUtils.sleepQuietly(200L);
212 }
213 } finally {
214 IOUtils.closeQuietly(reader);
215 }
216
217 assertTrue(file.exists());
218 assertEquals(size, file.length());
219 }
220
221 private List<String> expectLinesWithLongTimeout(final TestTailerListener listener, final long minDelay, final int count) throws Exception {
222 for (int i = 0; i < count; i++) {
223 TestUtils.sleep(minDelay);
224 final List<String> lines = listener.getLines();
225 if (lines.size() > 0) {
226 return lines;
227 }
228 }
229 fail("Waiting for TestTailerListener.getLines() timed out after " + count * minDelay + " ms");
230 return null;
231 }
232
233 @Test
234 void testBufferBreak() throws Exception {
235 final long delay = 50;
236 final File file = new File(temporaryFolder, "testBufferBreak.txt");
237 createFile(file, 0);
238 final String data = "SBTOURIST\n";
239 writeStrings(file, data);
240 final TestTailerListener listener = new TestTailerListener();
241 try (Tailer tailer = new Tailer(file, listener, delay, false, 1)) {
242 final Thread thread = new Thread(tailer, "commons-io-tailer-testBufferBreak");
243 thread.start();
244 List<String> lines = listener.getLines();
245 assertEquals(data.length(), tailer.getTailable().size());
246 while (lines.isEmpty() || !lines.get(lines.size() - 1).equals("SBTOURIST")) {
247 lines = listener.getLines();
248 }
249 listener.clear();
250 }
251 }
252
253 @Test
254 void testBuilderWithNonStandardTailable() throws Exception {
255 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
256 createFile(file, 0);
257 final TestTailerListener listener = new TestTailerListener(1);
258 try (Tailer tailer = Tailer.builder()
259 .setExecutorService(Executors.newSingleThreadExecutor())
260 .setTailable(new NonStandardTailable(file))
261 .setTailerListener(listener)
262 .get()) {
263 assertTrue(tailer.getTailable() instanceof NonStandardTailable);
264 validateTailer(listener, file);
265 }
266 }
267
268 @Test
269 void testCreate() throws Exception {
270 final File file = new File(temporaryFolder, "tailer-create.txt");
271 createFile(file, 0);
272 final TestTailerListener listener = new TestTailerListener(1);
273 try (Tailer tailer = Tailer.create(file, listener)) {
274 validateTailer(listener, file);
275 }
276 }
277
278 @Test
279 void testCreateWithDelay() throws Exception {
280 final File file = new File(temporaryFolder, "tailer-create-with-delay.txt");
281 createFile(file, 0);
282 final TestTailerListener listener = new TestTailerListener(1);
283 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS)) {
284 validateTailer(listener, file);
285 }
286 }
287
288 @Test
289 void testCreateWithDelayAndFromStart() throws Exception {
290 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start.txt");
291 createFile(file, 0);
292 final TestTailerListener listener = new TestTailerListener(1);
293 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false)) {
294 validateTailer(listener, file);
295 }
296 }
297
298 @Test
299 void testCreateWithDelayAndFromStartWithBufferSize() throws Exception {
300 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-buffersize.txt");
301 createFile(file, 0);
302 final TestTailerListener listener = new TestTailerListener(1);
303 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, TEST_BUFFER_SIZE)) {
304 validateTailer(listener, file);
305 }
306 }
307
308 @Test
309 void testCreateWithDelayAndFromStartWithReopenAndBufferSize() throws Exception {
310 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize.txt");
311 createFile(file, 0);
312 final TestTailerListener listener = new TestTailerListener(1);
313 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
314 validateTailer(listener, file);
315 }
316 }
317
318 @Test
319 void testCreateWithDelayAndFromStartWithReopenAndBufferSizeAndCharset() throws Exception {
320 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
321 createFile(file, 0);
322 final TestTailerListener listener = new TestTailerListener(1);
323 try (Tailer tailer = Tailer.create(file, StandardCharsets.UTF_8, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
324 validateTailer(listener, file);
325 }
326 }
327
328 @Test
329 void testCreatorWithDelayAndFromStartWithReopen() throws Exception {
330 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen.txt");
331 createFile(file, 0);
332 final TestTailerListener listener = new TestTailerListener(1);
333 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, false)) {
334 validateTailer(listener, file);
335 }
336 }
337
338
339
340
341 @Test
342 void testInterrupt() throws Exception {
343 final File file = new File(temporaryFolder, "nosuchfile");
344 assertFalse(file.exists(), "nosuchfile should not exist");
345 final TestTailerListener listener = new TestTailerListener();
346
347 final int delay = 1000;
348 final int idle = 50;
349 try (Tailer tailer = new Tailer(file, listener, delay, false, IOUtils.DEFAULT_BUFFER_SIZE)) {
350 final Thread thread = new Thread(tailer, "commons-io-tailer-testInterrupt");
351 thread.setDaemon(true);
352 thread.start();
353 TestUtils.sleep(idle);
354 thread.interrupt();
355 TestUtils.sleep(delay + idle);
356 assertNotNull(listener.exception, "Missing InterruptedException");
357 assertTrue(listener.exception instanceof InterruptedException, "Unexpected Exception: " + listener.exception);
358 assertEquals(1, listener.initialized, "Expected init to be called");
359 assertTrue(listener.notFound > 0, "fileNotFound should be called");
360 assertEquals(0, listener.rotated, "fileRotated should be not be called");
361 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
362 }
363 }
364
365 @Test
366 void testIO335() throws Exception {
367
368 final long delayMillis = 50;
369 final File file = new File(temporaryFolder, "tailer-testio334.txt");
370 createFile(file, 0);
371 final TestTailerListener listener = new TestTailerListener();
372 try (Tailer tailer = new Tailer(file, listener, delayMillis, false)) {
373 final Thread thread = new Thread(tailer, "commons-io-tailer-testIO335");
374 thread.start();
375
376
377 writeStrings(file, "CRLF\r\n", "LF\n", "CR\r", "CRCR\r\r", "trail");
378 final long testDelayMillis = delayMillis * 10;
379 TestUtils.sleep(testDelayMillis);
380 final List<String> lines = listener.getLines();
381 assertEquals(4, lines.size(), "line count");
382 assertEquals("CRLF", lines.get(0), "line 1");
383 assertEquals("LF", lines.get(1), "line 2");
384 assertEquals("CR", lines.get(2), "line 3");
385 assertEquals("CRCR\r", lines.get(3), "line 4");
386 }
387 }
388
389 @Test
390 void testLongFile() throws Exception {
391 final long delay = 50;
392 final File file = new File(temporaryFolder, "testLongFile.txt");
393 createFile(file, 0);
394 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.APPEND)) {
395 for (int i = 0; i < 100000; i++) {
396 writer.write("LineLineLineLineLineLineLineLineLineLine\n");
397 }
398 writer.write("SBTOURIST\n");
399 }
400 final TestTailerListener listener = new TestTailerListener();
401 try (Tailer tailer = new Tailer(file, listener, delay, false)) {
402
403 final Thread thread = new Thread(tailer, "commons-io-tailer-testLongFile");
404 thread.start();
405 List<String> lines = listener.getLines();
406 while (lines.isEmpty() || !lines.get(lines.size() - 1).equals("SBTOURIST")) {
407 lines = listener.getLines();
408 }
409
410 assertFalse(lines.isEmpty());
411 listener.clear();
412 }
413 }
414
415 @Test
416 void testMultiByteBreak() throws Exception {
417
418 final long delay = 50;
419 final File origin = TestResources.getFile("test-file-utf8.bin");
420 final File file = new File(temporaryFolder, "testMultiByteBreak.txt");
421 createFile(file, 0);
422 final TestTailerListener listener = new TestTailerListener();
423 final String osname = SystemProperties.getOsName();
424 final boolean isWindows = osname.startsWith("Windows");
425
426 final Charset charsetUTF8 = StandardCharsets.UTF_8;
427 try (Tailer tailer = new Tailer(file, charsetUTF8, listener, delay, false, isWindows, IOUtils.DEFAULT_BUFFER_SIZE)) {
428 final Thread thread = new Thread(tailer, "commons-io-tailer-testMultiByteBreak");
429 thread.start();
430 try (Writer out = new OutputStreamWriter(Files.newOutputStream(file.toPath()), charsetUTF8);
431 BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(origin.toPath()), charsetUTF8))) {
432 final List<String> lines = new ArrayList<>();
433 String line;
434 while ((line = reader.readLine()) != null) {
435 out.write(line);
436 out.write("\n");
437 lines.add(line);
438 }
439 out.close();
440 final long testDelayMillis = delay * 10;
441 TestUtils.sleep(testDelayMillis);
442 final List<String> tailerlines = listener.getLines();
443 assertEquals(lines.size(), tailerlines.size(), "line count");
444 for (int i = 0, len = lines.size(); i < len; i++) {
445 final String expected = lines.get(i);
446 final String actual = tailerlines.get(i);
447 if (!expected.equals(actual)) {
448 fail("Line: " + i + "\nExp: (" + expected.length() + ") " + expected + "\nAct: (" + actual.length() + ") " + actual);
449 }
450 }
451 }
452 }
453 }
454
455 @Test
456 void testSimpleConstructor() throws Exception {
457 final File file = new File(temporaryFolder, "tailer-simple-constructor.txt");
458 createFile(file, 0);
459 final TestTailerListener listener = new TestTailerListener(1);
460 try (Tailer tailer = new Tailer(file, listener)) {
461 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructor");
462 thread.start();
463 validateTailer(listener, file);
464 }
465 }
466
467 @Test
468 void testSimpleConstructorWithDelay() throws Exception {
469 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay.txt");
470 createFile(file, 0);
471 final TestTailerListener listener = new TestTailerListener(1);
472 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS)) {
473 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelay");
474 thread.start();
475 validateTailer(listener, file);
476 }
477 }
478
479 @Test
480 void testSimpleConstructorWithDelayAndFromStart() throws Exception {
481 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start.txt");
482 createFile(file, 0);
483 final TestTailerListener listener = new TestTailerListener(1);
484 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false)) {
485 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelayAndFromStart");
486 thread.start();
487 validateTailer(listener, file);
488 }
489 }
490
491 @Test
492 void testSimpleConstructorWithDelayAndFromStartWithBufferSize() throws Exception {
493 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-buffersize.txt");
494 createFile(file, 0);
495 final TestTailerListener listener = new TestTailerListener(1);
496 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, TEST_BUFFER_SIZE)) {
497 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelayAndFromStartWithBufferSize");
498 thread.start();
499 validateTailer(listener, file);
500 }
501 }
502
503 @Test
504 void testSimpleConstructorWithDelayAndFromStartWithReopen() throws Exception {
505 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen.txt");
506 createFile(file, 0);
507 final TestTailerListener listener = new TestTailerListener(1);
508 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, false)) {
509 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelayAndFromStartWithReopen");
510 thread.start();
511 validateTailer(listener, file);
512 }
513 }
514
515 @Test
516 void testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSize() throws Exception {
517 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen-and-buffersize.txt");
518 createFile(file, 0);
519 final TestTailerListener listener = new TestTailerListener(1);
520 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
521 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSize");
522 thread.start();
523 validateTailer(listener, file);
524 }
525 }
526
527 @Test
528 void testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSizeAndCharset() throws Exception {
529 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
530 createFile(file, 0);
531 final TestTailerListener listener = new TestTailerListener(1);
532 try (Tailer tailer = new Tailer(file, StandardCharsets.UTF_8, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
533 final Thread thread = new Thread(tailer, "commons-io-tailer-testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSizeAndCharset");
534 thread.start();
535 validateTailer(listener, file);
536 }
537 }
538
539 @Test
540 void testStopWithNoFile() throws Exception {
541 final File file = new File(temporaryFolder, "nosuchfile");
542 assertFalse(file.exists(), "nosuchfile should not exist");
543 final TestTailerListener listener = new TestTailerListener();
544 final int delay = 100;
545 final int idle = 50;
546 try (Tailer tailer = Tailer.create(file, listener, delay, false)) {
547 TestUtils.sleep(idle);
548 }
549 TestUtils.sleep(delay + idle);
550 if (listener.exception != null) {
551 listener.exception.printStackTrace();
552 }
553 assertNull(listener.exception, "Should not generate Exception");
554 assertEquals(1, listener.initialized, "Expected init to be called");
555 assertTrue(listener.notFound > 0, "fileNotFound should be called");
556 assertEquals(0, listener.rotated, "fileRotated should be not be called");
557 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
558 }
559
560 @Test
561 void testStopWithNoFileUsingExecutor() throws Exception {
562 final File file = new File(temporaryFolder, "nosuchfile");
563 assertFalse(file.exists(), "nosuchfile should not exist");
564 final TestTailerListener listener = new TestTailerListener();
565 final int delay = 100;
566 final int idle = 50;
567 try (Tailer tailer = new Tailer(file, listener, delay, false)) {
568 final Executor exec = new ScheduledThreadPoolExecutor(1);
569 exec.execute(tailer);
570 TestUtils.sleep(idle);
571 }
572 TestUtils.sleep(delay + idle);
573 assertNull(listener.exception, "Should not generate Exception");
574 assertEquals(1, listener.initialized, "Expected init to be called");
575 assertTrue(listener.notFound > 0, "fileNotFound should be called");
576 assertEquals(0, listener.rotated, "fileRotated should be not be called");
577 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
578 }
579
580 @Test
581 void testTailer() throws Exception {
582
583 final long delayMillis = 50;
584 final File file = new File(temporaryFolder, "tailer1-test.txt");
585 createFile(file, 0);
586 final TestTailerListener listener = new TestTailerListener();
587 final String osname = SystemProperties.getOsName();
588 final boolean isWindows = osname.startsWith("Windows");
589 try (Tailer tailer = new Tailer(file, listener, delayMillis, false, isWindows)) {
590 final Thread thread = new Thread(tailer, "commons-io-tailer-testTailer");
591 thread.start();
592
593 writeLines(file, "Line one", "Line two");
594 final long testDelayMillis = delayMillis * 10;
595 TestUtils.sleep(testDelayMillis);
596 List<String> lines = listener.getLines();
597 assertEquals(2, lines.size(), "1 line count");
598 assertEquals("Line one", lines.get(0), "1 line 1");
599 assertEquals("Line two", lines.get(1), "1 line 2");
600 listener.clear();
601
602 writeLines(file, "Line three");
603 TestUtils.sleep(testDelayMillis);
604 lines = listener.getLines();
605 assertEquals(1, lines.size(), "2 line count");
606 assertEquals("Line three", lines.get(0), "2 line 3");
607 listener.clear();
608
609 lines = FileUtils.readLines(file, StandardCharsets.UTF_8);
610 assertEquals(3, lines.size(), "3 line count");
611 assertEquals("Line one", lines.get(0), "3 line 1");
612 assertEquals("Line two", lines.get(1), "3 line 2");
613 assertEquals("Line three", lines.get(2), "3 line 3");
614
615 file.delete();
616 assertFalse(file.exists(), "File should not exist");
617 createFile(file, 0);
618 assertTrue(file.exists(), "File should now exist");
619 TestUtils.sleep(testDelayMillis);
620
621 writeLines(file, "Line four");
622 TestUtils.sleep(testDelayMillis);
623 lines = listener.getLines();
624 assertEquals(1, lines.size(), "4 line count");
625 assertEquals("Line four", lines.get(0), "4 line 3");
626 listener.clear();
627
628 thread.interrupt();
629 TestUtils.sleep(testDelayMillis * 4);
630 writeLines(file, "Line five");
631 assertEquals(0, listener.getLines().size(), "4 line count");
632 assertNotNull(listener.exception, "Missing InterruptedException");
633 assertTrue(listener.exception instanceof InterruptedException, "Unexpected Exception: " + listener.exception);
634 assertEquals(1, listener.initialized, "Expected init to be called");
635
636
637 assertEquals(1, listener.rotated, "fileRotated should be called");
638 }
639 }
640
641 @Test
642 void testTailerEndOfFileReached() throws Exception {
643
644 final long delayMillis = 50;
645 final long testDelayMillis = delayMillis * 10;
646 final File file = new File(temporaryFolder, "tailer-eof-test.txt");
647 createFile(file, 0);
648 final TestTailerListener listener = new TestTailerListener();
649 final String osname = SystemProperties.getOsName();
650 final boolean isWindows = osname.startsWith("Windows");
651 try (Tailer tailer = new Tailer(file, listener, delayMillis, false, isWindows)) {
652 final Thread thread = new Thread(tailer, "commons-io-tailer-testTailerEndOfFileReached");
653 thread.start();
654
655 writeLines(file, "line1", "line2", "line3");
656 TestUtils.sleep(testDelayMillis);
657
658 writeLines(file, "line4", "line5", "line6");
659 TestUtils.sleep(testDelayMillis);
660
661 writeLines(file, "line7", "line8", "line9");
662 TestUtils.sleep(testDelayMillis);
663
664 assertTrue(listener.reachedEndOfFile >= 3, "end of file reached at least 3 times");
665 }
666 }
667
668 @Test
669 void testTailerEof() throws Exception {
670
671 final long delayMillis = 100;
672 final File file = new File(temporaryFolder, "tailer2-test.txt");
673 createFile(file, 0);
674 final TestTailerListener listener = new TestTailerListener();
675 try (Tailer tailer = new Tailer(file, listener, delayMillis, false)) {
676 final Thread thread = new Thread(tailer, "commons-io-tailer-testTailerEof");
677 thread.start();
678
679 writeStrings(file, "Line");
680 TestUtils.sleep(delayMillis * 2);
681 List<String> lines = listener.getLines();
682 assertEquals(0, lines.size(), "1 line count");
683 writeStrings(file, " one\n");
684 TestUtils.sleep(delayMillis * 4);
685 lines = listener.getLines();
686 assertEquals(1, lines.size(), "1 line count");
687 assertEquals("Line one", lines.get(0), "1 line 1");
688 listener.clear();
689 }
690 }
691
692 @Test
693 void testTailerIgnoreTouch() throws Exception {
694
695 final long delayMillis = 50;
696 final File file = new File(temporaryFolder, "tailer1-testIgnoreTouch.txt");
697 createFile(file, 0);
698 final TestTailerListener listener = new TestTailerListener();
699 try (Tailer tailer = Tailer.builder().setFile(file).setTailerListener(listener).setDelayDuration(Duration.ofMillis(delayMillis)).setStartThread(false)
700 .setIgnoreTouch(true).get()) {
701 final Thread thread = new Thread(tailer, "commons-io-tailer-testTailerIgnoreTouch");
702 thread.start();
703
704 writeLines(file, "Line one");
705 List<String> lines = expectLinesWithLongTimeout(listener, delayMillis, 20);
706 assertEquals(1, lines.size(), "1 line count");
707 assertEquals("Line one", lines.get(0), "1 line 1");
708 listener.clear();
709
710 TestUtils.sleepToNextSecond();
711 file.setLastModified(System.currentTimeMillis());
712 TestUtils.sleep(delayMillis * 10);
713 lines = listener.getLines();
714 assertEquals(0, lines.size(), "nothing should have changed by touching");
715 }
716 }
717
718 @Test
719 void testTailerReissueOnTouch() throws Exception {
720
721 final long delayMillis = 50;
722 final File file = new File(temporaryFolder, "tailer1-testReissueOnTouch.txt");
723 createFile(file, 0);
724 final TestTailerListener listener = new TestTailerListener();
725 try (Tailer tailer = Tailer.builder().setFile(file).setTailerListener(listener).setDelayDuration(Duration.ofMillis(delayMillis)).setStartThread(false)
726 .setIgnoreTouch(false).get()) {
727 final Thread thread = new Thread(tailer, "commons-io-tailer-testTailerReissueOnTouch");
728 thread.start();
729
730 writeLines(file, "Line one");
731 List<String> lines = expectLinesWithLongTimeout(listener, delayMillis, 50);
732 assertEquals(1, lines.size(), "1 line count");
733 assertEquals("Line one", lines.get(0), "1 line 1");
734 listener.clear();
735
736 TestUtils.sleepToNextSecond();
737 file.setLastModified(System.currentTimeMillis());
738 lines = expectLinesWithLongTimeout(listener, delayMillis, 20);
739 assertEquals(1, lines.size(), "1 line count");
740 assertEquals("Line one", lines.get(0), "1 line 1");
741 listener.clear();
742 }
743 }
744
745 private void validateTailer(final TestTailerListener listener, final File file) throws IOException, InterruptedException {
746 writeLines(file, "foo");
747 final int timeout = 30;
748 final TimeUnit timeoutUnit = TimeUnit.SECONDS;
749 assertTrue(listener.awaitExpectedLines(timeout, timeoutUnit), () -> String.format("await timed out after %s %s", timeout, timeoutUnit));
750 assertEquals(listener.getLines(), Arrays.asList("foo"), "lines");
751 }
752
753
754 private void writeLines(final File file, final String... lines) throws IOException {
755 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.APPEND)) {
756 for (final String line : lines) {
757 writer.write(line + "\n");
758 }
759 }
760 }
761
762
763 private void writeStrings(final File file, final String... strings) throws IOException {
764 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.APPEND)) {
765 for (final String string : strings) {
766 writer.write(string);
767 }
768 }
769 }
770 }