TimeBasedRollingTest.java
/*
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2026, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v2.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core.rolling;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.function.UnaryOperator;
import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests;
import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import ch.qos.logback.core.encoder.EchoEncoder;
import ch.qos.logback.core.testUtil.EnvUtilForTests;
import ch.qos.logback.core.util.StatusPrinter;
/**
* A rather exhaustive set of tests. Tests include leaving the file option
* blank, or setting it, with and without compression, and tests with or without
* stopping/restarting the RollingFileAppender.
* <p>
* The regression tests log a few times using a RollingFileAppender. Then, they
* predict the names of the files which should be generated and compare them
* with witness files.
* <p>
*
* <pre>
* Compression file option Stop/Restart
* Test1 NO BLANK NO
* Test2 YES BLANK NO
* Test3 NO BLANK YES
* Test4 NO SET YES
* Test5 NO SET NO
* Test6 YES SET NO
* </pre>
*
* @author Ceki Gülcü
*/
public class TimeBasedRollingTest extends ScaffoldingForRollingTests {
static public final String DATE_PATTERN_WITH_SECONDS = "yyyy-MM-dd_HH_mm_ss";
static final int NO_RESTART = 0;
static final int WITH_RESTART = 1;
static final int WITH_RESTART_AND_LONG_WAIT = 2000;
static final boolean FILE_OPTION_SET = true;
static final boolean FILE_OPTION_BLANK = false;
RollingFileAppender<Object> rfa1 = new RollingFileAppender<Object>();
TimeBasedRollingPolicy<Object> tbrp1 = new TimeBasedRollingPolicy<Object>();
RollingFileAppender<Object> rfa2 = new RollingFileAppender<Object>();
TimeBasedRollingPolicy<Object> tbrp2 = new TimeBasedRollingPolicy<Object>();
EchoEncoder<Object> encoder = new EchoEncoder<Object>();
RolloverChecker rolloverChecker;
@BeforeEach
@Override
public void setUp() {
super.setUp();
}
@AfterEach
public void tearDown() {
}
void initRFA(RollingFileAppender<Object> rfa, String filename) {
rfa.setContext(context);
rfa.setEncoder(encoder);
if (filename != null) {
rfa.setFile(filename);
}
}
void initTRBP(RollingFileAppender<Object> rfa, TimeBasedRollingPolicy<Object> tbrp, String filenamePattern,
long givenTime) {
tbrp.setContext(context);
tbrp.setFileNamePattern(filenamePattern);
tbrp.setParent(rfa);
tbrp.timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy<Object>();
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(givenTime);
rfa.setRollingPolicy(tbrp);
tbrp.start();
rfa.start();
}
void genericTest(String testId, String patternPrefix, String compressionSuffix,
UnaryOperator<String> filenameFunction, int waitDuration) throws IOException {
String fileName = filenameFunction.apply(testId);
// String fileName = fileOptionIsSet ? testId2FileName(testId) : null;
initRFA(rfa1, fileName);
String fileNamePatternStr = randomOutputDir + patternPrefix + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}"
+ compressionSuffix;
initTRBP(rfa1, tbrp1, fileNamePatternStr, currentTime);
// compute the current filename
addExpectedFileName_ByDate(fileNamePatternStr, getMillisOfCurrentPeriodsStart());
incCurrentTime(1100);
tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
for (int i = 0; i < 3; i++) {
rfa1.doAppend("Hello---" + i);
addExpectedFileNamedIfItsTime_ByDate(fileNamePatternStr);
incCurrentTime(500);
tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
add(tbrp1.compressionFuture);
add(tbrp1.cleanUpFuture);
}
rfa1.stop();
waitForJobsToComplete();
if (waitDuration != NO_RESTART) {
doRestart(testId, patternPrefix, filenameFunction, waitDuration);
}
waitForJobsToComplete();
massageExpectedFilesToCorresponToCurrentTarget(testId, filenameFunction);
// StatusPrinter.print(context);
rolloverChecker.check(expectedFilenameList);
}
void defaultTest(String testId, String patternPrefix, String compressionSuffix,
UnaryOperator<String> filenameFunction, int waitDuration) throws IOException {
boolean withCompression = compressionSuffix.length() > 0;
rolloverChecker = new DefaultRolloverChecker(testId, withCompression, compressionSuffix);
genericTest(testId, patternPrefix, compressionSuffix, filenameFunction, waitDuration);
}
void doRestart(String testId, String patternPart, UnaryOperator<String> filenameFunction, int waitDuration) {
// change the timestamp of the currently actively file
File activeFile = new File(rfa1.getFile());
activeFile.setLastModified(currentTime);
incCurrentTime(waitDuration);
String filePatternStr = randomOutputDir + patternPart + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}";
String fileName = filenameFunction.apply(testId);
initRFA(rfa2, fileName);
initTRBP(rfa2, tbrp2, filePatternStr, currentTime);
for (int i = 0; i < 3; i++) {
rfa2.doAppend("World---" + i);
addExpectedFileNamedIfItsTime_ByDate(filePatternStr);
incCurrentTime(100);
tbrp2.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
add(tbrp2.compressionFuture);
add(tbrp1.cleanUpFuture);
}
rfa2.stop();
}
@Test
public void noCompression_FileBlank_NoRestart_1() throws IOException {
defaultTest("test1", "test1", "", this::nullFileName, NO_RESTART);
}
@Test
public void withCompression_FileBlank_NoRestart_2() throws IOException {
defaultTest("test2", "test2", ".gz", this::nullFileName, NO_RESTART);
}
@Test
public void noCompression_FileBlank_StopRestart_3() throws IOException {
defaultTest("test3", "test3", "", this::nullFileName, WITH_RESTART);
}
@Test
public void noCompression_FileSet_StopRestart_4() throws IOException {
defaultTest("test4", "test4", "", this::testId2FileName, WITH_RESTART);
}
@Test
public void noCompression_FileSet_StopRestart_WithLongWait_4B() throws IOException {
defaultTest("test4B", "test4B", "", this::testId2FileName, WITH_RESTART_AND_LONG_WAIT);
}
@Test
public void noCompression_FileSet_NoRestart_5() throws IOException {
defaultTest("test5", "test5", "", this::testId2FileName, NO_RESTART);
}
@Test
public void withCompression_FileSet_NoRestart_6() throws IOException {
defaultTest("test6", "test6", ".gz", this::testId2FileName, NO_RESTART);
}
// LOGBACK-168
@Test
public void withMissingTargetDirWithCompression() throws IOException {
defaultTest("test7", "%d{yyyy-MM-dd, aux}/test7", ".gz", this::testId2FileName, NO_RESTART);
}
@Test
public void withMissingTargetDirWithZipCompression() throws IOException {
defaultTest("test8", "%d{yyyy-MM-dd, aux}/test8", ".zip", this::testId2FileName, NO_RESTART);
}
@Test
public void failed_rename() throws IOException {
if (!EnvUtilForTests.isWindows())
return;
String testId = "failed_rename";
FileOutputStream fos = null;
try {
String fileName = testId2FileName(testId);
File file = new File(fileName);
file.getParentFile().mkdirs();
fos = new FileOutputStream(fileName);
rolloverChecker = new ZRolloverChecker(testId);
genericTest(testId, "failed_rename", "", this::testId2FileName, NO_RESTART);
rolloverChecker.check(expectedFilenameList);
} finally {
StatusPrinter.print(context);
if (fos != null)
fos.close();
}
}
// @Test
// public void failed_rename2() throws IOException {
//
// String testId = "failed_rename";
// try {
// String fileName = testId2FileName(testId);
//
//
// rolloverChecker = new ZRolloverChecker(testId);
// genericTest(testId, "test10", ".gz", this::testId2FileName, NO_RESTART);
// rolloverChecker.check(expectedFilenameList);
//
// } finally {
// StatusPrinter.print(context);
//
// }
// }
}