Make ZipEntryTest time-insensitive

Fix ZipEntryTest to not depend on the current system time.

The ZIP format represents timestamps outside of DOS time
(before 1980) via an extra field.

ZipEntryTest.testCommentAndExtraInSameOrder() asserts that
the extra field can be read back unchanged, but since it
defaulted to the current system time, this check failed
when the system time was before 1980.

This CL fixes the test by explicitly setting a timestamp
in 2010 on the new entry. It also adds new tests for the
behavior difference before/after 1980.

Bug: 37696493
Test: cts-tradefed run cts -m CtsLibcoreTestCases -t \
libcore.java.util.zip.ZipEntryTest
Test: cts-tradefed run cts -m CtsLibcoreTestCases -t \
libcore.java.util.zip.ZipOutputStreamTest
Change-Id: I8024c9dcb23179a69e1805e66c6b051f1a358800
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
index bf10c3e..f58c8aa 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
@@ -21,16 +21,24 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
 import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
 public class ZipEntryTest extends junit.framework.TestCase {
+  // The zip format differentiates between times before and after 1/1/1980. A timestamp before 1980
+  // will produce a different zip binary. ZipOutputStream.putNextEntry defaults the entry times to
+  // the current system clock value. This time can be used explicitly to ensure the behavior of most
+  // tests is independent of the system clock.
+  private static final long ENTRY_TIME = 1262304000000L; //  January 1, 2010 12:00:00 AM GMT
+
   private static File createTemporaryZipFile() throws IOException {
     File result = File.createTempFile("ZipFileTest", "zip");
     result.deleteOnExit();
@@ -134,6 +142,7 @@
     ZipOutputStream out = createZipOutputStream(f);
     ZipEntry ze = new ZipEntry("x");
     ze.setSize(0);
+    ze.setTime(ENTRY_TIME);
     ze.setExtra(maxLengthExtra);
     out.putNextEntry(ze);
     out.closeEntry();
@@ -145,6 +154,41 @@
     zipFile.close();
   }
 
+  public void testSetTime() throws Exception {
+    // Set a time before the lower bound of dos time, year 1980
+    checkSetTime(0L); // January 1, 1970 12:00:00 AM GMT
+    checkSetTime(31536000000L); // January 1, 1971 12:00:00 AM GMT
+    checkSetTime(315187200000L); // December 28, 1979 12:00:00 AM GMT
+    // December 31, 1979 11:59:59 AM Local time
+    checkSetTime(LocalDate.of(1980, 1, 1).atStartOfDay().minus(1, ChronoUnit.SECONDS)
+                    .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
+
+    // January 1, 1980 12:00:00 AM Local time
+    checkSetTime(LocalDate.of(1980, 1, 1).atStartOfDay().atZone(ZoneId.systemDefault())
+            .toInstant().toEpochMilli());
+    // Set a time after the lower bound of dos time, year 1980
+    checkSetTime(315705600000L); // January 3, 1980 12:00:00 AM GMT
+    checkSetTime(ENTRY_TIME); // January 1, 2010 12:00:00 AM
+
+    // Set a time after upper bound of dos time.
+    checkSetTime(4134153600000L); // January 3, 2101 12:00:00 AM GMT
+  }
+
+  private static void checkSetTime(long time) throws IOException {
+    File f = createTemporaryZipFile();
+    ZipOutputStream out = createZipOutputStream(f);
+    ZipEntry ze = new ZipEntry("x");
+    ze.setSize(0);
+    ze.setTime(time);
+    out.putNextEntry(ze);
+    out.closeEntry();
+    out.close();
+
+    // Read it back, and check that we see the entry.
+    ZipFile zipFile = new ZipFile(f);
+    assertEquals(time, zipFile.getEntry("x").getTime());
+    zipFile.close();
+  }
 
   // TODO: This test does not compile because we need to add a ZipOutputStream constructor
   // that forces zip64. This also needs followup changes in ZipInputStream et al. to assume zip64
@@ -206,6 +250,7 @@
     // Regular (non zip64) format.
     ZipEntry ze = new ZipEntry("x");
     ze.setSize(0);
+    ze.setTime(ENTRY_TIME);
     ze.setExtra(extra);
     ze.setComment(comment);
     out.putNextEntry(ze);
@@ -213,6 +258,7 @@
 
     // An entry without a length is assumed to be zip64.
     ze = new ZipEntry("y");
+    ze.setTime(ENTRY_TIME);
     ze.setExtra(extra);
     ze.setComment(comment);
     out.putNextEntry(ze);
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
index 4e72874..ddac57e 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -17,6 +17,7 @@
 package libcore.java.util.zip;
 
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -25,6 +26,8 @@
 import java.util.Random;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 import libcore.junit.junit3.TestCaseWithRules;
 import libcore.junit.util.ResourceLeakageDetector;
@@ -98,4 +101,30 @@
             out.finish();
         }
     }
+
+    /**
+     * Test {@link ZipOutputStream#putNextEntry(ZipEntry)} that the current time will be used
+     * if the entry has no set modification time.
+     */
+    public void testPutNextEntryUsingCurrentTime() throws IOException {
+        long timeBeforeZip = System.currentTimeMillis();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ZipOutputStream out = new ZipOutputStream(bos)) {
+            ZipEntry entryWithoutExplicitTime = new ZipEntry("name");
+            // We do not set a time on the entry. We expect ZipOutputStream to use the current
+            // system clock value.
+            out.putNextEntry(entryWithoutExplicitTime);
+            out.closeEntry();
+            out.finish();
+        }
+        long timeAfterZip = System.currentTimeMillis();
+
+        // Read it back, and check the modification time is almost the system clock value
+        try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            ZipEntry entry = zis.getNextEntry();
+            assertEquals("name", entry.getName());
+            assertTrue(timeBeforeZip <= entry.getTime());
+            assertTrue(timeAfterZip >= entry.getTime());
+        }
+    }
 }