/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2015, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.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 static ch.qos.logback.core.CoreConstants.DAILY_DATE_PATTERN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.time.DateTimeZone;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.pattern.SpacePadder;
import ch.qos.logback.core.rolling.helper.RollingCalendar;
import ch.qos.logback.core.status.StatusChecker;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.FixedRateInvocationGate;
import ch.qos.logback.core.util.StatusPrinter;
public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRollingTests {
String MONTHLY_DATE_PATTERN = "yyyy-MM";
String MONTHLY_CRONOLOG_DATE_PATTERN = "yyyy/MM";
final String DAILY_CRONOLOG_DATE_PATTERN = "yyyy/MM/dd";
RollingFileAppender<Object> rfa = new RollingFileAppender<Object>();
TimeBasedRollingPolicy<Object> tbrp = new TimeBasedRollingPolicy<Object>();
// by default tbfnatp is an instance of DefaultTimeBasedFileNamingAndTriggeringPolicy
TimeBasedFileNamingAndTriggeringPolicy<Object> tbfnatp = new DefaultTimeBasedFileNamingAndTriggeringPolicy<Object>();
StatusChecker checker = new StatusChecker(context);
static long MILLIS_IN_MINUTE = 60 * 1000;
static long MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE;
static long MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
static long MILLIS_IN_MONTH = (long) ((365.242199 / 12) * MILLIS_IN_DAY);
static int MONTHS_IN_YEAR = 12;
// Wed Mar 23 23:07:05 CET 2016
static final long WED_2016_03_23_T_230705_CET = 1458770825333L;
static final long THU_2016_03_17_T_230330_CET = 1458252210975L;
int slashCount = 0;
int ticksPerPeriod = 216;
ConfigParameters cp; // initialized in setup
FixedRateInvocationGate fixedRateInvocationGate = new FixedRateInvocationGate(ticksPerPeriod / 2);
@Before
public void setUp() {
super.setUp();
this.cp = new ConfigParameters(currentTime);
}
private int computeSlashCount(String datePattern) {
if (datePattern == null)
return 0;
else {
int count = 0;
for (int i = 0; i < datePattern.length(); i++) {
char c = datePattern.charAt(i);
if (c == '/')
count++;
}
return count;
}
}
// test that the number of files at the end of the test is same as the expected number taking into account end dates
// near the beginning of a new year. This test has been run in a loop with start date varying over a two years
// with success.
@Test
public void monthlyRolloverOverManyPeriods() {
this.slashCount = computeSlashCount(MONTHLY_CRONOLOG_DATE_PATTERN);
int maxHistory = 2;
int simulatedNumberOfPeriods = 30;
String fileNamePattern = randomOutputDir + "/%d{" + MONTHLY_CRONOLOG_DATE_PATTERN + "}/clean.txt.zip";
cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods).periodDurationInMillis(MILLIS_IN_MONTH);
long startTime = currentTime;
long endTime = logOverMultiplePeriods(cp);
System.out.println("randomOutputDir:" + randomOutputDir);
System.out.println("start:" + startTime + ", end=" + endTime);
int differenceInMonths = RollingCalendar.diffInMonths(startTime, endTime);
System.out.println("differenceInMonths:" + differenceInMonths);
Calendar startTimeAsCalendar = Calendar.getInstance();
startTimeAsCalendar.setTimeInMillis(startTime);
int indexOfStartPeriod = startTimeAsCalendar.get(Calendar.MONTH);
boolean withExtraFolder = extraFolder(differenceInMonths, MONTHS_IN_YEAR, indexOfStartPeriod, maxHistory);
checkFileCount(expectedCountWithFolders(maxHistory, withExtraFolder));
}
long generateDailyRollover(ConfigParameters cp) {
this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
cp.fileNamePattern(randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt");
return logOverMultiplePeriods(cp);
}
long generateDailyRolloverAndCheckFileCount(ConfigParameters cp) {
long millisAtEnd = generateDailyRollover(cp);
int periodBarriersCrossed = computeCrossedDayBarriers(currentTime, millisAtEnd);
System.out.println("**** periodBarriersCrossed=" + periodBarriersCrossed);
checkFileCount(expectedCountWithoutFoldersWithInactivity(cp.maxHistory, periodBarriersCrossed, cp.startInactivity + cp.numInactivityPeriods));
return millisAtEnd;
}
@Test
public void checkCrossedPeriodsWithDSTBarrier() {
long SAT_2016_03_26_T_230705_CET = WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY;
System.out.println("SAT_2016_03_26_T_230705_CET " + new Date(SAT_2016_03_26_T_230705_CET));
long MON_2016_03_28_T_000705_CET = SAT_2016_03_26_T_230705_CET + CoreConstants.MILLIS_IN_ONE_DAY;
System.out.println("MON_2016_03_28_T_000705_CET " + new Date(MON_2016_03_28_T_000705_CET));
int result = computeCrossedDayBarriers(SAT_2016_03_26_T_230705_CET, MON_2016_03_28_T_000705_CET, "CET");
assertEquals(2, result);
}
private int computeCrossedDayBarriers(long currentTime, long millisAtEnd) {
return computeCrossedDayBarriers(currentTime, millisAtEnd, null);
}
private int computeCrossedDayBarriers(long currentTime, long millisAtEnd, String timeZoneID) {
DateTimeZone dateTimeZone = DateTimeZone.getDefault();
if (timeZoneID != null) {
dateTimeZone = DateTimeZone.forID(timeZoneID);
}
LocalDate startInstant = new LocalDate(currentTime, dateTimeZone);
LocalDate endInstant = new LocalDate(millisAtEnd, dateTimeZone);
Days days = Days.daysBetween(startInstant, endInstant);
return days.getDays();
}
@Test
public void checkCleanupForBasicDailyRollover() {
cp.maxHistory(20).simulatedNumberOfPeriods(20 * 3).startInactivity(0).numInactivityPeriods(0);
generateDailyRolloverAndCheckFileCount(cp);
}
@Test
public void checkCleanupForBasicDailyRolloverWithSizeCap() {
long bytesOutputPerPeriod = 15984;
int sizeInUnitsOfBytesPerPeriod = 2;
cp.maxHistory(5).simulatedNumberOfPeriods(10).sizeCap(sizeInUnitsOfBytesPerPeriod * bytesOutputPerPeriod + 1000);
generateDailyRollover(cp);
StatusPrinter.print(context);
checkFileCount(sizeInUnitsOfBytesPerPeriod + 1);
}
@Test
public void checkThatSmallTotalSizeCapLeavesAtLeastOneArhcive() {
long WED_2016_03_23_T_131345_CET = WED_2016_03_23_T_230705_CET - 10 * CoreConstants.MILLIS_IN_ONE_HOUR;
// long bytesOutputPerPeriod = 15984;
cp = new ConfigParameters(WED_2016_03_23_T_131345_CET);
final int verySmallCapSize = 1;
cp.maxHistory(5).simulatedNumberOfPeriods(3).sizeCap(verySmallCapSize);
generateDailyRollover(cp);
StatusPrinter.print(context);
checkFileCountAtMost(1);
}
@Test
public void checkCleanupForBasicDailyRolloverWithMaxSize() {
cp.maxHistory(6).simulatedNumberOfPeriods(30).startInactivity(10).numInactivityPeriods(1);
generateDailyRolloverAndCheckFileCount(cp);
}
// Since the duration of a month (in seconds) varies from month to month, tests with inactivity period must
// be conducted with daily rollover not monthly
@Test
public void checkCleanupForDailyRollover_15Periods() {
cp.maxHistory(5).simulatedNumberOfPeriods(15).startInactivity(6).numInactivityPeriods(3);
generateDailyRolloverAndCheckFileCount(cp);
}
@Test
public void checkCleanupForDailyRolloverWithInactivity_30Periods() {
// / -------
cp.maxHistory(2).simulatedNumberOfPeriods(30).startInactivity(3).numInactivityPeriods(1);
generateDailyRolloverAndCheckFileCount(cp);
}
@Test
public void checkCleanupForDailyRolloverWithInactivity_10Periods() {
this.currentTime = THU_2016_03_17_T_230330_CET;
cp.maxHistory(6).simulatedNumberOfPeriods(10).startInactivity(2).numInactivityPeriods(2);
generateDailyRolloverAndCheckFileCount(cp);
}
@Test
public void checkCleanupForDailyRolloverWithSecondPhase() {
slashCount = computeSlashCount(DAILY_DATE_PATTERN);
int maxHistory = 5;
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
ConfigParameters cp0 = new ConfigParameters(currentTime).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
.simulatedNumberOfPeriods(maxHistory * 2);
long endTime = logOverMultiplePeriods(cp0);
ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 10).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
.simulatedNumberOfPeriods(maxHistory);
logOverMultiplePeriods(cp1);
checkFileCount(expectedCountWithoutFolders(maxHistory));
}
@Test
public void dailyRolloverWithCronologPattern() {
this.slashCount = computeSlashCount(DAILY_CRONOLOG_DATE_PATTERN);
String fileNamePattern = randomOutputDir + "/%d{" + DAILY_CRONOLOG_DATE_PATTERN + "}/clean.txt.zip";
cp.maxHistory(8).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(8 * 3);
logOverMultiplePeriods(cp);
int expectedDirMin = 9 + slashCount;
int expectDirMax = expectedDirMin + 1 + 1;
expectedFileAndDirCount(9, expectedDirMin, expectDirMax);
}
@Test
public void dailySizeBasedRolloverWithoutCap() {
SizeAndTimeBasedFNATP<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<Object>();
sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
tbfnatp = sizeAndTimeBasedFNATP;
this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i.zip";
cp.maxHistory(5).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(5 * 4);
logOverMultiplePeriods(cp);
checkPatternCompliance(5 + 1 + slashCount, "\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)(.zip)?");
}
@Test
public void dailySizeBasedRolloverWithSizeCap() {
SizeAndTimeBasedFNATP<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<Object>();
sizeAndTimeBasedFNATP.invocationGate = new FixedRateInvocationGate(ticksPerPeriod / 8);
long bytesPerPeriod = 17000;
long fileSize = (bytesPerPeriod) / 5;
int expectedFileCount = 10;
long sizeCap = expectedFileCount * fileSize;
sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(fileSize));
tbfnatp = sizeAndTimeBasedFNATP;
this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
// 2016-03-05 00:14:39 CET
long simulatedTime = 1457133279186L;
ConfigParameters params = new ConfigParameters(simulatedTime);
String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i";
params.maxHistory(60).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(10).sizeCap(sizeCap);
logOverMultiplePeriods(params);
List<File> foundFiles = findFilesByPattern("\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)");
Collections.sort(foundFiles, new Comparator<File>() {
public int compare(File f0, File f1) {
String s0 = f0.getName().toString();
String s1 = f1.getName().toString();
return s0.compareTo(s1);
}
});
System.out.print(foundFiles);
StatusPrinter.print(context);
checkFileCount(expectedFileCount - 1);
}
@Test
public void dailyChronologSizeBasedRollover() {
SizeAndTimeBasedFNATP<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<Object>();
sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
tbfnatp = sizeAndTimeBasedFNATP;
slashCount = 1;
String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i.zip";
cp.maxHistory(5).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(5 * 3);
logOverMultiplePeriods(cp);
checkDirPatternCompliance(6);
}
@Test
public void dailyChronologSizeBasedRolloverWithSecondPhase() {
SizeAndTimeBasedFNATP<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<Object>();
sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
tbfnatp = sizeAndTimeBasedFNATP;
this.slashCount = 1;
String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i";
int maxHistory = 5;
cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(3);
long endTime = logOverMultiplePeriods(cp);
int simulatedNumberOfPeriods = maxHistory * 4;
ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 7).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
.simulatedNumberOfPeriods(simulatedNumberOfPeriods);
logOverMultiplePeriods(cp1);
checkDirPatternCompliance(maxHistory + 1);
}
void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory) {
ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern).maxHistory(maxHistory);
buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
currentTime += MILLIS_IN_DAY / 2;
add(tbrp.compressionFuture);
add(tbrp.cleanUpFuture);
waitForJobsToComplete();
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
rfa.stop();
}
@Test
public void cleanHistoryOnStart() {
long simulatedTime = WED_2016_03_23_T_230705_CET;
System.out.println(new Date(simulatedTime));
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory);
simulatedTime += MILLIS_IN_DAY;
}
StatusPrinter.print(context);
checkFileCount(expectedCountWithoutFolders(maxHistory));
}
@Test
public void cleanHistoryOnStartWithDayPattern() {
long simulatedTime = WED_2016_03_23_T_230705_CET;
String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory);
simulatedTime += MILLIS_IN_DAY;
}
StatusPrinter.print(context);
checkFileCount(expectedCountWithoutFolders(maxHistory));
}
@Ignore
@Test
// this test assumes a high degree of collisions in the archived files. Every 24 hours, the archive
// belonging to the previous day will be overwritten. Given that logback goes 14 days (336 hours) in history
// to clean files on start up, it is bound to delete more recent files. It is not logback's responsibility
// to cater for such degenerate cases.
public void cleanHistoryOnStartWithHourPattern() {
long now = this.currentTime;
String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(now, fileNamePattern, maxHistory);
now = now + MILLIS_IN_HOUR;
}
StatusPrinter.print(context);
checkFileCount(expectedCountWithoutFolders(maxHistory));
}
int expectedCountWithoutFolders(int maxHistory) {
return maxHistory + 1;
}
int expectedCountWithFolders(int maxHistory, boolean withExtraFolder) {
int numLogFiles = (maxHistory + 1);
int numLogFilesAndFolders = numLogFiles * 2;
int result = numLogFilesAndFolders + slashCount;
if (withExtraFolder)
result += 1;
return result;
}
void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
rfa.setContext(context);
rfa.setEncoder(encoder);
tbrp.setContext(context);
tbrp.setFileNamePattern(cp.fileNamePattern);
tbrp.setMaxHistory(cp.maxHistory);
tbrp.setTotalSizeCap(new FileSize(cp.sizeCap));
tbrp.setParent(rfa);
tbrp.setCleanHistoryOnStart(cleanHistoryOnStart);
tbrp.timeBasedFileNamingAndTriggeringPolicy = tbfnatp;
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(cp.simulatedTime);
tbrp.start();
rfa.setRollingPolicy(tbrp);
rfa.start();
}
boolean DO_CLEAN_HISTORY_ON_START = true;
boolean DO_NOT_CLEAN_HISTORY_ON_START = false;
long logOverMultiplePeriods(ConfigParameters cp) {
buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
int runLength = cp.simulatedNumberOfPeriods * ticksPerPeriod;
int startInactivityIndex = cp.startInactivity * ticksPerPeriod;
int endInactivityIndex = startInactivityIndex + cp.numInactivityPeriods * ticksPerPeriod;
long tickDuration = cp.periodDurationInMillis / ticksPerPeriod;
System.out.println("cp.periodDurationInMillis=" + cp.periodDurationInMillis + ", tickDuration=:" + tickDuration + ", runLength=" + runLength);
for (int i = 0; i <= runLength; i++) {
Date currentDate = new Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
if (i < startInactivityIndex || i > endInactivityIndex) {
StringBuilder sb = new StringBuilder("Hello");
String currentDateStr = currentDate.toString();
String iAsString = Integer.toString(i);
sb.append(currentDateStr);
SpacePadder.spacePad(sb, 66 + (3 - iAsString.length() - currentDateStr.length()));
sb.append(iAsString);
rfa.doAppend(sb.toString());
}
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(addTime(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime(), tickDuration));
add(tbrp.compressionFuture);
add(tbrp.cleanUpFuture);
waitForJobsToComplete();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rfa.stop();
System.out.println("Current time at end of loop: "+new Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()));
return tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime();
}
void fillWithChar(StringBuffer sb, char c, int count) {
for (int i = 0; i < count; i++) {
sb.append(c);
}
}
boolean extraFolder(int numPeriods, int periodsPerEra, int beginPeriod, int maxHistory) {
int valueOfLastMonth = ((beginPeriod) + numPeriods) % periodsPerEra;
return (valueOfLastMonth < maxHistory);
}
long addTime(long time, long timeToWait) {
return time + timeToWait;
}
void expectedFileAndDirCount(int expectedFileAndDirCount, int expectedDirCountMin, int expectedDirCountMax) {
File dir = new File(randomOutputDir);
List<File> fileList = new ArrayList<File>();
findFilesInFolderRecursivelyByPatterMatch(dir, fileList, "clean");
List<File> dirList = new ArrayList<File>();
findAllFoldersInFolderRecursively(dir, dirList);
String msg = "expectedDirCountMin=" + expectedDirCountMin + ", expectedDirCountMax=" + expectedDirCountMax + " actual value=" + dirList.size();
assertTrue(msg, expectedDirCountMin <= dirList.size() && dirList.size() <= expectedDirCountMax);
}
void checkFileCount(int expectedCount) {
File dir = new File(randomOutputDir);
List<File> fileList = new ArrayList<File>();
findAllDirsOrStringContainsFilesRecursively(dir, fileList, "clean");
assertEquals(expectedCount, fileList.size());
}
void checkFileCountAtMost(int expectedCount) {
File dir = new File(randomOutputDir);
List<File> fileList = new ArrayList<File>();
findAllDirsOrStringContainsFilesRecursively(dir, fileList, "clean");
int fileListSize = fileList.size();
assertTrue("file list size "+ fileListSize+", expectedCount="+expectedCount, fileListSize <= expectedCount);
}
int expectedCountWithoutFoldersWithInactivity(int maxHistory, int totalPeriods, int endOfInactivity) {
int availableHistory = (totalPeriods + 1) - endOfInactivity;
int actualHistory = Math.min(availableHistory, maxHistory + 1);
return actualHistory;
}
void genericFindMatching(final FileMatchFunction matchFunc, File dir, List<File> fileList, final String pattern, boolean includeDirs) {
if (dir.isDirectory()) {
File[] matchArray = dir.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory() || matchFunc.match(f, pattern);
}
});
for (File f : matchArray) {
if (f.isDirectory()) {
if (includeDirs)
fileList.add(f);
genericFindMatching(matchFunc, f, fileList, pattern, includeDirs);
} else
fileList.add(f);
}
}
}
private void findAllFoldersInFolderRecursively(File dir, List<File> fileList) {
FileMatchFunction alwaysFalse = new FileMatchFunction() {
public boolean match(File f, String pattern) {
return false;
}
};
genericFindMatching(alwaysFalse, dir, fileList, null, true);
}
private void findAllDirsOrStringContainsFilesRecursively(File dir, List<File> fileList, String pattern) {
FileMatchFunction matchFunction = new FileMatchFunction() {
public boolean match(File f, String pattern) {
return f.getName().contains(pattern);
}
};
genericFindMatching(matchFunction, dir, fileList, pattern, true);
}
void findFilesInFolderRecursivelyByPatterMatch(File dir, List<File> fileList, String pattern) {
FileMatchFunction matchByPattern = new FileMatchFunction() {
public boolean match(File f, String pattern) {
return f.getName().matches(pattern);
}
};
genericFindMatching(matchByPattern, dir, fileList, pattern, false);
}
Set<String> groupByClass(List<File> fileList, String regex) {
Pattern p = Pattern.compile(regex);
Set<String> set = new HashSet<String>();
for (File f : fileList) {
String n = f.getName();
Matcher m = p.matcher(n);
m.matches();
int begin = m.start(1);
String reduced = n.substring(0, begin);
set.add(reduced);
}
return set;
}
void checkPatternCompliance(int expectedClassCount, String regex) {
Set<String> set = findFilesByPatternClass(regex);
assertEquals(expectedClassCount, set.size());
}
private List<File> findFilesByPattern(String regex) {
File dir = new File(randomOutputDir);
List<File> fileList = new ArrayList<File>();
findFilesInFolderRecursivelyByPatterMatch(dir, fileList, regex);
return fileList;
}
private Set<String> findFilesByPatternClass(String regex) {
List<File> fileList = findFilesByPattern(regex);
Set<String> set = groupByClass(fileList, regex);
return set;
}
void checkDirPatternCompliance(int expectedClassCount) {
File dir = new File(randomOutputDir);
List<File> fileList = new ArrayList<File>();
findAllFoldersInFolderRecursively(dir, fileList);
for (File f : fileList) {
assertTrue(f.list().length >= 1);
}
assertEquals(expectedClassCount, fileList.size());
}
}