‪TYPO3CMS  ‪main
RotatingFileWriterTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use Psr\Log\LogLevel;
29 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
30 
31 final class ‪RotatingFileWriterTest extends UnitTestCase
32 {
33  protected string ‪$logFileDirectory = 'Log';
34  protected string ‪$logFileName = 'test.log';
35  protected string ‪$testRoot;
36  protected bool ‪$resetSingletonInstances = true;
37 
38  protected function ‪setUp(): void
39  {
40  parent::setUp();
41  $this->testRoot = ‪Environment::getVarPath() . '/tests/';
42  ‪GeneralUtility::mkdir_deep($this->testRoot . $this->logFileDirectory);
43  $this->testFilesToDelete[] = ‪$this->testRoot;
44  }
45 
46  protected function ‪createWriter(string $prependName = ''): ‪RotatingFileWriter
47  {
48  ‪$logFileName = $this->‪getDefaultFileName($prependName);
49  if (file_exists(‪$logFileName)) {
50  unlink(‪$logFileName);
51  }
52  return GeneralUtility::makeInstance(RotatingFileWriter::class, [
53  'logFile' => ‪$logFileName,
54  ]);
55  }
56 
60  protected function ‪getDefaultFileName(string $prependName = ''): string
61  {
62  return $this->testRoot . $this->logFileDirectory . '/' . $prependName . ‪$this->logFileName;
63  }
64 
65  #[Test]
67  {
69 
70  touch(‪$logFileName);
71 
72  $writer = $this->‪createWriter();
73  $simpleRecord = GeneralUtility::makeInstance(LogRecord::class, ‪StringUtility::getUniqueId('test.core.log.rotatingFileWriter.simpleRecord.'), LogLevel::INFO, 'test record');
74  $writer->writeLog($simpleRecord);
75 
76  $rotatedFiles = glob(‪$logFileName . '.*');
77  self::assertCount(0, $rotatedFiles);
78  }
79 
80  #[Test]
82  {
84 
85  file_put_contents(‪$logFileName, 'fooo');
86 
87  $writer = GeneralUtility::makeInstance(RotatingFileWriter::class);
88  $writer->setLogFile(‪$logFileName);
89  $simpleRecord = GeneralUtility::makeInstance(LogRecord::class, ‪StringUtility::getUniqueId('test.core.log.rotatingFileWriter.simpleRecord.'), LogLevel::INFO, 'test record');
90  $writer->writeLog($simpleRecord);
91 
92  $rotatedFiles = glob(‪$logFileName . '.*');
93  self::assertCount(1, $rotatedFiles);
94  }
95 
96  public static function ‪intervalDataProvider(): array
97  {
98  return [
99  [Interval::DAILY],
100  [Interval::WEEKLY],
101  [Interval::MONTHLY],
102  [Interval::YEARLY],
103  ];
104  }
105 
106  #[DataProvider('intervalDataProvider')]
107  #[Test]
109  {
110  $testingTolerance = 100;
111  $rotationDate = (new \DateTime('@' . (time() + $testingTolerance)))
112  ->sub(new \DateInterval($interval->getDateInterval()))
113  ->format('YmdHis');
115 
116  file_put_contents(‪$logFileName, 'fooo');
117  file_put_contents(‪$logFileName . '.' . $rotationDate, 'fooo');
118 
119  $writer = GeneralUtility::makeInstance(RotatingFileWriter::class, [
120  'interval' => $interval,
121  'logFile' => ‪$logFileName,
122  ]);
123  $simpleRecord = GeneralUtility::makeInstance(LogRecord::class, ‪StringUtility::getUniqueId('test.core.log.rotatingFileWriter.simpleRecord.'), LogLevel::INFO, 'test record');
124  $writer->writeLog($simpleRecord);
125 
126  $rotatedFiles = glob(‪$logFileName . '.*');
127  self::assertCount(1, $rotatedFiles);
128  }
129 
130  #[DataProvider('intervalDataProvider')]
131  #[Test]
133  {
134  $rotationDateModifierClosure = match ($interval) {
135  Interval::MONTHLY, Interval::YEARLY => static function (\DateTimeImmutable $rotationDate) use ($interval): \DateTimeImmutable {
136  // This is great – when subtracting 1 month or 1 year, it may happen that the result would be invalid:
137  // e.g. 2024-03-30 -1 month -> 2024-02-30, 2024-05-31 -1 month -> 2024-04-31, 2028-02-29 -1 year -> 2027-02-29, etc.
138  // PHP thankfully catches this and rolls over to the next(!) valid date:
139  // e.g. 2024-03-30 -1 month -> 2024-03-01, 2024-05-31 -1 month -> 2024-05-01, 2028-02-29 -1 year -> 2027-03-01, etc.
140  // However, this is not the desired result in this case when SUBTRACTING a month as the previous(!!) valid date is required.
141  // For this reason, a custom handling in case of months and years is in place that goes back in time to the first day of
142  // the given period, and sets the current day or the last day of the resulting period, whatever fits:
143  // e.g. 2024-03-30 -1 month -> 2024-02-29, 2024-05-12 -1 month -> 2024-04-12, 2028-02-29 -1 year -> 2027-02-28, etc.
144  if ($interval === Interval::MONTHLY) {
145  $firstDayOfModifier = 'first day of -1 month';
146  } else {
147  $firstDayOfModifier = 'first day of -1 year';
148  }
149 
150  $currentDay = $rotationDate->format('j');
151  $rotationDate = $rotationDate->modify($firstDayOfModifier);
152  $totalDays = $rotationDate->format('t');
153  return $rotationDate->modify('+' . (min($currentDay, $totalDays) - 1) . ' days');
154  },
155  default => static function (\DateTimeImmutable $rotationDate) use ($interval): \DateTimeImmutable {
156  return $rotationDate->sub(new \DateInterval($interval->getDateInterval()));
157  },
158  };
159  $rotationDate = $rotationDateModifierClosure(new \DateTimeImmutable('@' . time()))->format('YmdHis');
161 
162  file_put_contents(‪$logFileName, 'fooo');
163  file_put_contents(‪$logFileName . '.' . $rotationDate, 'fooo');
164 
165  $writer = GeneralUtility::makeInstance(RotatingFileWriter::class, [
166  'interval' => $interval,
167  'logFile' => ‪$logFileName,
168  ]);
169  $simpleRecord = GeneralUtility::makeInstance(LogRecord::class, ‪StringUtility::getUniqueId('test.core.log.rotatingFileWriter.simpleRecord.'), LogLevel::INFO, 'test record');
170  $writer->writeLog($simpleRecord);
171 
172  $rotatedFiles = glob(‪$logFileName . '.*');
173  self::assertCount(2, $rotatedFiles);
174  }
175 
176  #[Test]
177  public function ‪rotationRespectsMaxAmountOfFiles(): void
178  {
180 
181  file_put_contents(‪$logFileName, 'fooo');
182  file_put_contents(‪$logFileName . '.20230609093215', 'fooo');
183  file_put_contents(‪$logFileName . '.20230608093215', 'fooo');
184  file_put_contents(‪$logFileName . '.20230607093215', 'fooo');
185 
186  $writer = GeneralUtility::makeInstance(RotatingFileWriter::class, [
187  'interval' => Interval::DAILY,
188  'logFile' => ‪$logFileName,
189  'maxFiles' => 3,
190  ]);
191  $simpleRecord = GeneralUtility::makeInstance(LogRecord::class, ‪StringUtility::getUniqueId('test.core.log.rotatingFileWriter.simpleRecord.'), LogLevel::INFO, 'test record');
192  $writer->writeLog($simpleRecord);
193 
194  self::assertFileDoesNotExist(‪$logFileName . '.20230607093215');
195  }
196 }
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\$logFileDirectory
‪string $logFileDirectory
Definition: RotatingFileWriterTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest
Definition: RotatingFileWriterTest.php:32
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: RotatingFileWriterTest.php:36
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\$logFileName
‪string $logFileName
Definition: RotatingFileWriterTest.php:34
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\$testRoot
‪string $testRoot
Definition: RotatingFileWriterTest.php:35
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep(string $directory)
Definition: GeneralUtility.php:1654
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\writingLogWithLatestRotationInTimeFrameDoesNotRotate
‪writingLogWithLatestRotationInTimeFrameDoesNotRotate(Interval $interval)
Definition: RotatingFileWriterTest.php:108
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Core\Log\LogRecord
Definition: LogRecord.php:24
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\writingLogWithoutLatestRotationAndEmptyLogDoesNotRotate
‪writingLogWithoutLatestRotationAndEmptyLogDoesNotRotate()
Definition: RotatingFileWriterTest.php:66
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\writingLogWithExpiredLatestRotationInTimeFrameRotates
‪writingLogWithExpiredLatestRotationInTimeFrameRotates(Interval $interval)
Definition: RotatingFileWriterTest.php:132
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\rotationRespectsMaxAmountOfFiles
‪rotationRespectsMaxAmountOfFiles()
Definition: RotatingFileWriterTest.php:177
‪TYPO3\CMS\Core\Log\Writer\RotatingFileWriter
Definition: RotatingFileWriter.php:33
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\createWriter
‪createWriter(string $prependName='')
Definition: RotatingFileWriterTest.php:46
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\intervalDataProvider
‪static intervalDataProvider()
Definition: RotatingFileWriterTest.php:96
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\writingLogWithoutLatestRotationAndNonEmptyLogRotates
‪writingLogWithoutLatestRotationAndNonEmptyLogRotates()
Definition: RotatingFileWriterTest.php:81
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer
Definition: AbstractWriterTest.php:18
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\getDefaultFileName
‪non empty string getDefaultFileName(string $prependName='')
Definition: RotatingFileWriterTest.php:60
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Log\Writer\Enum\Interval
‪Interval
Definition: Interval.php:21
‪TYPO3\CMS\Core\Tests\Unit\Log\Writer\RotatingFileWriterTest\setUp
‪setUp()
Definition: RotatingFileWriterTest.php:38
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57