View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.archivers.zip;
21  
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertSame;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  
29  import java.math.BigInteger;
30  import java.time.Instant;
31  import java.time.LocalDateTime;
32  import java.time.ZoneId;
33  import java.util.Calendar;
34  import java.util.Date;
35  
36  import org.junit.jupiter.api.BeforeEach;
37  import org.junit.jupiter.api.Test;
38  
39  class ZipUtilTest {
40  
41      static void assertDosDate(final long value, final int year, final int month, final int day, final int hour, final int minute, final int second) {
42          int pos = 0;
43          assertEquals(year - 1980, (int) (value << pos) >>> 32 - 7);
44          assertEquals(month, (int) (value << (pos += 7)) >>> 32 - 4);
45          assertEquals(day, (int) (value << (pos += 4)) >>> 32 - 5);
46          assertEquals(hour, (int) (value << (pos += 5)) >>> 32 - 5);
47          assertEquals(minute, (int) (value << (pos += 5)) >>> 32 - 6);
48          assertEquals(second, (int) (value << pos + 6) >>> 32 - 5 << 1); // DOS dates only store even seconds
49      }
50  
51      static Instant toLocalInstant(final String date) {
52          return LocalDateTime.parse(date).atZone(ZoneId.systemDefault()).toInstant();
53      }
54  
55      private Date time;
56  
57      private ZipLong zl;
58  
59      @BeforeEach
60      public void setUp() throws Exception {
61          time = new Date();
62          final Calendar cal = Calendar.getInstance();
63          cal.setTime(time);
64          final int year = cal.get(Calendar.YEAR);
65          final int month = cal.get(Calendar.MONTH) + 1;
66          // @formatter:off
67          final long value = year - 1980 << 25
68              | month << 21
69              | cal.get(Calendar.DAY_OF_MONTH) << 16
70              | cal.get(Calendar.HOUR_OF_DAY) << 11
71              | cal.get(Calendar.MINUTE) << 5
72              | cal.get(Calendar.SECOND) >> 1;
73          // @formatter:on
74  
75          final byte[] result = new byte[4];
76          result[0] = (byte) (value & 0xFF);
77          result[1] = (byte) ((value & 0xFF00) >> 8);
78          result[2] = (byte) ((value & 0xFF0000) >> 16);
79          result[3] = (byte) ((value & 0xFF000000L) >> 24);
80          zl = new ZipLong(result);
81      }
82  
83      @Test
84      void testAdjustToLong() {
85          assertEquals(Integer.MAX_VALUE, ZipUtil.adjustToLong(Integer.MAX_VALUE));
86          assertEquals((long) Integer.MAX_VALUE + 1, ZipUtil.adjustToLong(Integer.MAX_VALUE + 1));
87          assertEquals(2 * (long) Integer.MAX_VALUE, ZipUtil.adjustToLong(2 * Integer.MAX_VALUE));
88      }
89  
90      @Test
91      void testBigToLong() {
92          final BigInteger big1 = BigInteger.valueOf(1);
93          final BigInteger big2 = BigInteger.valueOf(Long.MAX_VALUE);
94          final BigInteger big3 = BigInteger.valueOf(Long.MIN_VALUE);
95  
96          assertEquals(1L, ZipUtil.toLong(big1));
97          assertEquals(Long.MAX_VALUE, ZipUtil.toLong(big2));
98          assertEquals(Long.MIN_VALUE, ZipUtil.toLong(big3));
99  
100         final BigInteger big4 = big2.add(big1);
101         assertThrows(IllegalArgumentException.class, () -> ZipUtil.toLong(big4), "Should have thrown IllegalArgumentException");
102 
103         final BigInteger big5 = big3.subtract(big1);
104         assertThrows(IllegalArgumentException.class, () -> ZipUtil.toLong(big5),
105                 "ZipUtil.bigToLong(BigInteger) should have thrown IllegalArgumentException");
106     }
107 
108     @Test
109     void testFromDosTime() {
110         ZipLong testDosTime = new ZipLong(1 << 21);
111         final Calendar cal = Calendar.getInstance();
112         cal.set(Calendar.YEAR, 1980);
113         cal.set(Calendar.MONTH, 0);
114         cal.set(Calendar.DATE, 0);
115         cal.set(Calendar.HOUR_OF_DAY, 0);
116         cal.set(Calendar.MINUTE, 0);
117         cal.set(Calendar.SECOND, 0);
118         cal.set(Calendar.MILLISECOND, 0);
119         Date testDate = ZipUtil.fromDosTime(testDosTime);
120         assertEquals(testDate.getTime(), cal.getTime().getTime());
121 
122         testDosTime = ZipUtil.toDosTime(time);
123         testDate = ZipUtil.fromDosTime(testDosTime);
124         // the minimal time unit for dos time is 2 seconds
125         assertEquals(testDate.getTime() / 2000, time.getTime() / 2000);
126     }
127 
128     @Test
129     void testInsideCalendar() {
130         final long date = toLocalInstant("1985-02-01T09:00:00").toEpochMilli();
131         final byte[] b1 = ZipUtil.toDosTime(date);
132         assertEquals(0, b1[0]);
133         assertEquals(72, b1[1]);
134         assertEquals(65, b1[2]);
135         assertEquals(10, b1[3]);
136     }
137 
138     @Test
139     void testInsideCalendar_bigValue() {
140         final long date = toLocalInstant("2097-11-27T23:59:59").toEpochMilli();
141         final long value = ZipLong.getValue(ZipUtil.toDosTime(date));
142         assertDosDate(value, 2097, 11, 27, 23, 59, 58); // DOS dates only store even seconds
143     }
144 
145     @Test
146     void testInsideCalendar_long() {
147         final long date = toLocalInstant("1985-02-01T09:00:00").toEpochMilli();
148         final long value = ZipLong.getValue(ZipUtil.toDosTime(date));
149         assertDosDate(value, 1985, 2, 1, 9, 0, 0);
150     }
151 
152     @Test
153     void testInsideCalendar_modernDate() {
154         final long date = toLocalInstant("2022-12-27T16:18:23").toEpochMilli();
155         final long value = ZipLong.getValue(ZipUtil.toDosTime(date));
156         assertDosDate(value, 2022, 12, 27, 16, 18, 22); // DOS dates only store even seconds
157     }
158 
159     @Test
160     void testIsDosTime() {
161         assertFalse(ZipUtil.isDosTime(toLocalInstant("1975-01-31T23:00:00").toEpochMilli()));
162         assertTrue(ZipUtil.isDosTime(toLocalInstant("1980-01-03T00:00:00").toEpochMilli()));
163         assertTrue(ZipUtil.isDosTime(toLocalInstant("2097-11-27T00:00:00").toEpochMilli()));
164         assertFalse(ZipUtil.isDosTime(toLocalInstant("2099-01-01T00:00:00").toEpochMilli()));
165         // The lowest data/time expressable as DOS Time, see comment in ZipUtil#DOSTIME_BEFORE_1980
166         final long lowestExpressableDosTime = 1 << 21 | 1 << 16; // 0x210000
167         assertTrue(ZipUtil.isDosTime(ZipUtil.dosToJavaTime(lowestExpressableDosTime)));
168     }
169 
170     @Test
171     void testLongToBig() {
172         final long l0 = 0;
173         final long l1 = 1;
174         final long l2 = -1;
175         final long l3 = Integer.MIN_VALUE;
176         final long l4 = Long.MAX_VALUE;
177         final long l5 = Long.MIN_VALUE;
178 
179         final BigInteger big0 = ZipUtil.longToBig(l0);
180         final BigInteger big1 = ZipUtil.longToBig(l1);
181         final BigInteger big2 = ZipUtil.longToBig(l2);
182         final BigInteger big3 = ZipUtil.longToBig(l3);
183         final BigInteger big4 = ZipUtil.longToBig(l4);
184 
185         assertEquals(0, big0.longValue());
186         assertEquals(1, big1.longValue());
187         assertEquals(0xFFFFFFFFL, big2.longValue());
188         assertEquals(0x80000000L, big3.longValue());
189         assertEquals(Long.MAX_VALUE, big4.longValue());
190 
191         assertThrows(IllegalArgumentException.class, () -> ZipUtil.longToBig(l5), "ZipUtil.longToBig(long) should have thrown IllegalArgumentException");
192     }
193 
194     @Test
195     void testMinTime() {
196         final byte[] b1 = ZipUtil.toDosTime(0);
197         final byte b10 = b1[0]; // Save the first byte
198         b1[0]++; // change it
199         final byte[] b2 = ZipUtil.toDosTime(0); // get the same time
200         assertEquals(b10, b2[0]); // first byte should still be the same
201     }
202 
203     @Test
204     void testOutsideCalendar() {
205         final long date = toLocalInstant("1975-01-31T23:00:00").toEpochMilli();
206         final byte[] b1 = ZipUtil.toDosTime(date);
207         assertEquals(0, b1[0]);
208         assertEquals(0, b1[1]);
209         assertEquals(33, b1[2]);
210         assertEquals(0, b1[3]);
211     }
212 
213     @Test
214     void testOutsideCalendar_long() {
215         final long date = toLocalInstant("1975-01-31T23:00:00").toEpochMilli();
216         final long value = ZipLong.getValue(ZipUtil.toDosTime(date));
217         assertDosDate(value, 1980, 1, 1, 0, 0, 0);
218     }
219 
220     @Test
221     void testReverse() {
222         final byte[][] bTest = new byte[6][];
223         bTest[0] = new byte[] {};
224         bTest[1] = new byte[] { 1 };
225         bTest[2] = new byte[] { 1, 2 };
226         bTest[3] = new byte[] { 1, 2, 3 };
227         bTest[4] = new byte[] { 1, 2, 3, 4 };
228         bTest[5] = new byte[] { 1, 2, 3, 4, 5 };
229 
230         final byte[][] rTest = new byte[6][];
231         rTest[0] = new byte[] {};
232         rTest[1] = new byte[] { 1 };
233         rTest[2] = new byte[] { 2, 1 };
234         rTest[3] = new byte[] { 3, 2, 1 };
235         rTest[4] = new byte[] { 4, 3, 2, 1 };
236         rTest[5] = new byte[] { 5, 4, 3, 2, 1 };
237 
238         assertEquals(bTest.length, rTest.length, "test and result arrays are same length");
239 
240         for (int i = 0; i < bTest.length; i++) {
241             final byte[] result = ZipUtil.reverse(bTest[i]);
242             assertSame(bTest[i], result, "reverse mutates in-place");
243             assertArrayEquals(rTest[i], result, "reverse actually reverses");
244         }
245     }
246 
247     @Test
248     void testSignedByteToUnsignedInt() {
249         // Yay, we can completely test all possible input values in this case!
250         int expectedVal = 128;
251         for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
252             final byte b = (byte) i;
253             assertEquals(expectedVal, Byte.toUnsignedInt(b));
254             expectedVal++;
255             if (expectedVal == 256) {
256                 expectedVal = 0;
257             }
258         }
259     }
260 
261     @Test
262     void testUnknownMethod() throws Exception {
263         final ZipArchiveEntry ze = new ZipArchiveEntry();
264         ze.setMethod(100);
265         assertThrows(UnsupportedZipFeatureException.class, () -> ZipUtil.checkRequestedFeatures(ze));
266     }
267 
268     @Test
269     void testUnsignedIntToSignedByte() {
270         int unsignedVal = 128;
271         for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
272             final byte expectedVal = (byte) i;
273             assertEquals(expectedVal, ZipUtil.unsignedIntToSignedByte(unsignedVal));
274             unsignedVal++;
275             if (unsignedVal == 256) {
276                 unsignedVal = 0;
277             }
278         }
279 
280         assertThrows(IllegalArgumentException.class, () -> ZipUtil.unsignedIntToSignedByte(-1),
281                 "ZipUtil.unsignedIntToSignedByte(-1) should have thrown IllegalArgumentException");
282 
283         assertThrows(IllegalArgumentException.class, () -> ZipUtil.unsignedIntToSignedByte(256),
284                 "ZipUtil.unsignedIntToSignedByte(256) should have thrown IllegalArgumentException");
285     }
286 
287     @Test
288     void testUnsupportedMethod() throws Exception {
289         final ZipArchiveEntry ze = new ZipArchiveEntry();
290         ze.setMethod(ZipMethod.EXPANDING_LEVEL_1.getCode());
291         assertThrows(UnsupportedZipFeatureException.class, () -> ZipUtil.checkRequestedFeatures(ze));
292     }
293 
294     @Test
295     void testZipLong() {
296         final ZipLong test = ZipUtil.toDosTime(time);
297         assertEquals(test.getValue(), zl.getValue());
298     }
299 }