1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
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    *
9    *   or (per the licensee's choosing)
10   *
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.helper;
15  
16  import java.time.Instant;
17  import java.util.Date;
18  import java.util.Locale;
19  import java.util.TimeZone;
20  
21  import org.junit.jupiter.api.*;
22  
23  import ch.qos.logback.core.CoreConstants;
24  import ch.qos.logback.core.util.EnvUtil;
25  
26  import static org.junit.jupiter.api.Assertions.assertEquals;
27  
28  public class RollingCalendarTest {
29  
30      String dailyPattern = "yyyy-MM-dd";
31  
32  
33      @BeforeEach
34      public void setUp() {
35  
36          // Due to fist day of week differences, tests may fail
37          // certain locales, namely GB.
38          //
39          // These tests are:
40          //
41          // checkCollisionFreeness("yyyy-WW", false);
42          // checkCollisionFreeness("yyyy-ww", true);
43          // checkCollisionFreeness("ww", false);
44          // {
45          // RollingCalendar rc = new RollingCalendar("yyyy-ww");
46          // assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
47          // }
48          //
49      }
50  
51      void set_EN_US_Locale() {
52          Locale usEn_Locale = Locale.forLanguageTag("en-US");
53          Locale.setDefault(usEn_Locale);
54      }
55  
56      @AfterEach
57      public void tearDown() {
58  
59      }
60  
61      @Test
62      public void testPeriodicity() {
63          {
64              RollingCalendar rc = new RollingCalendar("yyyy-MM-dd_HH_mm_ss");
65              assertEquals(PeriodicityType.TOP_OF_SECOND, rc.getPeriodicityType());
66          }
67  
68          {
69              RollingCalendar rc = new RollingCalendar("yyyy-MM-dd_HH_mm");
70              assertEquals(PeriodicityType.TOP_OF_MINUTE, rc.getPeriodicityType());
71          }
72  
73          {
74              RollingCalendar rc = new RollingCalendar("yyyy-MM-dd_HH");
75              assertEquals(PeriodicityType.TOP_OF_HOUR, rc.getPeriodicityType());
76          }
77  
78          {
79              RollingCalendar rc = new RollingCalendar("yyyy-MM-dd_hh");
80              assertEquals(PeriodicityType.TOP_OF_HOUR, rc.getPeriodicityType());
81          }
82  
83          {
84              RollingCalendar rc = new RollingCalendar("yyyy-MM-dd");
85              assertEquals(PeriodicityType.TOP_OF_DAY, rc.getPeriodicityType());
86          }
87  
88          {
89              RollingCalendar rc = new RollingCalendar("yyyy-MM");
90              assertEquals(PeriodicityType.TOP_OF_MONTH, rc.getPeriodicityType());
91          }
92  
93          {
94              RollingCalendar rc = new RollingCalendar("yyyy-ww");
95              assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
96          }
97  
98          {
99              RollingCalendar rc = new RollingCalendar("yyyy-W");
100             assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
101         }
102     }
103 
104     @Test
105     public void testVaryingNumberOfHourlyPeriods() {
106         RollingCalendar rc = new RollingCalendar("yyyy-MM-dd_HH");
107 
108         long MILLIS_IN_HOUR = 3600 * 1000;
109 
110         for (int p = 100; p > -100; p--) {
111             long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008
112             Instant result = rc.getEndOfNextNthPeriod(Instant.ofEpochMilli(now), p);
113             long expected = now - (now % (MILLIS_IN_HOUR)) + p * MILLIS_IN_HOUR;
114             assertEquals(expected, result.toEpochMilli());
115         }
116     }
117 
118     @Test
119     public void testVaryingNumberOfDailyPeriods() {
120         RollingCalendar rc = new RollingCalendar("yyyy-MM-dd");
121         final long MILLIS_IN_DAY = 24 * 3600 * 1000;
122 
123         for (int p = 20; p > -100; p--) {
124             long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008
125             Instant nowInstant = Instant.ofEpochMilli(now);
126             Instant result = rc.getEndOfNextNthPeriod(nowInstant, p);
127             long offset = rc.getTimeZone().getRawOffset() + rc.getTimeZone().getDSTSavings();
128 
129             long origin = now - ((now + offset) % (MILLIS_IN_DAY));
130             long expected = origin + p * MILLIS_IN_DAY;
131             assertEquals(expected, result.toEpochMilli(), "p=" + p);
132         }
133     }
134 
135     // Wed Mar 23 23:07:05 CET 2016
136     final long WED_2016_03_23_T_230705_CET = 1458770825333L;
137 
138     @Test
139     public void testBarrierCrossingComputation() {
140         checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmmss", WED_2016_03_23_T_230705_CET,
141                 WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_SECOND, 3);
142         checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmm", WED_2016_03_23_T_230705_CET,
143                 WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_MINUTE, 3);
144         checkPeriodBarriersCrossed("yyyy-MM-dd'T'HH", WED_2016_03_23_T_230705_CET,
145                 WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_HOUR, 3);
146         checkPeriodBarriersCrossed("yyyy-MM-dd", WED_2016_03_23_T_230705_CET,
147                 WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY, 3);
148     }
149 
150     private void checkPeriodBarriersCrossed(String pattern, long start, long end, int count) {
151         RollingCalendar rc = new RollingCalendar(pattern);
152         assertEquals(count, rc.periodBarriersCrossed(start, end));
153     }
154 
155     @Test
156     public void testCollisionFreenes() {
157         // hourly
158         checkCollisionFreeness("yyyy-MM-dd hh", false);
159         checkCollisionFreeness("yyyy-MM-dd hh a", true);
160 
161         checkCollisionFreeness("yyyy-MM-dd HH", true);
162         checkCollisionFreeness("yyyy-MM-dd kk", true);
163 
164         checkCollisionFreeness("yyyy-MM-dd KK", false);
165         checkCollisionFreeness("yyyy-MM-dd KK a", true);
166 
167         // daily
168         checkCollisionFreeness("yyyy-MM-dd", true);
169         checkCollisionFreeness("yyyy-dd", false);
170         checkCollisionFreeness("dd", false);
171         checkCollisionFreeness("MM-dd", false);
172 
173         checkCollisionFreeness("yyyy-DDD", true);
174         checkCollisionFreeness("DDD", false);
175 
176         // 'u' is new to JDK 7
177 //        if (EnvUtil.isJDK7OrHigher()) {
178 //            checkCollisionFreeness("yyyy-MM-dd-uu", true);
179 //            checkCollisionFreeness("yyyy-MM-uu", false);
180 //        }
181 
182 
183         Locale oldLocale = Locale.getDefault();
184         try {
185             set_EN_US_Locale();
186             // weekly
187             checkCollisionFreeness("yyyy-MM-W", true);
188             dumpCurrentLocale(Locale.getDefault());
189             checkCollisionFreeness("yyyy-W", false);
190             checkCollisionFreeness("yyyy-ww", true);
191             checkCollisionFreeness("ww", false);
192         } finally {
193             if(oldLocale != null)
194                 Locale.setDefault(oldLocale);
195         }
196     }
197 
198     private void dumpCurrentLocale(Locale locale) {
199         System.out.println("***Current default locale is " + locale);
200 
201     }
202 
203     private void checkCollisionFreeness(String pattern, boolean expected) {
204         RollingCalendar rc = new RollingCalendar(pattern);
205         assertEquals(expected, rc.isCollisionFree());
206     }
207 
208     @Test
209     public void basicPeriodBarriersCrossed() {
210         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
211         // Thu Jan 26 19:46:58 CET 2017, GMT offset = -1h
212         long start = 1485456418969L;
213         // Fri Jan 27 19:46:58 CET 2017, GMT offset = -1h
214         long end = start + CoreConstants.MILLIS_IN_ONE_DAY;
215         assertEquals(1, rc.periodBarriersCrossed(start, end));
216     }
217 
218     @Test
219     public void testPeriodBarriersCrossedWhenGoingIntoDaylightSaving() {
220         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
221         // Sun Mar 26 00:02:03 CET 2017, GMT offset = -1h
222         long start = 1490482923333L;
223         // Mon Mar 27 00:02:03 CEST 2017, GMT offset = -2h
224         long end = 1490565723333L;
225 
226         assertEquals(1, rc.periodBarriersCrossed(start, end));
227     }
228 
229     @Test
230     public void testPeriodBarriersCrossedWhenLeavingDaylightSaving() {
231         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
232         // Sun Oct 29 00:02:03 CEST 2017, GMT offset = -2h
233         long start = 1509228123333L;// 1490482923333L+217*CoreConstants.MILLIS_IN_ONE_DAY-CoreConstants.MILLIS_IN_ONE_HOUR;
234         // Mon Oct 30 00:02:03 CET 2017, GMT offset = -1h
235         long end = 1509228123333L + 25 * CoreConstants.MILLIS_IN_ONE_HOUR;
236         assertEquals(1, rc.periodBarriersCrossed(start, end));
237     }
238 
239     @Test
240     public void testPeriodBarriersCrossedJustBeforeEnteringDaylightSaving() {
241         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
242         // Sun Mar 26 22:18:38 CEST 2017, GMT offset = +2h
243         long start = 1490559518333L;
244         System.out.println(new Date(start));
245 
246         // Mon Mar 27 00:05:18 CEST 2017, GMT offset = +2h
247         long end = 1490565918333L;
248         System.out.println(new Date(end));
249         assertEquals(1, rc.periodBarriersCrossed(start, end));
250 
251     }
252 }