diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/HeaderChecksumType.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/HeaderChecksumType.java index e5cd704..4f11ca0 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/HeaderChecksumType.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/HeaderChecksumType.java @@ -16,35 +16,69 @@ package de.bmarwell.zchunk.fileformat; +import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.StringJoiner; +import java.util.logging.Logger; /** * 0 = SHA-1 * 1 = SHA-256 */ public enum HeaderChecksumType { - SHA1("SHA-1"), - SHA256("SHA-256"); + UNKNOWN("unknown", -1L, 0), + SHA1("SHA-1", 0L, -1), + SHA256("SHA-256", 1L, -1); private final String digestAlgorithm; private final int digestLength; + /** + * Constant and unique value as from {@code codezchunk_format.txt}. + */ + private final long identifier; - HeaderChecksumType(final String digestAlgorithm) { + HeaderChecksumType(final String digestAlgorithm, final long identifier, final int manualDigestLength) { try { this.digestAlgorithm = digestAlgorithm; - this.digestLength = MessageDigest.getInstance(digestAlgorithm).getDigestLength(); + if (manualDigestLength <= -1) { + // use default + this.digestLength = MessageDigest.getInstance(digestAlgorithm).getDigestLength(); + } else { + this.digestLength = manualDigestLength; + } + + this.identifier = identifier; } catch (final NoSuchAlgorithmException algoEx) { throw new IllegalArgumentException("Unable to create hashing algorithm: [" + digestAlgorithm + "]. Check your JVM settings.", algoEx); } } + public static HeaderChecksumType find(final BigInteger unsignedLongValue) { + if (unsignedLongValue.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 1) { + final String message = String.format("Unknown Checksum type: [%s], exeeds [%d]!", unsignedLongValue.toString(), Integer.MAX_VALUE); + Logger.getLogger(HeaderChecksumType.class.getCanonicalName()).warning(message); + return UNKNOWN; + } + + final long requestedId = unsignedLongValue.longValue(); + + return Arrays.stream(values()) + .filter(algo -> algo.identifier == requestedId) + .findFirst() + .orElse(UNKNOWN); + } + public int getDigestLength() { return this.digestLength; } - public MessageDigest digest() { + public long getIdentifier() { + return this.identifier; + } + + public MessageDigest getMessageDigest() { try { return MessageDigest.getInstance(this.digestAlgorithm); } catch (final NoSuchAlgorithmException algoEx) { @@ -57,6 +91,7 @@ public String toString() { return new StringJoiner(", ", HeaderChecksumType.class.getSimpleName() + "[", "]") .add("digestAlgorithm=" + this.digestAlgorithm) .add("digestLength=" + this.digestLength) + .add("identifier=" + this.identifier) .add("ordinal=" + this.ordinal()) .toString(); } diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/IndexChecksumType.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/IndexChecksumType.java index 6636ae5..3f8cc32 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/IndexChecksumType.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/IndexChecksumType.java @@ -33,16 +33,16 @@ public enum IndexChecksumType { SHA512("SHA-512", -1), SHA512_128("SHA-512", 16); - private final MessageDigest digestAlgorithm; + private final String digestAlgorithm; private final int length; IndexChecksumType(final String digestAlgorithm, final int length) { try { - this.digestAlgorithm = MessageDigest.getInstance(digestAlgorithm); + this.digestAlgorithm = digestAlgorithm; if (length != -1) { this.length = length; } else { - this.length = this.digestAlgorithm.getDigestLength(); + this.length = MessageDigest.getInstance(digestAlgorithm).getDigestLength(); } } catch (final NoSuchAlgorithmException algoEx) { throw new IllegalArgumentException("Unable to create hashing algorithm: [" + digestAlgorithm + "]. Check your JVM settings.", algoEx); @@ -54,9 +54,9 @@ public int actualChecksumLength() { } public byte[] digest(final byte[] input) { - final byte[] digest = this.digestAlgorithm.digest(input); + final byte[] digest = this.getMessageDigest().digest(input); - if (this.length != this.digestAlgorithm.getDigestLength()) { + if (this.length != getMessageDigest().getDigestLength()) { final byte[] actualDigest = new byte[this.length]; System.arraycopy(digest, 0, actualDigest, 0, this.length); return actualDigest; @@ -65,10 +65,18 @@ public byte[] digest(final byte[] input) { return digest; } + public MessageDigest getMessageDigest() { + try { + return MessageDigest.getInstance(this.digestAlgorithm); + } catch (final NoSuchAlgorithmException algoEx) { + throw new IllegalStateException("Unable to create message getMessageDigest instance!", algoEx); + } + } + @Override public String toString() { return new StringJoiner(", ", IndexChecksumType.class.getSimpleName() + "[", "]") - .add("digestAlgorithm=" + this.digestAlgorithm.getAlgorithm()) + .add("digestAlgorithm=" + this.digestAlgorithm) .add("actualChecksumLength=" + this.length) .add("ordinal=" + this.ordinal()) .toString(); diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunk.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunk.java new file mode 100644 index 0000000..5d29d29 --- /dev/null +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunk.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019, the zchunk-java contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.bmarwell.zchunk.fileformat; + +import de.bmarwell.zchunk.fileformat.err.InvalidFileException; +import de.bmarwell.zchunk.fileformat.util.ChecksumUtil; +import java.io.File; + +public class ZChunk { + + /** + * Reads in a zchunk file. + * + *

The header part will stay in memory (heap).
+ * The data streams and/or chunks will be available as inputstream, but are not + * eagerly loaded into memory.

+ * + * @param input + * the input file. + * @return a {@link ZChunkFile} instance. + * @throws InvalidFileException + * if the input file is not a zchunk file. + * @throws NullPointerException + * if the input file is {@code null}. + */ + public static ZChunkFile fromFile(final File input) { + final ZChunkHeader header = ZChunkHeaderFactory.getZChunkFileHeader(input); + + return ImmutableZChunkFile.builder().header(header).build(); + } + + public static boolean validateFile(final File file) { + final ZChunkFile zChunkFile = fromFile(file); + final ZChunkHeader header = zChunkFile.getHeader(); + + return ChecksumUtil.isValidHeader(header) + && ChecksumUtil.allChunksAreValid(header, file) + && ChecksumUtil.isValidData(header, file); + } + +} diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderChunkInfo.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderChunkInfo.java index c5d28a4..f7a8426 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderChunkInfo.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderChunkInfo.java @@ -18,6 +18,7 @@ import de.bmarwell.zchunk.compressedint.CompressedInt; import de.bmarwell.zchunk.fileformat.util.ByteUtils; +import java.util.Comparator; import java.util.Optional; import java.util.StringJoiner; import org.immutables.value.Value; @@ -38,6 +39,8 @@ @Value.Immutable public abstract class ZChunkHeaderChunkInfo { + public static final Comparator INDEX_COMPARATOR = Comparator.comparing(ZChunkHeaderChunkInfo::getCurrentIndex); + public abstract long getCurrentIndex(); public abstract Optional getChunkStream(); diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderFactory.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderFactory.java index 5cc936c..a9451de 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderFactory.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderFactory.java @@ -17,6 +17,7 @@ package de.bmarwell.zchunk.fileformat; import static de.bmarwell.zchunk.fileformat.ZChunkConstants.Header.MAX_LEAD_SIZE; +import static java.util.stream.Collectors.toConcurrentMap; import de.bmarwell.zchunk.compressedint.CompressedInt; import de.bmarwell.zchunk.compressedint.CompressedIntFactory; @@ -36,6 +37,7 @@ import java.math.BigInteger; import java.nio.channels.Channels; import java.util.Set; +import java.util.function.Function; import java.util.logging.Logger; public final class ZChunkHeaderFactory { @@ -46,29 +48,11 @@ private ZChunkHeaderFactory() { // } - /** - * Reads in a zchunk file. - * - *

The header part will stay in memory (heap).
- * The data streams and/or chunks will be available as inputstream, but are not - * eagerly loaded into memory.

- * - * @param input - * the input file. - * @return a {@link ZChunkFile} instance. - * @throws InvalidFileException - * if the input file is not a zchunk file. - * @throws NullPointerException - * if the input file is {@code null}. - */ - public static ZChunkFile fromFile(final File input) { - final ZChunkHeader header = getZChunkFileHeader(input); - - return ImmutableZChunkFile.builder().header(header).build(); - } - - private static ZChunkHeader getZChunkFileHeader(final File input) { + public static ZChunkHeader getZChunkFileHeader(final File input) { final ZChunkHeaderLead lead = readFileHeaderLead(input); + if (lead.getChecksumType() == HeaderChecksumType.UNKNOWN) { + throw new UnsupportedOperationException("Unknown getMessageDigest type: [" + lead.getChecksumType() + "]."); + } final byte[] completeHeader = readCompleteHeader(input, lead); final ZChunkHeaderPreface preface = readHeaderPreface(completeHeader, lead); final ZChunkHeaderIndex index = readHeaderIndex(completeHeader, lead, preface); @@ -235,7 +219,8 @@ private static ZChunkHeaderIndex readHeaderIndex(final byte[] completeHeader, fi .dictChecksum(parser.readDictChecksum()) .dictLength(parser.readDictLength()) .uncompressedDictLength(parser.readUncompressedDictLength()) - .addAllChunkInfo(parser.readChunkInfos()) + .putAllChunkInfo(parser.readChunkInfos().stream() + .collect(toConcurrentMap(ZChunkHeaderChunkInfo::getChunkChecksum, Function.identity()))) .build(); } diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderIndex.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderIndex.java index 0e7fff5..1f12dd9 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderIndex.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderIndex.java @@ -18,9 +18,13 @@ import de.bmarwell.zchunk.compressedint.CompressedInt; import de.bmarwell.zchunk.fileformat.util.ByteUtils; -import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.StringJoiner; +import java.util.TreeSet; +import java.util.function.Supplier; +import java.util.stream.Collectors; import org.immutables.value.Value; /** @@ -80,7 +84,17 @@ public IndexChecksumType getChunkChecksumType() { public abstract CompressedInt getUncompressedDictLength(); - public abstract List getChunkInfo(); + public abstract Map getChunkInfo(); + + @Value.Lazy + public Set getChunkInfoSortedByIndex() { + final Supplier> IndexSortedList = () -> new TreeSet<>(ZChunkHeaderChunkInfo.INDEX_COMPARATOR); + + return getChunkInfo().values() + .stream() + .sorted(ZChunkHeaderChunkInfo.INDEX_COMPARATOR) + .collect(Collectors.toCollection(IndexSortedList)); + } @Override public String toString() { diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderLead.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderLead.java index 96a8621..82e8898 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderLead.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/ZChunkHeaderLead.java @@ -41,7 +41,7 @@ public abstract class ZChunkHeaderLead { @Value.Derived public HeaderChecksumType getChecksumType() { - return HeaderChecksumType.values()[getChecksumTypeInt().getIntValue()]; + return HeaderChecksumType.find(getChecksumTypeInt().getValue()); } /** diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkIndexParser.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkIndexParser.java index ea84f89..ccc0979 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkIndexParser.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkIndexParser.java @@ -176,7 +176,7 @@ public CompressedInt readUncompressedDictLength() { } } - public Iterable readChunkInfos() { + public List readChunkInfos() { if (this.chunkStreamOffset == -1L) { readUncompressedDictLength(); } diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkLeadParser.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkLeadParser.java index 3dfe80e..db7bbeb 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkLeadParser.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/parser/ZChunkLeadParser.java @@ -105,7 +105,7 @@ public CompressedInt readLeadCksumType() { final CompressedInt checksumType = CompressedIntFactory.readCompressedInt(bis); this.headerSizeOffset = FILE_MAGIC.length + (long) checksumType.getCompressedBytes().length; - this.checksumType = HeaderChecksumType.values()[checksumType.getIntValue()]; + this.checksumType = HeaderChecksumType.find(checksumType.getValue()); return checksumType; } catch (final IOException ioEx) { diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/ChecksumUtil.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/ChecksumUtil.java index c42ac63..d1a1266 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/ChecksumUtil.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/ChecksumUtil.java @@ -20,27 +20,38 @@ import static java.util.stream.Collectors.toList; import de.bmarwell.zchunk.fileformat.HeaderChecksumType; +import de.bmarwell.zchunk.fileformat.PrefaceFlag; import de.bmarwell.zchunk.fileformat.ZChunkHeader; import de.bmarwell.zchunk.fileformat.ZChunkHeaderChunkInfo; import de.bmarwell.zchunk.fileformat.ZChunkHeaderIndex; import de.bmarwell.zchunk.fileformat.ZChunkHeaderLead; import de.bmarwell.zchunk.fileformat.ZChunkHeaderPreface; import de.bmarwell.zchunk.fileformat.ZChunkHeaderSignatures; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.security.MessageDigest; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; import java.util.logging.Logger; public final class ChecksumUtil { private static final Logger LOG = Logger.getLogger(ChecksumUtil.class.getCanonicalName()); + /** + * Buffer size for reading files. + */ + private static final int BUFFER_SIZE = 1024; + private ChecksumUtil() { // util } - public static boolean isValid(final ZChunkHeader header) { + public static boolean isValidHeader(final ZChunkHeader header) { final byte[] expectedChecksum = header.getLead().getChecksum(); final byte[] calculatedChecksum = calculateHeaderChecksum(header); @@ -49,7 +60,7 @@ public static boolean isValid(final ZChunkHeader header) { public static byte[] calculateHeaderChecksum(final ZChunkHeader header) { final HeaderChecksumType digestAlgorithm = header.getLead().getChecksumType(); - final MessageDigest digest = digestAlgorithm.digest(); + final MessageDigest digest = digestAlgorithm.getMessageDigest(); digest.update(getLeadBytes(header.getLead())); digest.update(getPrefaceBytes(header.getPreface())); @@ -81,7 +92,8 @@ private static byte[] getSignatureBytes(final ZChunkHeaderSignatures signatures) } private static byte[] getIndexBytes(final ZChunkHeaderIndex index) { - final byte[] chunkInfos = getChunkInfoBytes(index.getChunkInfo()); + + final byte[] chunkInfos = getChunkInfoBytes(index.getChunkInfoSortedByIndex()); final byte[] indexBytes = concat( index.getIndexSize().getCompressedBytes(), @@ -97,7 +109,7 @@ private static byte[] getIndexBytes(final ZChunkHeaderIndex index) { return indexBytes; } - private static byte[] getChunkInfoBytes(final List chunkInfos) { + private static byte[] getChunkInfoBytes(final Collection chunkInfos) { final List collect = chunkInfos.stream() .map(ChecksumUtil::getChunkInfoBytes) .collect(toList()); @@ -167,4 +179,55 @@ private static byte[] concatByteArrays(final List byteList) { } + public static boolean isValidData(final ZChunkHeader zChunkHeader, final File fileToCheck) { + if (zChunkHeader.getPreface().getPrefaceFlags().contains(PrefaceFlag.HAS_DATA_STREAMS)) { + throw new UnsupportedOperationException("Data streams not supported yet."); + } + + final int totalHeaderSize = OffsetUtil.getTotalHeaderSize(zChunkHeader.getLead()); + final HeaderChecksumType chunkChecksumType = zChunkHeader.getLead().getChecksumType(); + final MessageDigest messageDigest = chunkChecksumType.getMessageDigest(); + + try (final FileInputStream fis = new FileInputStream(fileToCheck)) { + fis.skip(totalHeaderSize); + final byte[] buffer = new byte[BUFFER_SIZE]; + int read = 0; + while ((read = fis.read(buffer)) == BUFFER_SIZE) { + messageDigest.update(buffer); + } + final byte[] lastChunk = new byte[read]; + System.arraycopy(buffer, 0, lastChunk, 0, read); + messageDigest.update(lastChunk); + + final byte[] expected = zChunkHeader.getPreface().getTotalDataChecksum(); + final byte[] actual = messageDigest.digest(); + + return Arrays.equals(expected, actual); + } catch (final IOException ioEx) { + LOG.log(Level.SEVERE, ioEx, () -> "Unable to seek [" + totalHeaderSize + "] bytes into the file."); + return false; + } + + } + + public static boolean allChunksAreValid(final ZChunkHeader zChunkFile, final File file) { + return zChunkFile.getIndex().getChunkInfoSortedByIndex().stream() + .allMatch(chunk -> chunkIsValid(chunk, zChunkFile, file)); + } + + private static boolean chunkIsValid(final ZChunkHeaderChunkInfo chunk, final ZChunkHeader zChunkFile, final File file) { + final long chunkOffset = OffsetUtil.getChunkOffset(zChunkFile, chunk.getCurrentIndex()); + + try (final FileInputStream fis = new FileInputStream(file)) { + fis.skip(chunkOffset); + final byte[] chunkData = new byte[chunk.getChunkLength().getIntValue()]; + fis.read(chunkData); + final byte[] digest = zChunkFile.getIndex().getChunkChecksumType().digest(chunkData); + + return Arrays.equals(chunk.getChunkChecksum(), digest); + } catch (final IOException ioEx) { + LOG.log(Level.SEVERE, ioEx, () -> "Unable to seek [" + chunkOffset + "] bytes into the file."); + return false; + } + } } diff --git a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/OffsetUtil.java b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/OffsetUtil.java index 0afe766..84ef792 100644 --- a/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/OffsetUtil.java +++ b/fileformat/src/main/java/de/bmarwell/zchunk/fileformat/util/OffsetUtil.java @@ -16,7 +16,10 @@ package de.bmarwell.zchunk.fileformat.util; +import de.bmarwell.zchunk.compressedint.CompressedInt; import de.bmarwell.zchunk.fileformat.OptionalElement; +import de.bmarwell.zchunk.fileformat.ZChunkHeader; +import de.bmarwell.zchunk.fileformat.ZChunkHeaderChunkInfo; import de.bmarwell.zchunk.fileformat.ZChunkHeaderLead; import de.bmarwell.zchunk.fileformat.ZChunkHeaderPreface; import java.math.BigInteger; @@ -73,4 +76,17 @@ private static long getOptElementCountBytes(final ZChunkHeaderPreface preface) { return preface.getOptionalElementCount().getCompressedBytes().length; } + public static long getChunkOffset(final ZChunkHeader zChunkHeader, final long chunkId) { + final long totalHeaderSize = OffsetUtil.getTotalHeaderSize(zChunkHeader.getLead()); + final CompressedInt dictLength = zChunkHeader.getIndex().getDictLength(); + + final long chunkOffset = zChunkHeader.getIndex().getChunkInfoSortedByIndex().stream() + .limit(chunkId) + .map(ZChunkHeaderChunkInfo::getChunkLength) + .mapToLong(CompressedInt::getLongValue) + .sum(); + + return totalHeaderSize + dictLength.getLongValue() + chunkOffset; + } + } diff --git a/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkFileTest.java b/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkFileTest.java index 1b0bc80..8cd0755 100644 --- a/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkFileTest.java +++ b/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkFileTest.java @@ -29,13 +29,19 @@ public class ZChunkFileTest { private static final Logger LOG = Logger.getLogger(ZChunkFileTest.class.getCanonicalName()); - private static final File TEST_FILE = new File(ZChunkFileTest.class.getResource("/testfiles/LICENSE.dict.fodt.zck").getPath()); - private static final File TEST_FILE_INVALID = new File( + public static final File TEST_FILE = new File(ZChunkFileTest.class.getResource("/testfiles/LICENSE.dict.fodt.zck").getPath()); + + public static final File TEST_FILE_INVALID = new File( ZChunkFileTest.class.getResource("/testfiles/LICENSE.dict.fodt.zck.invalid").getPath()); + public static final File TEST_FILE_HEADER_CKSUM_INVALID = new File( + ZChunkFileTest.class.getResource("/testfiles/LICENSE.dict.fodt.header-cksum-invalid.zck").getPath()); + + public static final File TEST_FILE_HEADER_DIGEST_INVALID = new File( + ZChunkFileTest.class.getResource("/testfiles/LICENSE.dict.fodt.digest-invalid.zck").getPath()); @Test public void testFileFormat() { - final ZChunkFile zChunkFile = ZChunkHeaderFactory.fromFile(TEST_FILE); + final ZChunkFile zChunkFile = ZChunk.fromFile(TEST_FILE); final ZChunkHeader header = zChunkFile.getHeader(); @@ -53,7 +59,7 @@ private void checkChecksum(final ZChunkHeader header) { Assertions.assertAll( () -> Assertions.assertArrayEquals(exp, actual), - () -> Assertions.assertTrue(ChecksumUtil.isValid(header)) + () -> Assertions.assertTrue(ChecksumUtil.isValidHeader(header)) ); } @@ -61,7 +67,7 @@ private void checkChecksum(final ZChunkHeader header) { public void testInvalidFile() { Assertions.assertAll( () -> Assertions.assertThrows(IllegalArgumentException.class, () -> ZChunkHeaderFactory.readFileHeaderLead(TEST_FILE_INVALID)), - () -> Assertions.assertThrows(IllegalArgumentException.class, () -> ZChunkHeaderFactory.fromFile(TEST_FILE_INVALID)) + () -> Assertions.assertThrows(IllegalArgumentException.class, () -> ZChunk.fromFile(TEST_FILE_INVALID)) ); } @@ -70,7 +76,7 @@ private void testLead(final ZChunkHeaderLead lead) { Assertions.assertAll( () -> Assertions.assertArrayEquals(ZChunkConstants.Header.FILE_MAGIC, lead.getId()), - () -> Assertions.assertEquals(1, lead.getChecksumType().ordinal()), + () -> Assertions.assertEquals(1L, lead.getChecksumType().getIdentifier()), () -> Assertions.assertEquals(394L, lead.getHeaderSize().getLongValue()), () -> Assertions.assertEquals(394, lead.getHeaderSize().getIntValue()), // TODO: 434 (reported by zck_read_header) vs 432 (this implementation). diff --git a/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkTest.java b/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkTest.java new file mode 100644 index 0000000..d4e12fc --- /dev/null +++ b/fileformat/src/test/java/de/bmarwell/zchunk/fileformat/ZChunkTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2019, the zchunk-java contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.bmarwell.zchunk.fileformat; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ZChunkTest { + + @Test + public void testZChunk_validate_valid() { + final boolean validateFile = ZChunk.validateFile(ZChunkFileTest.TEST_FILE); + Assertions.assertTrue(validateFile); + } + + @Test + public void testZChunk_validate_invalid() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ZChunk.validateFile(ZChunkFileTest.TEST_FILE_INVALID)); + } + + @Test + public void testZChunk_validate_checksum_header_invalid() { + final boolean validateFile = ZChunk.validateFile(ZChunkFileTest.TEST_FILE_HEADER_CKSUM_INVALID); + Assertions.assertFalse(validateFile); + } + + @Test + public void testZChunk_validate_digest_invalid() { + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> ZChunk.validateFile(ZChunkFileTest.TEST_FILE_HEADER_DIGEST_INVALID)); + } + +} diff --git a/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.digest-invalid.zck b/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.digest-invalid.zck new file mode 100644 index 0000000..feb138c Binary files /dev/null and b/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.digest-invalid.zck differ diff --git a/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.header-cksum-invalid.zck b/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.header-cksum-invalid.zck new file mode 100644 index 0000000..ec88fe8 Binary files /dev/null and b/fileformat/src/test/resources/testfiles/LICENSE.dict.fodt.header-cksum-invalid.zck differ