1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.rolling;
15
16 import static ch.qos.logback.core.CoreConstants.DAILY_DATE_PATTERN;
17 import static ch.qos.logback.core.CoreConstants.STRICT_ISO8601_PATTERN;
18 import static org.junit.jupiter.api.Assertions.assertEquals;
19 import static org.junit.jupiter.api.Assertions.assertTrue;
20
21 import java.io.File;
22 import java.io.FileFilter;
23 import java.time.Instant;
24 import java.time.LocalDateTime;
25 import java.time.ZoneId;
26 import java.time.ZonedDateTime;
27 import java.time.format.DateTimeFormatter;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.Date;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Set;
36 import java.util.concurrent.atomic.LongAdder;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 import java.time.temporal.ChronoUnit;
40
41 import ch.qos.logback.core.util.StatusPrinter;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Disabled;
44 import org.junit.jupiter.api.Test;
45
46 import ch.qos.logback.core.CoreConstants;
47 import ch.qos.logback.core.pattern.SpacePadder;
48 import ch.qos.logback.core.rolling.helper.RollingCalendar;
49 import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests;
50 import ch.qos.logback.core.status.testUtil.StatusChecker;
51 import ch.qos.logback.core.util.FileSize;
52 import ch.qos.logback.core.util.FixedRateInvocationGate;
53
54 public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRollingTests {
55 String MONTHLY_DATE_PATTERN = "yyyy-MM";
56 String MONTHLY_CRONOLOG_DATE_PATTERN = "yyyy/MM";
57 final String DAILY_CRONOLOG_DATE_PATTERN = "yyyy/MM/dd";
58
59 RollingFileAppender<Object> rfa = new RollingFileAppender<Object>();
60 TimeBasedRollingPolicy<Object> tbrp = new TimeBasedRollingPolicy<Object>();
61
62 DateTimeFormatter STRICT_DATE_PARSER = DateTimeFormatter.ofPattern(STRICT_ISO8601_PATTERN);
63
64
65
66 TimeBasedFileNamingAndTriggeringPolicy<Object> tbfnatp = new DefaultTimeBasedFileNamingAndTriggeringPolicy<Object>();
67
68 StatusChecker checker = new StatusChecker(context);
69
70 static long MILLIS_IN_MINUTE = 60 * 1000;
71 static long MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE;
72 static long MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
73 static long MILLIS_IN_MONTH = (long) ((365.242199 / 12) * MILLIS_IN_DAY);
74 static int MONTHS_IN_YEAR = 12;
75
76 public static final String DAILY_HOUR_PATTERN = "yyyy-MM-dd-HH";
77
78
79 static final long WED_2016_03_23_T_230705_CET = 1458770825333L;
80 static final long THU_2016_03_17_T_230330_CET = 1458252210975L;
81
82 int slashCount = 0;
83 int ticksPerPeriod = 216;
84
85 ConfigParameters cp;
86 FixedRateInvocationGate fixedRateInvocationGate = new FixedRateInvocationGate(ticksPerPeriod / 2);
87
88 @BeforeEach
89 public void setUp() {
90 super.setUp();
91 this.cp = new ConfigParameters(currentTime);
92 }
93
94 private int computeSlashCount(String datePattern) {
95 if (datePattern == null)
96 return 0;
97 else {
98 int count = 0;
99 for (int i = 0; i < datePattern.length(); i++) {
100 char c = datePattern.charAt(i);
101 if (c == '/')
102 count++;
103 }
104 return count;
105 }
106 }
107
108
109
110
111 @Test
112 public void monthlyRolloverOverManyPeriods() {
113 this.slashCount = computeSlashCount(MONTHLY_CRONOLOG_DATE_PATTERN);
114 int maxHistory = 2;
115 int simulatedNumberOfPeriods = 30;
116 String fileNamePattern = randomOutputDir + "/%d{" + MONTHLY_CRONOLOG_DATE_PATTERN + "}/clean.txt.zip";
117
118 cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods)
119 .periodDurationInMillis(MILLIS_IN_MONTH);
120
121 long startTime = currentTime;
122 long endTime = logOverMultiplePeriods(cp);
123 System.out.println("randomOutputDir:" + randomOutputDir);
124 System.out.println("start:" + startTime + ", end=" + endTime);
125 int differenceInMonths = RollingCalendar.diffInMonths(startTime, endTime);
126 System.out.println("differenceInMonths:" + differenceInMonths);
127 Calendar startTimeAsCalendar = Calendar.getInstance();
128 startTimeAsCalendar.setTimeInMillis(startTime);
129 int indexOfStartPeriod = startTimeAsCalendar.get(Calendar.MONTH);
130 boolean withExtraFolder = extraFolder(differenceInMonths, MONTHS_IN_YEAR, indexOfStartPeriod, maxHistory);
131
132 checkFileCount(expectedCountWithFolders(maxHistory, withExtraFolder));
133 }
134
135 long generateDailyRollover(ConfigParameters cp) {
136 this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
137 cp.fileNamePattern(randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt");
138 return logOverMultiplePeriods(cp);
139 }
140
141 long generateDailyRolloverAndCheckFileCount(ConfigParameters cp) {
142 long millisAtEnd = generateDailyRollover(cp);
143 int periodBarriersCrossed = computeCrossedDayBarriers(currentTime, millisAtEnd);
144
145 checkFileCount(expectedCountWithoutFoldersWithInactivity(cp.maxHistory, periodBarriersCrossed,
146 cp.startInactivity + cp.numInactivityPeriods));
147 return millisAtEnd;
148 }
149
150 @Test
151 public void checkCrossedPeriodsWithDSTBarrier() {
152 long SAT_2016_03_26_T_230705_CET = WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY;
153 long MON_2016_03_28_T_000705_CET = SAT_2016_03_26_T_230705_CET + CoreConstants.MILLIS_IN_ONE_DAY;
154
155 long result = computeCrossedDayBarriers(SAT_2016_03_26_T_230705_CET, MON_2016_03_28_T_000705_CET, "CET");
156 assertEquals(2, result);
157 }
158
159 private int computeCrossedDayBarriers(long currentTime, long millisAtEnd) {
160 return computeCrossedDayBarriers(currentTime, millisAtEnd, null);
161 }
162
163 private int computeCrossedDayBarriers(long currentTime, long millisAtEnd, String timeZoneID) {
164 ZoneId dateTimeZone = ZoneId.systemDefault();
165 if (timeZoneID != null) {
166 dateTimeZone = ZoneId.of(timeZoneID);
167 }
168
169 Instant startInstant = Instant.ofEpochMilli(currentTime);
170 ZonedDateTime startZDT = startInstant.atZone(dateTimeZone);
171
172 ZonedDateTime startZDT0 = startZDT.truncatedTo(ChronoUnit.DAYS);
173
174 Instant endInstant = Instant.ofEpochMilli(millisAtEnd);
175 ZonedDateTime endZDT = endInstant.atZone(dateTimeZone);
176
177 ZonedDateTime endZDT0 = endZDT.truncatedTo(ChronoUnit.DAYS);
178
179
180 long dayCount = ChronoUnit.DAYS.between(startZDT0, endZDT0);
181 return (int) dayCount;
182 }
183
184 @Test
185 public void checkCleanupForBasicDailyRollover() {
186 cp.maxHistory(20).simulatedNumberOfPeriods(20 * 3).startInactivity(0).numInactivityPeriods(0);
187 generateDailyRolloverAndCheckFileCount(cp);
188 }
189
190 @Test
191 public void checkCleanupForBasicDailyRolloverWithSizeCap() {
192 long bytesOutputPerPeriod = 15984;
193 int sizeInUnitsOfBytesPerPeriod = 2;
194
195 cp.maxHistory(5).simulatedNumberOfPeriods(10)
196 .sizeCap(sizeInUnitsOfBytesPerPeriod * bytesOutputPerPeriod + 1000);
197 generateDailyRollover(cp);
198 checkFileCount(sizeInUnitsOfBytesPerPeriod + 1);
199 }
200
201 @Test
202 public void checkThatSmallTotalSizeCapLeavesAtLeastOneArhcive() {
203 long WED_2016_03_23_T_131345_CET = WED_2016_03_23_T_230705_CET - 10 * CoreConstants.MILLIS_IN_ONE_HOUR;
204
205
206
207 cp = new ConfigParameters(WED_2016_03_23_T_131345_CET);
208 final int verySmallCapSize = 1;
209 cp.maxHistory(5).simulatedNumberOfPeriods(3).sizeCap(verySmallCapSize);
210 generateDailyRollover(cp);
211
212 checkFileCountAtMost(1);
213
214 }
215
216 @Test
217 public void checkCleanupForBasicDailyRolloverWithMaxSize() {
218 cp.maxHistory(6).simulatedNumberOfPeriods(30).startInactivity(10).numInactivityPeriods(1);
219 generateDailyRolloverAndCheckFileCount(cp);
220 }
221
222
223
224
225 @Test
226 public void checkCleanupForDailyRollover_15Periods() {
227 cp.maxHistory(5).simulatedNumberOfPeriods(15).startInactivity(6).numInactivityPeriods(3);
228 generateDailyRolloverAndCheckFileCount(cp);
229 }
230
231 @Test
232 public void checkCleanupForDailyRolloverWithInactivity_30Periods() {
233
234 cp.maxHistory(2).simulatedNumberOfPeriods(30).startInactivity(3).numInactivityPeriods(1);
235 generateDailyRolloverAndCheckFileCount(cp);
236 }
237
238 @Test
239 public void checkCleanupForDailyRolloverWithInactivity_10Periods() {
240 this.currentTime = THU_2016_03_17_T_230330_CET;
241 cp.maxHistory(6).simulatedNumberOfPeriods(10).startInactivity(2).numInactivityPeriods(2);
242 generateDailyRolloverAndCheckFileCount(cp);
243 }
244
245 @Test
246 public void checkCleanupForDailyRolloverWithSecondPhase() {
247 slashCount = computeSlashCount(DAILY_DATE_PATTERN);
248 int maxHistory = 5;
249 String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
250
251 ConfigParameters cp0 = new ConfigParameters(currentTime).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
252 .simulatedNumberOfPeriods(maxHistory * 2);
253 long endTime = logOverMultiplePeriods(cp0);
254
255 ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 10).maxHistory(maxHistory)
256 .fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(maxHistory);
257 logOverMultiplePeriods(cp1);
258 checkFileCount(expectedCountWithoutFolders(maxHistory));
259 }
260
261 @Test
262 public void dailyRolloverWithCronologPattern() {
263 this.slashCount = computeSlashCount(DAILY_CRONOLOG_DATE_PATTERN);
264 String fileNamePattern = randomOutputDir + "/%d{" + DAILY_CRONOLOG_DATE_PATTERN + "}/clean.txt.zip";
265 cp.maxHistory(8).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(8 * 3);
266 logOverMultiplePeriods(cp);
267 int expectedDirMin = 9 + slashCount;
268 int expectDirMax = expectedDirMin + 1 + 1;
269 expectedFileAndDirCount(9, expectedDirMin, expectDirMax);
270 }
271
272 @Test
273 public void dailySizeBasedRolloverWithoutCap() {
274 SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object>();
275
276
277 sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
278 tbfnatp = sizeAndTimeBasedFNATP;
279 this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
280 String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i.zip";
281 cp.maxHistory(5).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(5 * 4);
282 logOverMultiplePeriods(cp);
283 checkPatternCompliance(5 + 1 + slashCount, "\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)(.zip)?");
284 }
285
286 @Test
287 public void dailySizeBasedRolloverWithSizeCap() {
288 SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object>();
289
290 long fileSize = 3400;
291 int expectedFileCount = 10;
292 long sizeCap = expectedFileCount * fileSize;
293 sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(fileSize));
294 tbfnatp = sizeAndTimeBasedFNATP;
295 this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
296
297
298
299 long simulatedTime = getSimulatedTimeFromString("2016-03-05T00:14:39,186");
300
301 ConfigParameters params = new ConfigParameters(simulatedTime);
302 String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i";
303 params.maxHistory(60).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(10).sizeCap(sizeCap);
304 logOverMultiplePeriods(params);
305
306 List<File> foundFiles = findFilesByPattern("\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)");
307 Collections.sort(foundFiles, new Comparator<File>() {
308 public int compare(File f0, File f1) {
309 String s0 = f0.getName().toString();
310 String s1 = f1.getName().toString();
311 return s0.compareTo(s1);
312 }
313 });
314
315 StatusPrinter.print(context);
316 foundFiles.forEach(f -> System.out.println(""+f+ " "+f.length()));
317 LongAdder la = new LongAdder();
318 foundFiles.forEach(f -> la.add(f.length()));
319 System.out.println("Sum: "+la.sum());
320
321
322 assertTrue(la.sum() < sizeCap);
323
324 checkFileCount(expectedFileCount + 1);
325 }
326
327
328
329
330
331
332 private long getSimulatedTimeFromString(String dateStr) {
333 LocalDateTime localDateTime = LocalDateTime.parse(dateStr, STRICT_DATE_PARSER);
334 long simulatedTime = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
335 return simulatedTime;
336 }
337
338 @Test
339 public void dailyChronologSizeBasedRollover() {
340 SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object> sizeAndTimeBasedFileNamingAndTriggeringPolicy = new SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object>();
341 sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize(new FileSize(10000));
342
343 tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy;
344 slashCount = 1;
345 String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i.zip";
346 cp.maxHistory(5).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(5 * 3);
347 logOverMultiplePeriods(cp);
348 checkDirPatternCompliance(6);
349 }
350
351 @Test
352 public void dailyChronologSizeBasedRolloverWithSecondPhase() {
353 SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object> sizeAndTimeBasedFileNamingAndTriggeringPolicy = new SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object>();
354 sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize(new FileSize(10000));
355
356 tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy;
357 this.slashCount = 1;
358 String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i";
359 int maxHistory = 5;
360 cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(3);
361 long endTime = logOverMultiplePeriods(cp);
362
363 int simulatedNumberOfPeriods = maxHistory * 4;
364 ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 7).maxHistory(maxHistory)
365 .fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods);
366 logOverMultiplePeriods(cp1);
367 checkDirPatternCompliance(maxHistory + 1);
368 }
369
370 void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis) {
371 ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern)
372 .maxHistory(maxHistory);
373 buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
374 rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
375 currentTime += durationInMillis / 2;
376 add(tbrp.compressionFuture);
377 add(tbrp.cleanUpFuture);
378 waitForJobsToComplete();
379 tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
380 rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
381 rfa.stop();
382 }
383
384
385 @Test
386 public void cleanHistoryOnStartWithHourPattern() {
387 long simulatedTime = WED_2016_03_23_T_230705_CET;
388 String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_HOUR_PATTERN + "}.txt";
389 int maxHistory = 3;
390 for (int i = 0; i <= 5; i++) {
391 logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
392 simulatedTime += MILLIS_IN_HOUR;
393 }
394 checkFileCount(expectedCountWithoutFolders(maxHistory));
395 }
396
397 @Disabled
398 @Test
399
400
401
402
403
404
405
406 public void cleanHistoryOnStartWithHourPatternWithCollisions() {
407 long now = this.currentTime;
408 String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
409 int maxHistory = 3;
410 for (int i = 0; i <= 5; i++) {
411 logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY);
412 now = now + MILLIS_IN_HOUR;
413 }
414 checkFileCount(expectedCountWithoutFolders(maxHistory));
415 }
416
417 @Test
418 public void cleanHistoryOnStartWithDayPattern() {
419 long simulatedTime = WED_2016_03_23_T_230705_CET;
420 String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
421 int maxHistory = 3;
422 for (int i = 0; i <= 5; i++) {
423 logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY);
424 simulatedTime += MILLIS_IN_DAY;
425 }
426 checkFileCount(expectedCountWithoutFolders(maxHistory));
427 }
428
429 @Test
430 public void cleanHistoryOnStartWithHourDayPattern() {
431 long simulatedTime = WED_2016_03_23_T_230705_CET;
432 String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
433 int maxHistory = 3;
434 for (int i = 0; i <= 5; i++) {
435 logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
436 simulatedTime += MILLIS_IN_HOUR;
437 }
438 checkFileCount(expectedCountWithoutFolders(maxHistory));
439 }
440
441 int expectedCountWithoutFolders(int maxHistory) {
442 return maxHistory + 1;
443 }
444
445 int expectedCountWithFolders(int maxHistory, boolean withExtraFolder) {
446 int numLogFiles = (maxHistory + 1);
447 int numLogFilesAndFolders = numLogFiles * 2;
448 int result = numLogFilesAndFolders + slashCount;
449 if (withExtraFolder)
450 result += 1;
451 return result;
452 }
453
454 void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
455 rfa.setContext(context);
456 rfa.setEncoder(encoder);
457 tbrp.setContext(context);
458 tbrp.setFileNamePattern(cp.fileNamePattern);
459 tbrp.setMaxHistory(cp.maxHistory);
460 tbrp.setTotalSizeCap(new FileSize(cp.sizeCap));
461 tbrp.setParent(rfa);
462 tbrp.setCleanHistoryOnStart(cleanHistoryOnStart);
463 tbrp.timeBasedFileNamingAndTriggeringPolicy = tbfnatp;
464 tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(cp.simulatedTime);
465 tbrp.start();
466 rfa.setRollingPolicy(tbrp);
467 rfa.start();
468 }
469
470 boolean DO_CLEAN_HISTORY_ON_START = true;
471 boolean DO_NOT_CLEAN_HISTORY_ON_START = false;
472
473 long logOverMultiplePeriods(ConfigParameters cp) {
474
475 buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
476
477 int runLength = cp.simulatedNumberOfPeriods * ticksPerPeriod;
478 int startInactivityIndex = cp.startInactivity * ticksPerPeriod;
479 int endInactivityIndex = startInactivityIndex + cp.numInactivityPeriods * ticksPerPeriod;
480 long tickDuration = cp.periodDurationInMillis / ticksPerPeriod;
481
482 for (int i = 0; i <= runLength; i++) {
483 Date currentDate = new Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
484 if (i < startInactivityIndex || i > endInactivityIndex) {
485 rfa.doAppend(buildMessageString(currentDate, i));
486 }
487
488 tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(
489 addTime(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime(), tickDuration));
490
491 add(tbrp.compressionFuture);
492 add(tbrp.cleanUpFuture);
493 waitForJobsToComplete();
494 }
495
496 try {
497 Thread.sleep(100);
498 } catch (InterruptedException e) {
499
500 e.printStackTrace();
501 }
502 rfa.stop();
503
504
505
506 return tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime();
507 }
508
509 private static String buildMessageString(Date currentDate, int i) {
510 StringBuilder sb = new StringBuilder("Hello");
511 String currentDateStr = currentDate.toString();
512 String iAsString = Integer.toString(i);
513 sb.append(currentDateStr);
514 SpacePadder.spacePad(sb, 68 + (3 - iAsString.length() - currentDateStr.length() - CoreConstants.LINE_SEPARATOR_LEN));
515 sb.append(iAsString);
516 return sb.toString();
517 }
518
519 void fillWithChar(StringBuffer sb, char c, int count) {
520 for (int i = 0; i < count; i++) {
521 sb.append(c);
522 }
523 }
524
525 boolean extraFolder(int numPeriods, int periodsPerEra, int beginPeriod, int maxHistory) {
526 int valueOfLastMonth = ((beginPeriod) + numPeriods) % periodsPerEra;
527 return (valueOfLastMonth < maxHistory);
528 }
529
530 long addTime(long time, long timeToWait) {
531 return time + timeToWait;
532 }
533
534 void expectedFileAndDirCount(int expectedFileAndDirCount, int expectedDirCountMin, int expectedDirCountMax) {
535 File dir = new File(randomOutputDir);
536 List<File> fileList = new ArrayList<File>();
537 findFilesInFolderRecursivelyByPatterMatch(dir, fileList, "clean");
538 List<File> dirList = new ArrayList<File>();
539 findAllFoldersInFolderRecursively(dir, dirList);
540 String msg = "expectedDirCountMin=" + expectedDirCountMin + ", expectedDirCountMax=" + expectedDirCountMax
541 + " actual value=" + dirList.size();
542 assertTrue(expectedDirCountMin <= dirList.size() && dirList.size() <= expectedDirCountMax, msg);
543 }
544
545 void checkFileCount(int expectedCount) {
546 File dir = new File(randomOutputDir);
547 List<File> fileList = new ArrayList<File>();
548 findAllDirsOrStringContainsFilesRecursively(dir, fileList, "clean");
549 assertEquals(expectedCount, fileList.size());
550 }
551
552 void checkFileCountAtMost(int expectedCount) {
553 File dir = new File(randomOutputDir);
554 List<File> fileList = new ArrayList<File>();
555 findAllDirsOrStringContainsFilesRecursively(dir, fileList, "clean");
556 int fileListSize = fileList.size();
557
558 assertTrue(fileListSize <= expectedCount, "file list size " + fileListSize + ", expectedCount=" + expectedCount);
559 }
560
561 int expectedCountWithoutFoldersWithInactivity(int maxHistory, int totalPeriods, int endOfInactivity) {
562 int availableHistory = (totalPeriods + 1) - endOfInactivity;
563 int actualHistory = Math.min(availableHistory, maxHistory + 1);
564 return actualHistory;
565 }
566
567 void genericFindMatching(final FileMatchFunction matchFunc, File dir, List<File> fileList, final String pattern,
568 boolean includeDirs) {
569 if (dir.isDirectory()) {
570 File[] matchArray = dir.listFiles(new FileFilter() {
571 public boolean accept(File f) {
572 return f.isDirectory() || matchFunc.match(f, pattern);
573 }
574 });
575 for (File f : matchArray) {
576 if (f.isDirectory()) {
577 if (includeDirs)
578 fileList.add(f);
579 genericFindMatching(matchFunc, f, fileList, pattern, includeDirs);
580 } else
581 fileList.add(f);
582 }
583 }
584 }
585
586 private void findAllFoldersInFolderRecursively(File dir, List<File> fileList) {
587 FileMatchFunction alwaysFalse = new FileMatchFunction() {
588 public boolean match(File f, String pattern) {
589 return false;
590 }
591 };
592 genericFindMatching(alwaysFalse, dir, fileList, null, true);
593 }
594
595 private void findAllDirsOrStringContainsFilesRecursively(File dir, List<File> fileList, String pattern) {
596 FileMatchFunction matchFunction = new FileMatchFunction() {
597 public boolean match(File f, String pattern) {
598 return f.getName().contains(pattern);
599 }
600 };
601 genericFindMatching(matchFunction, dir, fileList, pattern, true);
602 }
603
604 void findFilesInFolderRecursivelyByPatterMatch(File dir, List<File> fileList, String pattern) {
605 FileMatchFunction matchByPattern = new FileMatchFunction() {
606 public boolean match(File f, String pattern) {
607 return f.getName().matches(pattern);
608 }
609 };
610 genericFindMatching(matchByPattern, dir, fileList, pattern, false);
611 }
612
613 Set<String> groupByClass(List<File> fileList, String regex) {
614 Pattern p = Pattern.compile(regex);
615 Set<String> set = new HashSet<String>();
616 for (File f : fileList) {
617 String n = f.getName();
618 Matcher m = p.matcher(n);
619 m.matches();
620 int begin = m.start(1);
621 String reduced = n.substring(0, begin);
622 set.add(reduced);
623 }
624 return set;
625 }
626
627 void checkPatternCompliance(int expectedClassCount, String regex) {
628 Set<String> set = findFilesByPatternClass(regex);
629 assertEquals(expectedClassCount, set.size());
630 }
631
632 private List<File> findFilesByPattern(String regex) {
633 File dir = new File(randomOutputDir);
634 List<File> fileList = new ArrayList<File>();
635 findFilesInFolderRecursivelyByPatterMatch(dir, fileList, regex);
636 return fileList;
637 }
638
639 private Set<String> findFilesByPatternClass(String regex) {
640 List<File> fileList = findFilesByPattern(regex);
641 Set<String> set = groupByClass(fileList, regex);
642 return set;
643 }
644
645 void checkDirPatternCompliance(int expectedClassCount) {
646 File dir = new File(randomOutputDir);
647 List<File> fileList = new ArrayList<File>();
648 findAllFoldersInFolderRecursively(dir, fileList);
649 for (File f : fileList) {
650 assertTrue(f.list().length >= 1);
651 }
652 assertEquals(expectedClassCount, fileList.size());
653 }
654 }