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