mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
217 lines
9.0 KiB
Swift
217 lines
9.0 KiB
Swift
// Copyright (c) 2026 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
import XCTest
|
|
import SWCompression
|
|
|
|
class LZ4Tests: XCTestCase {
|
|
|
|
private static let testType: String = "lz4"
|
|
|
|
// These tests test frames with independent blocks (since they all have only one block). The frames also have
|
|
// additional features enabled, such as content size and block checksums. They also test legacy frame format.
|
|
|
|
func perform(test testName: String) throws {
|
|
let testData = try Constants.data(forTest: testName, withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
|
|
let answerData = try Constants.data(forAnswer: testName)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
private static func perform(legacyTest testName: String) throws {
|
|
let testData = try Constants.data(forTest: testName + "_legacy", withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
|
|
let answerData = try Constants.data(forAnswer: testName)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func test1LZ4() throws {
|
|
try self.perform(test: "test1")
|
|
try LZ4Tests.perform(legacyTest: "test1")
|
|
}
|
|
|
|
func test2LZ4() throws {
|
|
try self.perform(test: "test2")
|
|
try LZ4Tests.perform(legacyTest: "test2")
|
|
}
|
|
|
|
func test3LZ4() throws {
|
|
try self.perform(test: "test3")
|
|
try LZ4Tests.perform(legacyTest: "test3")
|
|
}
|
|
|
|
func test4LZ4() throws {
|
|
try self.perform(test: "test4")
|
|
try LZ4Tests.perform(legacyTest: "test4")
|
|
}
|
|
|
|
func test5LZ4() throws {
|
|
try self.perform(test: "test5")
|
|
try LZ4Tests.perform(legacyTest: "test5")
|
|
}
|
|
|
|
func test6LZ4() throws {
|
|
try self.perform(test: "test6")
|
|
try LZ4Tests.perform(legacyTest: "test6")
|
|
}
|
|
|
|
func test7LZ4() throws {
|
|
try self.perform(test: "test7")
|
|
try LZ4Tests.perform(legacyTest: "test7")
|
|
}
|
|
|
|
func test8LZ4() throws {
|
|
try self.perform(test: "test8")
|
|
try LZ4Tests.perform(legacyTest: "test8")
|
|
}
|
|
|
|
func test9LZ4() throws {
|
|
try self.perform(test: "test9")
|
|
try LZ4Tests.perform(legacyTest: "test9")
|
|
}
|
|
|
|
func testDependentBlocks() throws {
|
|
// This test contains dependent blocks (with the size of 64 kB), as well as has additional features enabled,
|
|
// such as content size and block checksums.
|
|
let testData = try Constants.data(forTest: "SWCompressionSourceCode.tar", withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
|
|
let answerData = try Constants.data(forTest: "SWCompressionSourceCode", withType: "tar")
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func testBadFile_short() {
|
|
LZ4Tests.checkTruncationError(Data([0]))
|
|
}
|
|
|
|
func testBadFile_invalid() throws {
|
|
let testData = try Constants.data(forAnswer: "test6")
|
|
var thrownError: Error?
|
|
XCTAssertThrowsError(try LZ4.decompress(data: testData)) { thrownError = $0 }
|
|
XCTAssertTrue(thrownError is DataError, "Unexpected error type: \(type(of: thrownError))")
|
|
XCTAssertEqual(thrownError as? DataError, .corrupted)
|
|
}
|
|
|
|
func testEmptyData() {
|
|
LZ4Tests.checkTruncationError(Data())
|
|
}
|
|
|
|
private static func checkTruncationError(_ data: Data) {
|
|
var thrownError: Error?
|
|
XCTAssertThrowsError(try LZ4.decompress(data: data)) { thrownError = $0 }
|
|
XCTAssertTrue(thrownError is DataError, "Unexpected error type: \(type(of: thrownError))")
|
|
XCTAssertEqual(thrownError as? DataError, .truncated)
|
|
}
|
|
|
|
func testSkippableFrame() throws {
|
|
let testData = try Constants.data(forTest: "test_skippable_frame", withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
|
|
let answerData = try Constants.data(forAnswer: "test4")
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func testLegacyFrameMultipleBlocks() throws {
|
|
let testData = try Constants.data(forTest: "zeros", withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
|
|
let answerData = Data(count: 18874368)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func testBlockSizes() throws {
|
|
// These tests don't include any checksums (becaused they are too time consuming). Only content sizes are used
|
|
// for verification. We still test both dependent and independent blocks.
|
|
let answerData = Data(count: 5242880)
|
|
|
|
for blockSize in ["4", "5", "6", "7", "1234"] {
|
|
for dep in ["", "_BD"] {
|
|
let testData = try Constants.data(forTest: "test_B" + blockSize + dep, withType: LZ4Tests.testType)
|
|
let decompressedData = try LZ4.decompress(data: testData)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDictionary() throws {
|
|
// Unfortunately, LZ4 reference implementation doesn't save dictID inside a frame, even though it is present
|
|
// in the dictionary file. So we test dictID comparison by using the manually constructed file (the last test).
|
|
let answerData = try Constants.data(forTest: "SWCompressionSourceCode", withType: "tar")
|
|
let dictData = try Constants.data(forTest: "lz4_dict", withType: "")
|
|
|
|
var testData = try Constants.data(forTest: "test_dict_B5", withType: LZ4Tests.testType)
|
|
var decompressedData = try LZ4.decompress(data: testData, dictionary: dictData)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
|
|
testData = try Constants.data(forTest: "test_dict_B5_BD", withType: LZ4Tests.testType)
|
|
decompressedData = try LZ4.decompress(data: testData, dictionary: dictData)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
|
|
testData = try Constants.data(forTest: "test_dict_B5_dictID", withType: LZ4Tests.testType)
|
|
decompressedData = try LZ4.decompress(data: testData, dictionary: dictData, dictionaryID: 20000)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func testSmallDictionary() throws {
|
|
// Here we test decompression with a small dictionary, i.e. smaller than standard "lookback window" of 64 KB.
|
|
let answerData = try Constants.data(forTest: "SWCompressionSourceCode", withType: "tar")
|
|
let dictData = try Constants.data(forTest: "lz4_small_dict", withType: "")
|
|
|
|
var testData = try Constants.data(forTest: "test_small_dict_B5", withType: LZ4Tests.testType)
|
|
var decompressedData = try LZ4.decompress(data: testData, dictionary: dictData)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
|
|
testData = try Constants.data(forTest: "test_small_dict_B5_BD", withType: LZ4Tests.testType)
|
|
decompressedData = try LZ4.decompress(data: testData, dictionary: dictData)
|
|
XCTAssertEqual(decompressedData, answerData)
|
|
}
|
|
|
|
func testMultiFrameDecompress() throws {
|
|
// The test file contains three frames:
|
|
// - Legacy frame format, compressed test1.answer,
|
|
// - Skippable frame with 1233 bytes of random data,
|
|
// - Normal frame with compressed test4.answer.
|
|
let testData = try Constants.data(forTest: "test_multi_frame", withType: LZ4Tests.testType)
|
|
let result = try LZ4.multiDecompress(data: testData)
|
|
|
|
XCTAssertEqual(result.count, 2)
|
|
XCTAssertEqual(result[0], try Constants.data(forAnswer: "test1"))
|
|
XCTAssertEqual(result[1], try Constants.data(forAnswer: "test4"))
|
|
}
|
|
|
|
func testChecksumMismatch() throws {
|
|
// Here we test that an error for checksum mismatch is thrown correctly and its associated value contains
|
|
// expected data. We do this by programmatically adjusting the input: we change one of the bytes for the checkum,
|
|
// which makes it incorrect.
|
|
var testData = try Constants.data(forTest: "test1", withType: LZ4Tests.testType)
|
|
// The content checksum is the last 4 bytes.
|
|
testData[testData.endIndex - 2] &+= 1
|
|
var thrownError: Error? = nil
|
|
XCTAssertThrowsError(try LZ4.decompress(data: testData)) { thrownError = $0 }
|
|
XCTAssertTrue(thrownError is DataError, "Unexpected error type: \(type(of: thrownError))")
|
|
if case let .some(.checksumMismatch(decompressedData)) = thrownError as? DataError {
|
|
XCTAssertEqual(decompressedData.count, 1)
|
|
let answerData = try Constants.data(forAnswer: "test1")
|
|
XCTAssertEqual(decompressedData.first, answerData)
|
|
} else {
|
|
XCTFail("Unexpected error: \(String(describing: thrownError))")
|
|
}
|
|
}
|
|
|
|
func testLZ4Truncation() throws {
|
|
for i in 1...9 {
|
|
let testName = "test\(i)"
|
|
let testData = try Constants.data(forTest: testName, withType: LZ4Tests.testType)
|
|
for _ in 0..<100 {
|
|
let truncationIndex = Int.random(in: (testData.startIndex + 1)..<testData.endIndex)
|
|
_ = try? LZ4.decompress(data: testData[testData.startIndex..<truncationIndex])
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|