diff --git a/CHANGELOG.md b/CHANGELOG.md index a51cfd00..6ad1472a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Latest * [#343](https://github.com/cleverage/process-bundle/issues/343) The temporary files of `FileSplitterTask` (`file_splitter`), `CsvSplitterTask` (`csv_splitter`) and of the JSON buffer of `ProcessLauncherTask` (`json_buffering`) are created with a random name, exclusively (an existing path, symbolic links included, is never reused) and readable by their owner only, instead of a name derived from the time (`uniqid()`), opened in `w` mode with the permissions of the umask. Update documentation, add tests. * [#331](https://github.com/cleverage/process-bundle/issues/331) `ProcessManager`: the iterable tasks that produced items and the blocking tasks waiting for `proceed()` are tracked by task configuration instead of task code, and forgotten when their process ends. A task of a sub-process (`process_executor`) with the same code as a task of the parent no longer makes the parent lose the output of its blocking task (without error) or skip the flush at the end of its iteration (merged batches), and a failed sub-process no longer leaves its tasks to its next execution. Add tests. * [#333](https://github.com/cleverage/process-bundle/issues/333) `ProcessManager`: restore the previous task configuration (`getTaskConfiguration()`, read by the log processors for `task_code` and `task_service`) when a task, an initialization, a finalization or a sub-process ends. The logs of an iterable task no longer lose their task after its first item, and the logs of a parent process are no longer attributed to a task of its sub-process; the failing task is kept for the logs of the process failure. Add tests. +* [#335](https://github.com/cleverage/process-bundle/issues/335) `LineReaderTask` (`line_reader`) and `InputLineReaderTask` (`input_line_reader`): the last line of a file that does not end with a line break is no longer lost. Add tests. v5.4.1 ----- diff --git a/src/Task/File/LineReaderTask.php b/src/Task/File/LineReaderTask.php index 69f1d4c3..9e2fc5de 100644 --- a/src/Task/File/LineReaderTask.php +++ b/src/Task/File/LineReaderTask.php @@ -61,7 +61,8 @@ public function next(ProcessState $state): bool throw new LogicException('No File initialized'); } - $endOfFile = $this->file->eof(); + // Not eof(): with READ_AHEAD, the end of the file is reached when the last line is read, before it is emitted + $endOfFile = !$this->file->valid(); if ($endOfFile) { // Release the file to allow the following iteration to read it again $this->file = null; diff --git a/tests/Task/File/LineReaderTaskTest.php b/tests/Task/File/LineReaderTaskTest.php index d84de8a0..1da68ee0 100644 --- a/tests/Task/File/LineReaderTaskTest.php +++ b/tests/Task/File/LineReaderTaskTest.php @@ -42,6 +42,7 @@ protected function setUp(): void $filesystem = new Filesystem(); $filesystem->dumpFile($this->tmpDir.'/a.txt', "a1\na2\n"); $filesystem->dumpFile($this->tmpDir.'/b.txt', "b1\n"); + $filesystem->dumpFile($this->tmpDir.'/no_final_line_break.txt', "l1\n\nl3\r\nl4"); } protected function tearDown(): void @@ -60,6 +61,18 @@ public function testSameFileIsReadAgainOnSecondExecution(): void ); } + public function testTheLastLineIsReadWithoutFinalLineBreak(): void + { + self::assertSame( + ["l1\n", "\n", "l3\r\n", 'l4'], + $this->iterate(new LineReaderTask(), $this->createState(['filename' => $this->tmpDir.'/no_final_line_break.txt'])), + ); + self::assertSame( + ["l1\n", "\n", "l3\r\n", 'l4'], + $this->iterate(new InputLineReaderTask(), $this->createState([]), $this->tmpDir.'/no_final_line_break.txt'), + ); + } + public function testInputLineReaderReadsSameFileTwice(): void { $task = new InputLineReaderTask();