|
1 | 1 | /* |
2 | | - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
|
22 | 22 | */ |
23 | 23 |
|
24 | 24 | /* @test |
25 | | - @bug 4028605 4109069 4234207 4401122 |
26 | | - @summary Make sure ZipInputStream/InflaterInputStream.available() will |
27 | | - return 0 after EOF has reached and 1 otherwise. |
28 | | - */ |
| 25 | + * @bug 4028605 4109069 4234207 4401122 8339154 |
| 26 | + * @summary Verify that ZipInputStream, InflaterInputStream, ZipFileInputStream, |
| 27 | + * ZipFileInflaterInputStream.available() return values according |
| 28 | + * to their specification or long-standing behavior |
| 29 | + * @run junit Available |
| 30 | + */ |
29 | 31 |
|
| 32 | +import org.junit.jupiter.api.AfterEach; |
| 33 | +import org.junit.jupiter.api.BeforeEach; |
| 34 | +import org.junit.jupiter.api.Test; |
| 35 | +import org.junit.jupiter.params.ParameterizedTest; |
| 36 | +import org.junit.jupiter.params.provider.ValueSource; |
30 | 37 |
|
31 | 38 | import java.io.*; |
| 39 | +import java.nio.charset.StandardCharsets; |
| 40 | +import java.nio.file.Files; |
| 41 | +import java.nio.file.Path; |
32 | 42 | import java.util.zip.*; |
33 | 43 |
|
| 44 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
| 45 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
| 46 | + |
34 | 47 | public class Available { |
35 | 48 |
|
36 | | - public static void main(String[] args) throws Exception { |
37 | | - // 4028605 4109069 4234207 |
38 | | - test1(); |
39 | | - // test 4401122 |
40 | | - test2(); |
41 | | - } |
| 49 | + // ZIP file produced in this test |
| 50 | + private final Path zip = Path.of("available.jar"); |
42 | 51 |
|
43 | | - private static void test1() throws Exception { |
44 | | - File f = new File(System.getProperty("test.src", "."), "input.jar"); |
| 52 | + /** |
| 53 | + * Create the ZIP file used in this test, containing |
| 54 | + * one deflated and one stored entry. |
| 55 | + * |
| 56 | + * @throws IOException if an unexpected error occurs |
| 57 | + */ |
| 58 | + @BeforeEach |
| 59 | + public void setup() throws IOException { |
| 60 | + byte[] contents = "contents".repeat(10).getBytes(StandardCharsets.UTF_8); |
45 | 61 |
|
46 | | - // test ZipInputStream |
47 | | - try (FileInputStream fis = new FileInputStream(f); |
48 | | - ZipInputStream z = new ZipInputStream(fis)) |
49 | | - { |
50 | | - z.getNextEntry(); |
51 | | - tryAvail(z); |
52 | | - } |
| 62 | + try (ZipOutputStream zo = new ZipOutputStream(Files.newOutputStream(zip))) { |
| 63 | + // First entry uses DEFLATE method |
| 64 | + zo.putNextEntry(new ZipEntry("deflated.txt")); |
| 65 | + zo.write(contents); |
53 | 66 |
|
54 | | - // test InflaterInputStream |
55 | | - try (ZipFile zfile = new ZipFile(f)) { |
56 | | - tryAvail(zfile.getInputStream(zfile.getEntry("Available.java"))); |
| 67 | + // Second entry uses STORED method |
| 68 | + ZipEntry stored = new ZipEntry("stored.txt"); |
| 69 | + stored.setMethod(ZipEntry.STORED); |
| 70 | + stored.setSize(contents.length); |
| 71 | + CRC32 crc32 = new CRC32(); |
| 72 | + crc32.update(contents); |
| 73 | + stored.setCrc(crc32.getValue()); |
| 74 | + zo.putNextEntry(stored); |
| 75 | + zo.write(contents); |
57 | 76 | } |
58 | 77 | } |
59 | 78 |
|
60 | | - static void tryAvail(InputStream in) throws Exception { |
61 | | - byte[] buf = new byte[1024]; |
62 | | - int n; |
| 79 | + /** |
| 80 | + * Delete the ZIP file created by this test |
| 81 | + * |
| 82 | + * @throws IOException if an unexpected error occurs |
| 83 | + */ |
| 84 | + @AfterEach |
| 85 | + public void cleanup() throws IOException { |
| 86 | + Files.deleteIfExists(zip); |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Verify that ZipInputStream.available() returns 0 after EOF or |
| 91 | + * closeEntry, otherwise 1, as specified in the API description. |
| 92 | + * This tests 4028605 4109069 4234207 |
| 93 | + * @throws IOException if an unexpected error occurs |
| 94 | + */ |
| 95 | + @Test |
| 96 | + public void testZipInputStream() throws IOException { |
| 97 | + try (InputStream in = Files.newInputStream(zip)) { |
| 98 | + ZipInputStream z = new ZipInputStream(in); |
| 99 | + z.getNextEntry(); |
| 100 | + assertEquals(1, z.available()); |
| 101 | + z.read(); |
| 102 | + assertEquals(1, z.available()); |
| 103 | + z.transferTo(OutputStream.nullOutputStream()); |
| 104 | + assertEquals(0, z.available(), |
| 105 | + "ZipInputStream.available() should return 0 after EOF"); |
| 106 | + |
| 107 | + z.close(); |
| 108 | + assertThrows(IOException.class, () -> z.available(), |
| 109 | + "Expected an IOException when calling available on a closed stream"); |
| 110 | + } |
63 | 111 |
|
64 | | - while ((n = in.read(buf)) != -1); |
65 | | - if (in.available() != 0) { |
66 | | - throw new Exception("available should return 0 after EOF"); |
| 112 | + try (InputStream in = Files.newInputStream(zip); |
| 113 | + ZipInputStream z = new ZipInputStream(in)) { |
| 114 | + z.getNextEntry(); |
| 115 | + z.closeEntry(); |
| 116 | + assertEquals(0, z.available(), |
| 117 | + "ZipInputStream.available() should return 0 after closeEntry"); |
67 | 118 | } |
68 | 119 | } |
69 | 120 |
|
70 | | - // To reproduce 4401122 |
71 | | - private static void test2() throws Exception { |
72 | | - File f = new File(System.getProperty("test.src", "."), "input.jar"); |
73 | | - try (ZipFile zf = new ZipFile(f)) { |
74 | | - InputStream in = zf.getInputStream(zf.getEntry("Available.java")); |
| 121 | + /** |
| 122 | + * Verify that ZipFileInputStream|ZipFileInflaterInputStream.available() |
| 123 | + * return the number of remaining uncompressed bytes. |
| 124 | + * |
| 125 | + * This verifies unspecified, but long-standing behavior. See 4401122. |
| 126 | + * |
| 127 | + * @throws IOException if an unexpected error occurs |
| 128 | + */ |
| 129 | + @ParameterizedTest |
| 130 | + @ValueSource(strings = { "stored.txt", "deflated.txt" }) |
| 131 | + public void testZipFileStreamsRemainingBytes(String entryName) throws IOException { |
| 132 | + try (ZipFile zfile = new ZipFile(zip.toFile())) { |
| 133 | + ZipEntry entry = zfile.getEntry(entryName); |
| 134 | + // Could be ZipFileInputStream or ZipFileInflaterInputStream |
| 135 | + InputStream in = zfile.getInputStream(entry); |
75 | 136 |
|
76 | 137 | int initialAvailable = in.available(); |
77 | | - in.read(); |
78 | | - if (in.available() != initialAvailable - 1) |
79 | | - throw new RuntimeException("Available not decremented."); |
80 | | - for(int j=0; j<initialAvailable-1; j++) |
| 138 | + |
| 139 | + // Initally, the number of remaining uncompressed bytes is the entry size |
| 140 | + assertEquals(entry.getSize(), initialAvailable); |
| 141 | + |
| 142 | + // Read all bytes one by one |
| 143 | + for (int i = initialAvailable; i > 0; i--) { |
| 144 | + // Reading a single byte should decrement available by 1 |
81 | 145 | in.read(); |
82 | | - if (in.available() != 0) |
83 | | - throw new RuntimeException(); |
| 146 | + assertEquals(i - 1, in.available(), "Available not decremented"); |
| 147 | + } |
| 148 | + |
| 149 | + // No remaining uncompressed bytes |
| 150 | + assertEquals(0, in.available()); |
| 151 | + |
| 152 | + // available() should still return 0 after close |
84 | 153 | in.close(); |
85 | | - if (in.available() != 0) |
86 | | - throw new RuntimeException(); |
| 154 | + assertEquals(0, in.available()); |
87 | 155 | } |
88 | 156 | } |
89 | | - |
90 | 157 | } |
0 commit comments