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 @SuppressWarnings("squid:S2699")
235 void testBufferBreak() throws Exception {
236 final long delay = 50;
237 final File file = new File(temporaryFolder, "testBufferBreak.txt");
238 createFile(file, 0);
239 writeStrings(file, "SBTOURIST\n");
240 final TestTailerListener listener = new TestTailerListener();
241 try (Tailer tailer = new Tailer(file, listener, delay, false, 1)) {
242 final Thread thread = new Thread(tailer);
243 thread.start();
244 List<String> lines = listener.getLines();
245 while (lines.isEmpty() || !lines.get(lines.size() - 1).equals("SBTOURIST")) {
246 lines = listener.getLines();
247 }
248 listener.clear();
249 }
250 }
251
252 @Test
253 void testBuilderWithNonStandardTailable() throws Exception {
254 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
255 createFile(file, 0);
256 final TestTailerListener listener = new TestTailerListener(1);
257 try (Tailer tailer = Tailer.builder()
258 .setExecutorService(Executors.newSingleThreadExecutor())
259 .setTailable(new NonStandardTailable(file))
260 .setTailerListener(listener)
261 .get()) {
262 assertTrue(tailer.getTailable() instanceof NonStandardTailable);
263 validateTailer(listener, file);
264 }
265 }
266
267 @Test
268 void testCreate() throws Exception {
269 final File file = new File(temporaryFolder, "tailer-create.txt");
270 createFile(file, 0);
271 final TestTailerListener listener = new TestTailerListener(1);
272 try (Tailer tailer = Tailer.create(file, listener)) {
273 validateTailer(listener, file);
274 }
275 }
276
277 @Test
278 void testCreateWithDelay() throws Exception {
279 final File file = new File(temporaryFolder, "tailer-create-with-delay.txt");
280 createFile(file, 0);
281 final TestTailerListener listener = new TestTailerListener(1);
282 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS)) {
283 validateTailer(listener, file);
284 }
285 }
286
287 @Test
288 void testCreateWithDelayAndFromStart() throws Exception {
289 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start.txt");
290 createFile(file, 0);
291 final TestTailerListener listener = new TestTailerListener(1);
292 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false)) {
293 validateTailer(listener, file);
294 }
295 }
296
297 @Test
298 void testCreateWithDelayAndFromStartWithBufferSize() throws Exception {
299 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-buffersize.txt");
300 createFile(file, 0);
301 final TestTailerListener listener = new TestTailerListener(1);
302 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, TEST_BUFFER_SIZE)) {
303 validateTailer(listener, file);
304 }
305 }
306
307 @Test
308 void testCreateWithDelayAndFromStartWithReopenAndBufferSize() throws Exception {
309 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize.txt");
310 createFile(file, 0);
311 final TestTailerListener listener = new TestTailerListener(1);
312 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
313 validateTailer(listener, file);
314 }
315 }
316
317 @Test
318 void testCreateWithDelayAndFromStartWithReopenAndBufferSizeAndCharset() throws Exception {
319 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
320 createFile(file, 0);
321 final TestTailerListener listener = new TestTailerListener(1);
322 try (Tailer tailer = Tailer.create(file, StandardCharsets.UTF_8, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
323 validateTailer(listener, file);
324 }
325 }
326
327 @Test
328 void testCreatorWithDelayAndFromStartWithReopen() throws Exception {
329 final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen.txt");
330 createFile(file, 0);
331 final TestTailerListener listener = new TestTailerListener(1);
332 try (Tailer tailer = Tailer.create(file, listener, TEST_DELAY_MILLIS, false, false)) {
333 validateTailer(listener, file);
334 }
335 }
336
337
338
339
340 @Test
341 void testInterrupt() throws Exception {
342 final File file = new File(temporaryFolder, "nosuchfile");
343 assertFalse(file.exists(), "nosuchfile should not exist");
344 final TestTailerListener listener = new TestTailerListener();
345
346 final int delay = 1000;
347 final int idle = 50;
348 try (Tailer tailer = new Tailer(file, listener, delay, false, IOUtils.DEFAULT_BUFFER_SIZE)) {
349 final Thread thread = new Thread(tailer);
350 thread.setDaemon(true);
351 thread.start();
352 TestUtils.sleep(idle);
353 thread.interrupt();
354 TestUtils.sleep(delay + idle);
355 assertNotNull(listener.exception, "Missing InterruptedException");
356 assertTrue(listener.exception instanceof InterruptedException, "Unexpected Exception: " + listener.exception);
357 assertEquals(1, listener.initialized, "Expected init to be called");
358 assertTrue(listener.notFound > 0, "fileNotFound should be called");
359 assertEquals(0, listener.rotated, "fileRotated should be not be called");
360 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
361 }
362 }
363
364 @Test
365 void testIO335() throws Exception {
366
367 final long delayMillis = 50;
368 final File file = new File(temporaryFolder, "tailer-testio334.txt");
369 createFile(file, 0);
370 final TestTailerListener listener = new TestTailerListener();
371 try (Tailer tailer = new Tailer(file, listener, delayMillis, false)) {
372 final Thread thread = new Thread(tailer);
373 thread.start();
374
375
376 writeStrings(file, "CRLF\r\n", "LF\n", "CR\r", "CRCR\r\r", "trail");
377 final long testDelayMillis = delayMillis * 10;
378 TestUtils.sleep(testDelayMillis);
379 final List<String> lines = listener.getLines();
380 assertEquals(4, lines.size(), "line count");
381 assertEquals("CRLF", lines.get(0), "line 1");
382 assertEquals("LF", lines.get(1), "line 2");
383 assertEquals("CR", lines.get(2), "line 3");
384 assertEquals("CRCR\r", lines.get(3), "line 4");
385 }
386 }
387
388 @Test
389 @SuppressWarnings("squid:S2699")
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);
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 listener.clear();
411 }
412 }
413
414 @Test
415 void testMultiByteBreak() throws Exception {
416
417 final long delay = 50;
418 final File origin = TestResources.getFile("test-file-utf8.bin");
419 final File file = new File(temporaryFolder, "testMultiByteBreak.txt");
420 createFile(file, 0);
421 final TestTailerListener listener = new TestTailerListener();
422 final String osname = SystemProperties.getOsName();
423 final boolean isWindows = osname.startsWith("Windows");
424
425 final Charset charsetUTF8 = StandardCharsets.UTF_8;
426 try (Tailer tailer = new Tailer(file, charsetUTF8, listener, delay, false, isWindows, IOUtils.DEFAULT_BUFFER_SIZE)) {
427 final Thread thread = new Thread(tailer);
428 thread.start();
429 try (Writer out = new OutputStreamWriter(Files.newOutputStream(file.toPath()), charsetUTF8);
430 BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(origin.toPath()), charsetUTF8))) {
431 final List<String> lines = new ArrayList<>();
432 String line;
433 while ((line = reader.readLine()) != null) {
434 out.write(line);
435 out.write("\n");
436 lines.add(line);
437 }
438 out.close();
439 final long testDelayMillis = delay * 10;
440 TestUtils.sleep(testDelayMillis);
441 final List<String> tailerlines = listener.getLines();
442 assertEquals(lines.size(), tailerlines.size(), "line count");
443 for (int i = 0, len = lines.size(); i < len; i++) {
444 final String expected = lines.get(i);
445 final String actual = tailerlines.get(i);
446 if (!expected.equals(actual)) {
447 fail("Line: " + i + "\nExp: (" + expected.length() + ") " + expected + "\nAct: (" + actual.length() + ") " + actual);
448 }
449 }
450 }
451 }
452 }
453
454 @Test
455 void testSimpleConstructor() throws Exception {
456 final File file = new File(temporaryFolder, "tailer-simple-constructor.txt");
457 createFile(file, 0);
458 final TestTailerListener listener = new TestTailerListener(1);
459 try (Tailer tailer = new Tailer(file, listener)) {
460 final Thread thread = new Thread(tailer);
461 thread.start();
462 validateTailer(listener, file);
463 }
464 }
465
466 @Test
467 void testSimpleConstructorWithDelay() throws Exception {
468 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay.txt");
469 createFile(file, 0);
470 final TestTailerListener listener = new TestTailerListener(1);
471 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS)) {
472 final Thread thread = new Thread(tailer);
473 thread.start();
474 validateTailer(listener, file);
475 }
476 }
477
478 @Test
479 void testSimpleConstructorWithDelayAndFromStart() throws Exception {
480 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start.txt");
481 createFile(file, 0);
482 final TestTailerListener listener = new TestTailerListener(1);
483 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false)) {
484 final Thread thread = new Thread(tailer);
485 thread.start();
486 validateTailer(listener, file);
487 }
488 }
489
490 @Test
491 void testSimpleConstructorWithDelayAndFromStartWithBufferSize() throws Exception {
492 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-buffersize.txt");
493 createFile(file, 0);
494 final TestTailerListener listener = new TestTailerListener(1);
495 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, TEST_BUFFER_SIZE)) {
496 final Thread thread = new Thread(tailer);
497 thread.start();
498 validateTailer(listener, file);
499 }
500 }
501
502 @Test
503 void testSimpleConstructorWithDelayAndFromStartWithReopen() throws Exception {
504 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen.txt");
505 createFile(file, 0);
506 final TestTailerListener listener = new TestTailerListener(1);
507 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, false)) {
508 final Thread thread = new Thread(tailer);
509 thread.start();
510 validateTailer(listener, file);
511 }
512 }
513
514 @Test
515 void testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSize() throws Exception {
516 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen-and-buffersize.txt");
517 createFile(file, 0);
518 final TestTailerListener listener = new TestTailerListener(1);
519 try (Tailer tailer = new Tailer(file, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
520 final Thread thread = new Thread(tailer);
521 thread.start();
522 validateTailer(listener, file);
523 }
524 }
525
526 @Test
527 void testSimpleConstructorWithDelayAndFromStartWithReopenAndBufferSizeAndCharset() throws Exception {
528 final File file = new File(temporaryFolder, "tailer-simple-constructor-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
529 createFile(file, 0);
530 final TestTailerListener listener = new TestTailerListener(1);
531 try (Tailer tailer = new Tailer(file, StandardCharsets.UTF_8, listener, TEST_DELAY_MILLIS, false, true, TEST_BUFFER_SIZE)) {
532 final Thread thread = new Thread(tailer);
533 thread.start();
534 validateTailer(listener, file);
535 }
536 }
537
538 @Test
539 void testStopWithNoFile() throws Exception {
540 final File file = new File(temporaryFolder, "nosuchfile");
541 assertFalse(file.exists(), "nosuchfile should not exist");
542 final TestTailerListener listener = new TestTailerListener();
543 final int delay = 100;
544 final int idle = 50;
545 try (Tailer tailer = Tailer.create(file, listener, delay, false)) {
546 TestUtils.sleep(idle);
547 }
548 TestUtils.sleep(delay + idle);
549 if (listener.exception != null) {
550 listener.exception.printStackTrace();
551 }
552 assertNull(listener.exception, "Should not generate Exception");
553 assertEquals(1, listener.initialized, "Expected init to be called");
554 assertTrue(listener.notFound > 0, "fileNotFound should be called");
555 assertEquals(0, listener.rotated, "fileRotated should be not be called");
556 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
557 }
558
559 @Test
560 void testStopWithNoFileUsingExecutor() throws Exception {
561 final File file = new File(temporaryFolder, "nosuchfile");
562 assertFalse(file.exists(), "nosuchfile should not exist");
563 final TestTailerListener listener = new TestTailerListener();
564 final int delay = 100;
565 final int idle = 50;
566 try (Tailer tailer = new Tailer(file, listener, delay, false)) {
567 final Executor exec = new ScheduledThreadPoolExecutor(1);
568 exec.execute(tailer);
569 TestUtils.sleep(idle);
570 }
571 TestUtils.sleep(delay + idle);
572 assertNull(listener.exception, "Should not generate Exception");
573 assertEquals(1, listener.initialized, "Expected init to be called");
574 assertTrue(listener.notFound > 0, "fileNotFound should be called");
575 assertEquals(0, listener.rotated, "fileRotated should be not be called");
576 assertEquals(0, listener.reachedEndOfFile, "end of file never reached");
577 }
578
579 @Test
580 void testTailer() throws Exception {
581
582 final long delayMillis = 50;
583 final File file = new File(temporaryFolder, "tailer1-test.txt");
584 createFile(file, 0);
585 final TestTailerListener listener = new TestTailerListener();
586 final String osname = SystemProperties.getOsName();
587 final boolean isWindows = osname.startsWith("Windows");
588 try (Tailer tailer = new Tailer(file, listener, delayMillis, false, isWindows)) {
589 final Thread thread = new Thread(tailer);
590 thread.start();
591
592 writeLines(file, "Line one", "Line two");
593 final long testDelayMillis = delayMillis * 10;
594 TestUtils.sleep(testDelayMillis);
595 List<String> lines = listener.getLines();
596 assertEquals(2, lines.size(), "1 line count");
597 assertEquals("Line one", lines.get(0), "1 line 1");
598 assertEquals("Line two", lines.get(1), "1 line 2");
599 listener.clear();
600
601 writeLines(file, "Line three");
602 TestUtils.sleep(testDelayMillis);
603 lines = listener.getLines();
604 assertEquals(1, lines.size(), "2 line count");
605 assertEquals("Line three", lines.get(0), "2 line 3");
606 listener.clear();
607
608 lines = FileUtils.readLines(file, StandardCharsets.UTF_8);
609 assertEquals(3, lines.size(), "3 line count");
610 assertEquals("Line one", lines.get(0), "3 line 1");
611 assertEquals("Line two", lines.get(1), "3 line 2");
612 assertEquals("Line three", lines.get(2), "3 line 3");
613
614 file.delete();
615 assertFalse(file.exists(), "File should not exist");
616 createFile(file, 0);
617 assertTrue(file.exists(), "File should now exist");
618 TestUtils.sleep(testDelayMillis);
619
620 writeLines(file, "Line four");
621 TestUtils.sleep(testDelayMillis);
622 lines = listener.getLines();
623 assertEquals(1, lines.size(), "4 line count");
624 assertEquals("Line four", lines.get(0), "4 line 3");
625 listener.clear();
626
627 thread.interrupt();
628 TestUtils.sleep(testDelayMillis * 4);
629 writeLines(file, "Line five");
630 assertEquals(0, listener.getLines().size(), "4 line count");
631 assertNotNull(listener.exception, "Missing InterruptedException");
632 assertTrue(listener.exception instanceof InterruptedException, "Unexpected Exception: " + listener.exception);
633 assertEquals(1, listener.initialized, "Expected init to be called");
634
635
636 assertEquals(1, listener.rotated, "fileRotated should be called");
637 }
638 }
639
640 @Test
641 void testTailerEndOfFileReached() throws Exception {
642
643 final long delayMillis = 50;
644 final long testDelayMillis = delayMillis * 10;
645 final File file = new File(temporaryFolder, "tailer-eof-test.txt");
646 createFile(file, 0);
647 final TestTailerListener listener = new TestTailerListener();
648 final String osname = SystemProperties.getOsName();
649 final boolean isWindows = osname.startsWith("Windows");
650 try (Tailer tailer = new Tailer(file, listener, delayMillis, false, isWindows)) {
651 final Thread thread = new Thread(tailer);
652 thread.start();
653
654 writeLines(file, "line1", "line2", "line3");
655 TestUtils.sleep(testDelayMillis);
656
657 writeLines(file, "line4", "line5", "line6");
658 TestUtils.sleep(testDelayMillis);
659
660 writeLines(file, "line7", "line8", "line9");
661 TestUtils.sleep(testDelayMillis);
662
663 assertTrue(listener.reachedEndOfFile >= 3, "end of file reached at least 3 times");
664 }
665 }
666
667 @Test
668 void testTailerEof() throws Exception {
669
670 final long delayMillis = 100;
671 final File file = new File(temporaryFolder, "tailer2-test.txt");
672 createFile(file, 0);
673 final TestTailerListener listener = new TestTailerListener();
674 try (Tailer tailer = new Tailer(file, listener, delayMillis, false)) {
675 final Thread thread = new Thread(tailer);
676 thread.start();
677
678 writeStrings(file, "Line");
679 TestUtils.sleep(delayMillis * 2);
680 List<String> lines = listener.getLines();
681 assertEquals(0, lines.size(), "1 line count");
682 writeStrings(file, " one\n");
683 TestUtils.sleep(delayMillis * 4);
684 lines = listener.getLines();
685 assertEquals(1, lines.size(), "1 line count");
686 assertEquals("Line one", lines.get(0), "1 line 1");
687 listener.clear();
688 }
689 }
690
691 @Test
692 void testTailerIgnoreTouch() throws Exception {
693
694 final long delayMillis = 50;
695 final File file = new File(temporaryFolder, "tailer1-testIgnoreTouch.txt");
696 createFile(file, 0);
697 final TestTailerListener listener = new TestTailerListener();
698 try (Tailer tailer = Tailer.builder().setFile(file).setTailerListener(listener).setDelayDuration(Duration.ofMillis(delayMillis)).setStartThread(false)
699 .setIgnoreTouch(true).get()) {
700 final Thread thread = new Thread(tailer);
701 thread.start();
702
703 writeLines(file, "Line one");
704 List<String> lines = expectLinesWithLongTimeout(listener, delayMillis, 20);
705 assertEquals(1, lines.size(), "1 line count");
706 assertEquals("Line one", lines.get(0), "1 line 1");
707 listener.clear();
708
709 TestUtils.sleepToNextSecond();
710 file.setLastModified(System.currentTimeMillis());
711 TestUtils.sleep(delayMillis * 10);
712 lines = listener.getLines();
713 assertEquals(0, lines.size(), "nothing should have changed by touching");
714 }
715 }
716
717 @Test
718 void testTailerReissueOnTouch() throws Exception {
719
720 final long delayMillis = 50;
721 final File file = new File(temporaryFolder, "tailer1-testReissueOnTouch.txt");
722 createFile(file, 0);
723 final TestTailerListener listener = new TestTailerListener();
724 try (Tailer tailer = Tailer.builder().setFile(file).setTailerListener(listener).setDelayDuration(Duration.ofMillis(delayMillis)).setStartThread(false)
725 .setIgnoreTouch(false).get()) {
726 final Thread thread = new Thread(tailer);
727 thread.start();
728
729 writeLines(file, "Line one");
730 List<String> lines = expectLinesWithLongTimeout(listener, delayMillis, 50);
731 assertEquals(1, lines.size(), "1 line count");
732 assertEquals("Line one", lines.get(0), "1 line 1");
733 listener.clear();
734
735 TestUtils.sleepToNextSecond();
736 file.setLastModified(System.currentTimeMillis());
737 lines = expectLinesWithLongTimeout(listener, delayMillis, 20);
738 assertEquals(1, lines.size(), "1 line count");
739 assertEquals("Line one", lines.get(0), "1 line 1");
740 listener.clear();
741 }
742 }
743
744 private void validateTailer(final TestTailerListener listener, final File file) throws IOException, InterruptedException {
745 writeLines(file, "foo");
746 final int timeout = 30;
747 final TimeUnit timeoutUnit = TimeUnit.SECONDS;
748 assertTrue(listener.awaitExpectedLines(timeout, timeoutUnit), () -> String.format("await timed out after %s %s", timeout, timeoutUnit));
749 assertEquals(listener.getLines(), Arrays.asList("foo"), "lines");
750 }
751
752
753 private void writeLines(final File file, final String... lines) throws IOException {
754 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.APPEND)) {
755 for (final String line : lines) {
756 writer.write(line + "\n");
757 }
758 }
759 }
760
761
762 private void writeStrings(final File file, final String... strings) throws IOException {
763 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.APPEND)) {
764 for (final String string : strings) {
765 writer.write(string);
766 }
767 }
768 }
769 }