Skip to content

Commit

Permalink
Merge pull request #85 from tayloraswift/adopt-swift-testing
Browse files Browse the repository at this point in the history
Adopt swift testing
  • Loading branch information
tayloraswift authored Jan 16, 2025
2 parents 4e3ed28 + f296c24 commit d444d98
Show file tree
Hide file tree
Showing 26 changed files with 1,167 additions and 1,529 deletions.
File renamed without changes.
20 changes: 9 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ let package:Package = .init(name: "swift-png",
dependencies: [
.package(url: "https://github.com/tayloraswift/swift-hash", .upToNextMinor(
from: "0.7.1")),
.package(url: "https://github.com/tayloraswift/swift-grammar", .upToNextMinor(
from: "0.4.0")),
],
targets: [
.target(name: "LZ77",
Expand All @@ -32,29 +30,29 @@ let package:Package = .init(name: "swift-png",
.target(name: "PNG"),
]),

.executableTarget(name: "LZ77Tests",
.testTarget(name: "LZ77Tests",
dependencies: [
.target(name: "LZ77"),
.product(name: "Testing_", package: "swift-grammar"),
],
path: "Sources/LZ77Tests",
swiftSettings: [
.define("DEBUG", .when(configuration: .debug))
]),

.executableTarget(name: "PNGTests",
.testTarget(name: "PNGTests",
dependencies: [
.target(name: "PNG"),
.product(name: "Testing_", package: "swift-grammar"),
],
path: "Sources/PNGTests",
swiftSettings: [
.define("DEBUG", .when(configuration: .debug))
]),

.executableTarget(name: "PNGIntegrationTests",
.testTarget(name: "PNGIntegrationTests",
dependencies: [
.target(name: "PNG"),
.product(name: "Testing_", package: "swift-grammar"),
],
path: "Sources/PNGIntegrationTests",
exclude: [
"PngSuite.LICENSE",
"PngSuite.README",
Expand All @@ -63,11 +61,11 @@ let package:Package = .init(name: "swift-png",
"RGBA/",
]),

.executableTarget(name: "PNGCompressionTests",
.testTarget(name: "PNGCompressionTests",
dependencies: [
.target(name: "PNG"),
.product(name: "Testing_", package: "swift-grammar"),
]),
],
path: "Sources/PNGCompressionTests"),

.executableTarget(name: "PNGCompressionBenchmarks",
dependencies: [
Expand Down
8 changes: 3 additions & 5 deletions Scripts/TestAll
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ swift --version
swift build
swift build -c release
# Debug-only tests
swift run -c debug LZ77Tests
swift run -c debug PNGTests
swift test -c debug --filter LZ77Tests
swift test -c debug --filter PNGTests
# All tests
for f in .build/release/*Tests; do
$f
done
swift test -c release --no-parallel
187 changes: 187 additions & 0 deletions Sources/LZ77Tests/Bitstreams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#if DEBUG
@testable
import LZ77
import Testing

@Suite
enum CompressionInternals
{
@Test
static func BitstreamDecoding()
{
var bits:LZ77.InflatorIn = [
0b1001_1110,
0b1111_0110,
0b0010_0011,
]
#expect(bits[ 0] == 0b1111_0110_1001_1110)
#expect(bits[ 1] == 0b1_1111_0110_1001_111)
#expect(bits[ 2] == 0b11_1111_0110_1001_11)
#expect(bits[ 3] == 0b011_1111_0110_1001_1)
#expect(bits[ 4] == 0b0011_1111_0110_1001)
#expect(bits[ 5] == 0b0_0011_1111_0110_100)
#expect(bits[ 6] == 0b10_0011_1111_0110_10)
#expect(bits[ 7] == 0b010_0011_1111_0110_1)
#expect(bits[ 8] == 0b0010_0011_1111_0110)
#expect(bits[ 9] == 0b0_0010_0011_1111_011)
#expect(bits[23] == 0b0000_0000_0000_0000)

#expect(bits[0, count: 4, as: Int.self] == 0b1110)
#expect(bits[1, count: 4, as: Int.self] == 0b1_111)
#expect(bits[1, count: 6, as: Int.self] == 0b001_111)
#expect(bits[2, count: 6, as: Int.self] == 0b1001_11)
#expect(bits[2, count: 16, as: Int.self] == 0b11_1111_0110_1001_11)

// test rebase
// { 0010_0011, 1111_0110, 1001_1110 }
// ^
// b = 20
// ->
// { 0001_1000, 1010_1101, 0010_0011 }
// ^
// b = 4
var b:Int = 20

bits.rebase([0b1010_1101, 0b0001_1000], pointer: &b)

#expect(bits[b ] == 0b1000_1010_1101_0010)
#expect(bits[b + 1] == 0b1_1000_1010_1101_001)

// test rebase
// { 0001_1000, 1010_1101, 0010_0011 }
// ^
// b = 4
// { 1111_1100, 0011_1111, 0001_1000, 1010_1101, 0010_0011 }
bits.rebase([0b0011_1111, 0b1111_1100], pointer: &b)

#expect(bits[b ] == 0b1000_1010_1101_0010)
#expect(bits[b + 8] == 0b1111_0001_1000_1010)
}
@Test
static func BitstreamEncoding()
{
var bits:LZ77.DeflatorOut = .init(hint: 4)

bits.append(0b11, count: 2)
bits.append(0b01_10, count: 4)

bits.append(0b0110, count: 0)

bits.append(0b1_1111_11, count: 7)
bits.append(0b1010_1010_1010_101, count: 15)
bits.append(0b000, count: 3)
bits.append(0b0_1101_1, count: 6)
bits.append(0b1_0000_0000_111, count: 12)

var encoded:[UInt8] = []

while let chunk:[UInt8] = bits.pop()
{
encoded.append(contentsOf: chunk)
}

encoded.append(contentsOf: bits.pull())

#expect(encoded == [
0b1101_1011,
0b1011_1111,
0b1010_1010,
0b1000_1010,
0b1110_1101,
0b0000_0000,
0b0000_0001
])
}

@Test
static func Matching()
{
let segments:[[UInt8]] = [
[1, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 1, 2, 2, 2, 2, 2, 2, 0, 1, 2],
[2, 2, 2, 2, 0, 1, 2, 2, 0, 0, 0, 0, 2, 3, 2, 1, 2, 3, 3, 1, 5],
[1, 1, 3, 3, 1, 2, 3, 1, 2, 4, 4, 2, 1]
]
var input:LZ77.DeflatorIn<LZ77.MRC32> = .init()
var window:LZ77.DeflatorWindow = .init(exponent: 4)
var output:[[UInt8]] = []
for (s, segment):(Int, [UInt8]) in segments.enumerated()
{
input.enqueue(contentsOf: segment[...])

let lookahead:Int = (s == segments.count - 1 ? 0 : 10)
while window.endIndex < 0, input.count > lookahead
{
window.initialize(with: input.dequeue())
}
while input.count > lookahead
{
let head:(index:Int, next:UInt16?) = window.update(with: input.dequeue())
if let match:(run:Int, distance:Int) = window.match(from: head,
lookahead: input,
attempts: .max,
goal: .max)
{
var run:[UInt8] = [window.literal]
for _:Int in 1 ..< match.run
{
window.update(with: input.dequeue())
run.append(window.literal)
}
output.append(run)
}
else
{
output.append([window.literal])
}
}

guard s == segments.count - 1
else
{
continue
}

// epilogue: get the matches still sitting in the pipeline
let epilogue:Int = -3 - min(0, window.endIndex)
while input.count > epilogue
{
window.update(with: input.dequeue())
output.append([window.literal])
}
}
#expect(output == [
[1],
[2],
[3],
[3],
[1, 2, 3, 3, 1, 2, 3],
[1],
[2], [2], [2], [2], [2], [2],
[0],
[1, 2, 2, 2, 2, 2],
[0],
[1],
[2], [2],
[0], [0], [0], [0],
[2],
[3],
[2],
[1],
[2],
[3], [3],
[1],
[5],
[1], [1],
[3], [3],
[1],
[2],
[3],
[1],
[2],
[4], [4],
[2],
[1]
])
}
}
#endif
50 changes: 50 additions & 0 deletions Sources/LZ77Tests/Compression.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import LZ77
import Testing

@Suite
enum Compression
{
@Test(arguments: [4, 7, 9], [5, 15, 100, 200, 2000, 5000])
static func LZ77(_ level:Int, _ count:Int) throws
{
let input:[UInt8] = (0 ..< count).map{ _ in .random(in: .min ... .max) }

var deflator:LZ77.Deflator = .init(level: level, exponent: 8, hint: 16)
deflator.push(input[...], last: true)

var compressed:[UInt8] = []
while let part:[UInt8] = deflator.pull()
{
compressed += part
}

var inflator:LZ77.Inflator = .init()
try inflator.push(compressed[...])

let output:[UInt8] = inflator.pull()

#expect(input == output)
}

@Test(arguments: [5, 15, 100, 200, 2000, 5000])
static func Gzip(_ count:Int) throws
{
let input:[UInt8] = (0 ..< count).map{ _ in .random(in: .min ... .max) }

var deflator:Gzip.Deflator = .init(level: 7, exponent: 15, hint: 64 << 10)
deflator.push(input[...], last: true)

var compressed:[UInt8] = []
while let part:[UInt8] = deflator.pull()
{
compressed += part
}

var inflator:Gzip.Inflator = .init()
try inflator.push(compressed[...])

let output:[UInt8] = inflator.pull()

#expect(input == output)
}
}
29 changes: 29 additions & 0 deletions Sources/LZ77Tests/CompressionMicro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import LZ77
import Testing

@Suite
enum CompressionMicro
{
@Test(arguments: [[], [1], [2, 3], [4, 5, 6]])
static func Roundtrip(_ bytes:[UInt8]) throws
{
let archive:[UInt8] = Gzip.archive(bytes: bytes[...], level: 10)
#expect(try Gzip.extract(from: archive[...]) == bytes)
}

@Test
static func InParts() throws
{
var deflator:Gzip.Deflator = .init(level: 13, exponent: 15)
deflator.push([1], last: false)
deflator.push([2], last: true)

var archive:[UInt8] = []
while let part:[UInt8] = deflator.pull()
{
archive += part
}

#expect(try Gzip.extract(from: archive[...]) == [1, 2])
}
}
50 changes: 50 additions & 0 deletions Sources/LZ77Tests/HardwareAcceleration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#if DEBUG
@testable
import LZ77
import Testing

@Suite
enum HardwareAcceleration
{
@Test
static func DictionarySemantics()
{
let dictionary:F14.HashTable = .init(exponent: 10)

#expect(nil == dictionary.update(key: 0, value: 1))
#expect(nil == dictionary.update(key: 1, value: 2))
#expect(dictionary.update(key: 0, value: 3) == 1)
#expect(nil == dictionary.update(key: 2, value: 4))
#expect(nil != dictionary.remove(key: 1, value: 5))
#expect(dictionary.update(key: 1, value: 6) == 2)
#expect(nil != dictionary.remove(key: 1, value: 6))
#expect(nil == dictionary.update(key: 1, value: 7))

var a:F14.HashTable = .init(exponent: 15),
b:[UInt32: UInt16] = [:]
for i:UInt16 in ((0 ... .max).map{ $0 & 0x00ff })
{
let key:UInt32 = .random(in: 0 ... 1000)

#expect(a.update(key: key, value: i) == b.updateValue(i, forKey: key))
}
for i:UInt16 in ((0 ... .max).map{ $0 & 0x00ff })
{
let key:UInt32 = .random(in: 0 ... 1000)

if b[key] == i
{
b[key] = nil
}

a.remove(key: key, value: i)
}
for i:UInt16 in ((0 ... .max).map{ $0 & 0x00ff })
{
let key:UInt32 = .random(in: 0 ... 1000)

#expect(a.update(key: key, value: i) == b.updateValue(i, forKey: key))
}
}
}
#endif
Loading

0 comments on commit d444d98

Please sign in to comment.