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