This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push: new 0be1c6d50 Sort members 0be1c6d50 is described below commit 0be1c6d5087e31bd94691c60a5c4761d3a63eeba Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Wed Aug 27 07:13:30 2025 -0400 Sort members --- .../archivers/dump/DumpArchiveInputStreamTest.java | 74 +++---- .../archivers/dump/DumpArchiveTestFactory.java | 20 +- .../compress/archivers/tar/TarUtilsTest.java | 234 ++++++++++----------- .../pack200/Pack200CompressorInputStreamTest.java | 8 +- 4 files changed, 168 insertions(+), 168 deletions(-) diff --git a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java index 92d47e1d5..4535627d9 100644 --- a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStreamTest.java @@ -48,6 +48,43 @@ class DumpArchiveInputStreamTest extends AbstractTest { + @ParameterizedTest + @ValueSource(ints = {1, 10, 32, 1024}) + void checkSupportedRecordSizes(final int ntrec) throws Exception { + try (InputStream is = createArchive(ntrec); + DumpArchiveInputStream dump = new DumpArchiveInputStream(is)) { + final DumpArchiveSummary summary = dump.getSummary(); + assertNotNull(summary); + assertEquals(ntrec, summary.getNTRec()); + } + } + + @ParameterizedTest + @ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 1025, Integer.MAX_VALUE}) + void checkUnsupportedRecordSizes(final int ntrec) throws Exception { + try (InputStream is = createArchive(ntrec)) { + final ArchiveException ex = assertThrows(ArchiveException.class, () -> new DumpArchiveInputStream(is)); + assertInstanceOf(ArchiveException.class, ex.getCause()); + assertTrue( + ex.getMessage().contains(Integer.toString(ntrec)), + "message should contain the invalid ntrec value"); + } + } + + private InputStream createArchive(final int ntrec) { + final byte[] dump = new byte[1024 * TP_SIZE]; + int offset = 0; + // summary + System.arraycopy(createSummary(ntrec), 0, dump, offset, TP_SIZE); + offset += TP_SIZE; + // CLRI segment + System.arraycopy(createSegment(DumpArchiveConstants.SEGMENT_TYPE.CLRI), 0, dump, offset, TP_SIZE); + offset += TP_SIZE; + // BITS segment + System.arraycopy(createSegment(DumpArchiveConstants.SEGMENT_TYPE.BITS), 0, dump, offset, TP_SIZE); + return new ByteArrayInputStream(dump); + } + @SuppressWarnings("deprecation") @Test void testConsumesArchiveCompletely() throws Exception { @@ -145,41 +182,4 @@ void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception { assertEquals(-1, archive.read()); } } - - @ParameterizedTest - @ValueSource(ints = {1, 10, 32, 1024}) - void checkSupportedRecordSizes(final int ntrec) throws Exception { - try (InputStream is = createArchive(ntrec); - DumpArchiveInputStream dump = new DumpArchiveInputStream(is)) { - final DumpArchiveSummary summary = dump.getSummary(); - assertNotNull(summary); - assertEquals(ntrec, summary.getNTRec()); - } - } - - @ParameterizedTest - @ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 1025, Integer.MAX_VALUE}) - void checkUnsupportedRecordSizes(final int ntrec) throws Exception { - try (InputStream is = createArchive(ntrec)) { - final ArchiveException ex = assertThrows(ArchiveException.class, () -> new DumpArchiveInputStream(is)); - assertInstanceOf(ArchiveException.class, ex.getCause()); - assertTrue( - ex.getMessage().contains(Integer.toString(ntrec)), - "message should contain the invalid ntrec value"); - } - } - - private InputStream createArchive(final int ntrec) { - final byte[] dump = new byte[1024 * TP_SIZE]; - int offset = 0; - // summary - System.arraycopy(createSummary(ntrec), 0, dump, offset, TP_SIZE); - offset += TP_SIZE; - // CLRI segment - System.arraycopy(createSegment(DumpArchiveConstants.SEGMENT_TYPE.CLRI), 0, dump, offset, TP_SIZE); - offset += TP_SIZE; - // BITS segment - System.arraycopy(createSegment(DumpArchiveConstants.SEGMENT_TYPE.BITS), 0, dump, offset, TP_SIZE); - return new ByteArrayInputStream(dump); - } } diff --git a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveTestFactory.java b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveTestFactory.java index 44f8e002e..ac8dfa3cd 100644 --- a/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveTestFactory.java +++ b/src/test/java/org/apache/commons/compress/archivers/dump/DumpArchiveTestFactory.java @@ -26,34 +26,34 @@ final class DumpArchiveTestFactory { private static final int NTREC_OFFSET = 896; private static final int SEGMENT_TYPE_OFFSET = 0; - static byte[] createSummary(final int ntrec) { + private static void convert32(final long value, final byte[] buffer, final int offset) { + ByteUtils.toLittleEndian(buffer, value, offset, 4); + } + + static byte[] createSegment(final DumpArchiveConstants.SEGMENT_TYPE segmentType) { final byte[] buffer = new byte[DumpArchiveConstants.TP_SIZE]; // magic convert32(DumpArchiveConstants.NFS_MAGIC, buffer, MAGIC_OFFSET); - // ntrec - convert32(ntrec, buffer, NTREC_OFFSET); + // type + ByteUtils.toLittleEndian(buffer, segmentType.code, SEGMENT_TYPE_OFFSET, 2); // checksum final int checksum = DumpArchiveUtil.calculateChecksum(buffer); convert32(checksum, buffer, CHECKSUM_OFFSET); return buffer; } - static byte[] createSegment(final DumpArchiveConstants.SEGMENT_TYPE segmentType) { + static byte[] createSummary(final int ntrec) { final byte[] buffer = new byte[DumpArchiveConstants.TP_SIZE]; // magic convert32(DumpArchiveConstants.NFS_MAGIC, buffer, MAGIC_OFFSET); - // type - ByteUtils.toLittleEndian(buffer, segmentType.code, SEGMENT_TYPE_OFFSET, 2); + // ntrec + convert32(ntrec, buffer, NTREC_OFFSET); // checksum final int checksum = DumpArchiveUtil.calculateChecksum(buffer); convert32(checksum, buffer, CHECKSUM_OFFSET); return buffer; } - private static void convert32(final long value, final byte[] buffer, final int offset) { - ByteUtils.toLittleEndian(buffer, value, offset, 4); - } - private DumpArchiveTestFactory() { // prevent instantiation } diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java index 4a19b3c45..8b3a630d6 100644 --- a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java @@ -53,6 +53,84 @@ class TarUtilsTest extends AbstractTest { + /** + * Builds an NTFS-style path (\\?\C:\...) up to a target total UTF-16 length, respecting 255-unit segments. + */ + private static String createNtfsLongNameByUtf16Units(final int totalUnits) { + final String prefix = "\\\\?\\C:\\"; + final String extension = ".txt"; + + // U+2605 BLACK STAR (BMP, 1 UTF-16 unit, 3 UTF-8 bytes) => lets us pack 255 units per segment easily + final String segment = StringUtils.repeat("★", 255) + '\\'; + + final StringBuilder sb = new StringBuilder(prefix); + while (sb.length() + extension.length() < totalUnits) { + sb.append(segment); + } + + // Trim to exact totalUnits (UTF-16 units), then append extension + sb.setLength(totalUnits - extension.length()); + sb.append(extension); + assertEquals(totalUnits, sb.length(), "Final length should be " + totalUnits + " UTF-16 code units"); + return sb.toString(); + } + + /** + * Builds a POSIX-style path (rooted at `/`) up to a target total *byte* length in UTF-8, 255 bytes/segment. + */ + private static String createPosixLongNameByUtf8Bytes(final int totalBytes) { + final String extension = ".txt"; + // U+2605 BLACK STAR (BMP, 1 UTF-16 unit, 3 UTF-8 bytes) => 85 * 3 UTF-8 bytes = 255 bytes + final String segment = StringUtils.repeat("★", 85) + '/'; + assertEquals(256, utf8Len(segment), "Segment length with separator should be 256 bytes in UTF-8"); + + final StringBuilder sb = new StringBuilder(); + int count = totalBytes / 256; // how many full 256-byte chunks can we fit? + while (count-- > 0) { + sb.append(segment); + } + count = totalBytes - utf8Len(sb) - utf8Len(extension); + while (count-- > 0) { + sb.append('a'); + } + sb.append(extension); + assertEquals(totalBytes, utf8Len(sb), "Final length should be " + totalBytes + " bytes in UTF-8"); + return sb.toString(); + } + + private static byte[] paddedUtf8Bytes(final String s) { + final int blockSize = 1024; + final byte[] bytes = s.getBytes(UTF_8); + return Arrays.copyOf(bytes, ((bytes.length + blockSize - 1) / blockSize) * blockSize); + } + + static Stream<Arguments> testReadLongNameHandlesLimits() { + final String empty = ""; + final String ntfsLongName = createNtfsLongNameByUtf16Units(32767); + final String posixLongName = createPosixLongNameByUtf8Bytes(4095); + return Stream.of( + Arguments.of("Empty", empty, utf8Bytes(empty)), + Arguments.of("Empty (padded)", empty, paddedUtf8Bytes(empty)), + Arguments.of("NTFS", ntfsLongName, utf8Bytes(ntfsLongName)), + Arguments.of("NTFS (padded)", ntfsLongName, paddedUtf8Bytes(ntfsLongName)), + Arguments.of("POSIX", posixLongName, utf8Bytes(posixLongName)), + Arguments.of("POSIX (padded)", posixLongName, paddedUtf8Bytes(posixLongName))); + } + + static Stream<Arguments> testReadLongNameThrowsOnTruncation() { + return Stream.of( + Arguments.of(Integer.MAX_VALUE, "truncated long name"), + Arguments.of(Long.MAX_VALUE, "invalid long name")); + } + + private static byte[] utf8Bytes(final String s) { + return s.getBytes(UTF_8); + } + + private static int utf8Len(final CharSequence s) { + return s.toString().getBytes(UTF_8).length; + } + private void checkName(final String string) { final byte[] buff = new byte[100]; final int len = TarUtils.formatNameBytes(string, buff, 0, buff.length); @@ -324,6 +402,45 @@ void testPaxHeaderEntryWithEmptyValueRemovesKey() throws Exception { assertEquals(0, headers.size()); } + @ParameterizedTest(name = "{0} long name is read correctly") + @MethodSource + void testReadLongNameHandlesLimits(final String kind, final String expectedName, final byte[] data) throws IOException { + final TarArchiveEntry entry = new TarArchiveEntry("test"); + entry.setSize(data.length); + // Lets add a trailing "garbage" to ensure we only read what we should + final byte[] dataWithGarbage = Arrays.copyOf(data, data.length + 1024); + Arrays.fill(dataWithGarbage, data.length, dataWithGarbage.length, (byte) 0xFF); + + try (InputStream in = new ByteArrayInputStream(dataWithGarbage)) { + final String actualName = TarUtils.readLongName(in, ZipEncodingHelper.getZipEncoding(UTF_8), entry); + assertEquals( + expectedName, + actualName, + () -> String.format("[%s] The long name read does not match the expected value.", kind)); + } + } + + @ParameterizedTest(name = "readLongName of {0} bytes throws ArchiveException") + @MethodSource + void testReadLongNameThrowsOnTruncation(final long size, final CharSequence expectedMessage) throws IOException { + final TarArchiveEntry entry = new TarArchiveEntry("test"); + entry.setSize(size); // absurdly large so any finite stream truncates + try (InputStream in = new NullInputStream()) { + final ArchiveException ex = assertThrows( + ArchiveException.class, + () -> TarUtils.readLongName(in, TarUtils.DEFAULT_ENCODING, entry), + "Expected ArchiveException due to truncated long name, but no exception was thrown"); + final String actualMessage = StringUtils.toRootLowerCase(ex.getMessage()); + assertNotNull(actualMessage, "Exception message should not be null"); + assertTrue( + actualMessage.contains(expectedMessage), + () -> "Expected exception message to contain '" + expectedMessage + "', but got: " + actualMessage); + assertTrue( + actualMessage.contains(String.format("%,d", size)), + () -> "Expected exception message to mention '" + size + "', but got: " + actualMessage); + } + } + @Test void testReadNegativeBinary12Byte() { final byte[] b = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -606,121 +723,4 @@ void testWriteNegativeBinary8Byte() { final byte[] b = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf1, (byte) 0xef, }; assertEquals(-3601L, TarUtils.parseOctalOrBinary(b, 0, 8)); } - - /** - * Builds an NTFS-style path (\\?\C:\...) up to a target total UTF-16 length, respecting 255-unit segments. - */ - private static String createNtfsLongNameByUtf16Units(final int totalUnits) { - final String prefix = "\\\\?\\C:\\"; - final String extension = ".txt"; - - // U+2605 BLACK STAR (BMP, 1 UTF-16 unit, 3 UTF-8 bytes) => lets us pack 255 units per segment easily - final String segment = StringUtils.repeat("★", 255) + '\\'; - - final StringBuilder sb = new StringBuilder(prefix); - while (sb.length() + extension.length() < totalUnits) { - sb.append(segment); - } - - // Trim to exact totalUnits (UTF-16 units), then append extension - sb.setLength(totalUnits - extension.length()); - sb.append(extension); - assertEquals(totalUnits, sb.length(), "Final length should be " + totalUnits + " UTF-16 code units"); - return sb.toString(); - } - - /** - * Builds a POSIX-style path (rooted at `/`) up to a target total *byte* length in UTF-8, 255 bytes/segment. - */ - private static String createPosixLongNameByUtf8Bytes(final int totalBytes) { - final String extension = ".txt"; - // U+2605 BLACK STAR (BMP, 1 UTF-16 unit, 3 UTF-8 bytes) => 85 * 3 UTF-8 bytes = 255 bytes - final String segment = StringUtils.repeat("★", 85) + '/'; - assertEquals(256, utf8Len(segment), "Segment length with separator should be 256 bytes in UTF-8"); - - final StringBuilder sb = new StringBuilder(); - int count = totalBytes / 256; // how many full 256-byte chunks can we fit? - while (count-- > 0) { - sb.append(segment); - } - count = totalBytes - utf8Len(sb) - utf8Len(extension); - while (count-- > 0) { - sb.append('a'); - } - sb.append(extension); - assertEquals(totalBytes, utf8Len(sb), "Final length should be " + totalBytes + " bytes in UTF-8"); - return sb.toString(); - } - - private static int utf8Len(final CharSequence s) { - return s.toString().getBytes(UTF_8).length; - } - - private static byte[] utf8Bytes(final String s) { - return s.getBytes(UTF_8); - } - - private static byte[] paddedUtf8Bytes(final String s) { - final int blockSize = 1024; - final byte[] bytes = s.getBytes(UTF_8); - return Arrays.copyOf(bytes, ((bytes.length + blockSize - 1) / blockSize) * blockSize); - } - - static Stream<Arguments> testReadLongNameHandlesLimits() { - final String empty = ""; - final String ntfsLongName = createNtfsLongNameByUtf16Units(32767); - final String posixLongName = createPosixLongNameByUtf8Bytes(4095); - return Stream.of( - Arguments.of("Empty", empty, utf8Bytes(empty)), - Arguments.of("Empty (padded)", empty, paddedUtf8Bytes(empty)), - Arguments.of("NTFS", ntfsLongName, utf8Bytes(ntfsLongName)), - Arguments.of("NTFS (padded)", ntfsLongName, paddedUtf8Bytes(ntfsLongName)), - Arguments.of("POSIX", posixLongName, utf8Bytes(posixLongName)), - Arguments.of("POSIX (padded)", posixLongName, paddedUtf8Bytes(posixLongName))); - } - - @ParameterizedTest(name = "{0} long name is read correctly") - @MethodSource - void testReadLongNameHandlesLimits(final String kind, final String expectedName, final byte[] data) throws IOException { - final TarArchiveEntry entry = new TarArchiveEntry("test"); - entry.setSize(data.length); - // Lets add a trailing "garbage" to ensure we only read what we should - final byte[] dataWithGarbage = Arrays.copyOf(data, data.length + 1024); - Arrays.fill(dataWithGarbage, data.length, dataWithGarbage.length, (byte) 0xFF); - - try (InputStream in = new ByteArrayInputStream(dataWithGarbage)) { - final String actualName = TarUtils.readLongName(in, ZipEncodingHelper.getZipEncoding(UTF_8), entry); - assertEquals( - expectedName, - actualName, - () -> String.format("[%s] The long name read does not match the expected value.", kind)); - } - } - - static Stream<Arguments> testReadLongNameThrowsOnTruncation() { - return Stream.of( - Arguments.of(Integer.MAX_VALUE, "truncated long name"), - Arguments.of(Long.MAX_VALUE, "invalid long name")); - } - - @ParameterizedTest(name = "readLongName of {0} bytes throws ArchiveException") - @MethodSource - void testReadLongNameThrowsOnTruncation(final long size, final CharSequence expectedMessage) throws IOException { - final TarArchiveEntry entry = new TarArchiveEntry("test"); - entry.setSize(size); // absurdly large so any finite stream truncates - try (InputStream in = new NullInputStream()) { - final ArchiveException ex = assertThrows( - ArchiveException.class, - () -> TarUtils.readLongName(in, TarUtils.DEFAULT_ENCODING, entry), - "Expected ArchiveException due to truncated long name, but no exception was thrown"); - final String actualMessage = StringUtils.toRootLowerCase(ex.getMessage()); - assertNotNull(actualMessage, "Exception message should not be null"); - assertTrue( - actualMessage.contains(expectedMessage), - () -> "Expected exception message to contain '" + expectedMessage + "', but got: " + actualMessage); - assertTrue( - actualMessage.contains(String.format("%,d", size)), - () -> "Expected exception message to mention '" + size + "', but got: " + actualMessage); - } - } } diff --git a/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStreamTest.java b/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStreamTest.java index 92eb4bf8f..5b2bef1bc 100644 --- a/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStreamTest.java @@ -33,16 +33,16 @@ */ public class Pack200CompressorInputStreamTest { + private void assertThrowsIOException(final String inputBase64) { + assertThrows(IOException.class, () -> new Pack200CompressorInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(inputBase64)))); + } + @BeforeEach void beforeAll() { // final Runtime runtime = Runtime.getRuntime(); // System.out.printf("freeMemory %,d, maxMemory %,d, totalMemory %,d%n", runtime.freeMemory(), runtime.maxMemory(), runtime.totalMemory()); } - private void assertThrowsIOException(final String inputBase64) { - assertThrows(IOException.class, () -> new Pack200CompressorInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(inputBase64)))); - } - /** * Tests bad input detected in org.apache.commons.compress.harmony.unpack200.CpBands.parseCpUtf8(InputStream). *