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.ar;
21  
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  
27  import java.io.BufferedInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  
31  import org.apache.commons.compress.AbstractTest;
32  import org.apache.commons.compress.archivers.ArchiveEntry;
33  import org.apache.commons.compress.utils.ArchiveUtils;
34  import org.apache.commons.io.IOUtils;
35  import org.junit.jupiter.api.Test;
36  import org.junit.jupiter.params.ParameterizedTest;
37  import org.junit.jupiter.params.provider.ValueSource;
38  
39  class ArArchiveInputStreamTest extends AbstractTest {
40  
41      private void checkLongNameEntry(final String archive) throws Exception {
42          try (InputStream fis = newInputStream(archive);
43                  ArArchiveInputStream s = new ArArchiveInputStream(new BufferedInputStream(fis))) {
44              ArchiveEntry e = s.getNextEntry();
45              assertEquals("this_is_a_long_file_name.txt", e.getName());
46              assertEquals(14, e.getSize());
47              final byte[] hello = new byte[14];
48              s.read(hello);
49              assertEquals("Hello, world!\n", ArchiveUtils.toAsciiString(hello));
50              e = s.getNextEntry();
51              assertEquals("this_is_a_long_file_name_as_well.txt", e.getName());
52              assertEquals(4, e.getSize());
53              final byte[] bye = new byte[4];
54              s.read(bye);
55              assertEquals("Bye\n", ArchiveUtils.toAsciiString(bye));
56              assertNull(s.getNextEntry());
57          }
58      }
59  
60      @Test
61      void testCantReadAfterClose() throws Exception {
62          try (InputStream in = newInputStream("bla.ar");
63                  ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
64              archive.close();
65              assertThrows(IllegalStateException.class, () -> archive.read());
66          }
67      }
68  
69      @Test
70      void testCantReadWithoutOpeningAnEntry() throws Exception {
71          try (InputStream in = newInputStream("bla.ar");
72                  ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
73              assertThrows(IllegalStateException.class, () -> archive.read());
74          }
75      }
76  
77      @Test
78      void testCompress661() throws IOException {
79          testCompress661(false);
80          testCompress661(true);
81      }
82  
83      private void testCompress661(final boolean checkMarkReadReset) throws IOException {
84          try (InputStream in = newInputStream("org/apache/commons/compress/COMPRESS-661/testARofText.ar");
85                  ArArchiveInputStream archive = new ArArchiveInputStream(new BufferedInputStream(in))) {
86              assertNotNull(archive.getNextEntry());
87              if (checkMarkReadReset && archive.markSupported()) {
88                  // mark() shouldn't be supported, but if it would be,
89                  // mark+read+reset should not do any harm.
90                  archive.mark(10);
91                  archive.read(new byte[10]);
92                  archive.reset();
93              }
94              final byte[] ba = IOUtils.toByteArray(archive);
95              assertEquals("Test d'indexation de Txt\nhttp://www.apache.org\n", new String(ba));
96              assertEquals(-1, archive.read());
97              assertEquals(-1, archive.read());
98              assertNull(archive.getNextEntry());
99          }
100     }
101 
102     @Test
103     void testInvalidBadTableLength() throws Exception {
104         try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/bad_table_length_gnu-fail.ar");
105                 ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
106             assertThrows(IOException.class, archive::getNextEntry);
107         }
108     }
109 
110     @ParameterizedTest
111     @ValueSource(strings = { "bad_long_namelen_bsd-fail.ar", "bad_long_namelen_gnu1-fail.ar", "bad_long_namelen_gnu2-fail.ar", "bad_long_namelen_gnu3-fail.ar",
112             "bad_table_length_gnu-fail.ar" })
113     void testInvalidLongNameLength(final String testFileName) throws Exception {
114         try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/" + testFileName);
115                 ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
116             assertThrows(IOException.class, archive::getNextEntry);
117         }
118     }
119 
120     @ParameterizedTest
121     @ValueSource(strings = { "bad_group-fail.ar", "bad_length-fail.ar", "bad_modified-fail.ar", "bad_user-fail.ar" })
122     void testInvalidNumericFields(final String testFileName) throws Exception {
123         try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/" + testFileName);
124                 ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
125             assertThrows(IOException.class, archive::getNextEntry);
126         }
127     }
128 
129     @Test
130     void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
131         final byte[] buf = new byte[2];
132         try (InputStream in = newInputStream("bla.ar");
133                 ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
134             assertNotNull(archive.getNextEntry());
135             IOUtils.toByteArray(archive);
136             assertEquals(-1, archive.read(buf));
137             assertEquals(-1, archive.read(buf));
138         }
139     }
140 
141     @Test
142     void testReadLongNamesBSD() throws Exception {
143         checkLongNameEntry("longfile_bsd.ar");
144     }
145 
146     @Test
147     void testReadLongNamesGNU() throws Exception {
148         checkLongNameEntry("longfile_gnu.ar");
149     }
150 
151     @Test
152     void testSimpleInputStream() throws IOException {
153         try (InputStream fileInputStream = newInputStream("bla.ar");
154                 // This default implementation of InputStream.available() always returns zero,
155                 // and there are many streams in practice where the total length of the stream is not known.
156                 InputStream simpleInputStream = new InputStream() {
157                     @Override
158                     public int read() throws IOException {
159                         return fileInputStream.read();
160                     }
161                 }) {
162             try (ArArchiveInputStream archiveInputStream = new ArArchiveInputStream(simpleInputStream)) {
163                 final ArArchiveEntry entry1 = archiveInputStream.getNextEntry();
164                 assertNotNull(entry1);
165                 assertEquals("test1.xml", entry1.getName());
166                 assertEquals(610L, entry1.getLength());
167                 final ArArchiveEntry entry2 = archiveInputStream.getNextEntry();
168                 assertEquals("test2.xml", entry2.getName());
169                 assertEquals(82L, entry2.getLength());
170                 assertNull(archiveInputStream.getNextEntry());
171             }
172         }
173     }
174 
175     @SuppressWarnings("deprecation")
176     @Test
177     void testSimpleInputStreamDeprecated() throws IOException {
178         try (InputStream fileInputStream = newInputStream("bla.ar");
179                 // This default implementation of InputStream.available() always returns zero,
180                 // and there are many streams in practice where the total length of the stream is not known.
181                 InputStream simpleInputStream = new InputStream() {
182                     @Override
183                     public int read() throws IOException {
184                         return fileInputStream.read();
185                     }
186                 }) {
187             try (ArArchiveInputStream archiveInputStream = new ArArchiveInputStream(simpleInputStream)) {
188                 final ArArchiveEntry entry1 = archiveInputStream.getNextArEntry();
189                 assertNotNull(entry1);
190                 assertEquals("test1.xml", entry1.getName());
191                 assertEquals(610L, entry1.getLength());
192                 final ArArchiveEntry entry2 = archiveInputStream.getNextArEntry();
193                 assertEquals("test2.xml", entry2.getName());
194                 assertEquals(82L, entry2.getLength());
195                 assertNull(archiveInputStream.getNextArEntry());
196             }
197         }
198     }
199 
200     @Test
201     void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
202         try (InputStream in = newInputStream("bla.ar");
203                 ArArchiveInputStream archive = new ArArchiveInputStream(in)) {
204             assertNotNull(archive.getNextEntry());
205             IOUtils.toByteArray(archive);
206             assertEquals(-1, archive.read());
207             assertEquals(-1, archive.read());
208         }
209     }
210 
211 }