288 lines
9.2 KiB
Nim
288 lines
9.2 KiB
Nim
## test_variant_fingerprint.nim
|
|
## Tests for variant fingerprint calculation
|
|
## Ensures determinism, uniqueness, and correctness
|
|
|
|
import std/[unittest, tables, strutils]
|
|
import ../src/nimpak/variant_fingerprint
|
|
import ../src/nimpak/variant_types
|
|
import ../src/nimpak/config
|
|
|
|
suite "Variant Fingerprint Tests":
|
|
|
|
# Test data
|
|
let testToolchain = newToolchainInfo("gcc", "13.2.0")
|
|
let testTarget = newTargetInfo("x86_64", "linux")
|
|
let testCompilerFlags = CompilerFlags(
|
|
cflags: "-O2 -pipe",
|
|
cxxflags: "-O2 -pipe",
|
|
ldflags: "-Wl,-O1",
|
|
makeflags: "-j4"
|
|
)
|
|
|
|
test "Same inputs produce identical fingerprints":
|
|
# Create two identical configurations
|
|
var domains1 = initTable[string, seq[string]]()
|
|
domains1["init"] = @["dinit"]
|
|
domains1["security"] = @["pie", "relro"]
|
|
domains1["optimization"] = @["lto"]
|
|
|
|
var domains2 = initTable[string, seq[string]]()
|
|
domains2["init"] = @["dinit"]
|
|
domains2["security"] = @["pie", "relro"]
|
|
domains2["optimization"] = @["lto"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains1, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains2, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 == fp2
|
|
check fp1.len == 20 # "blake2b-" + 12 chars
|
|
check fp1.startsWith("blake2b-")
|
|
|
|
test "Different package names produce different fingerprints":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"apache", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
|
|
test "Different versions produce different fingerprints":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.29.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
test "Different domain flags produce different fingerprints":
|
|
var domains1 = initTable[string, seq[string]]()
|
|
domains1["init"] = @["dinit"]
|
|
domains1["security"] = @["pie", "relro"]
|
|
|
|
var domains2 = initTable[string, seq[string]]()
|
|
domains2["init"] = @["systemd"]
|
|
domains2["security"] = @["pie", "relro"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains1, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains2, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
test "Flag order independence - same flags different order produce same fingerprint":
|
|
# Test that domain flags are sorted before hashing
|
|
var domains1 = initTable[string, seq[string]]()
|
|
domains1["security"] = @["pie", "relro", "hardened"]
|
|
domains1["optimization"] = @["lto", "pgo"]
|
|
domains1["init"] = @["dinit"]
|
|
|
|
var domains2 = initTable[string, seq[string]]()
|
|
domains2["init"] = @["dinit"]
|
|
domains2["optimization"] = @["lto", "pgo"]
|
|
domains2["security"] = @["pie", "relro", "hardened"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains1, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains2, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 == fp2
|
|
|
|
test "Value order within domain affects fingerprint":
|
|
# Different order of values within a domain should produce same result
|
|
# because values are sorted
|
|
var domains1 = initTable[string, seq[string]]()
|
|
domains1["security"] = @["relro", "pie", "hardened"]
|
|
|
|
var domains2 = initTable[string, seq[string]]()
|
|
domains2["security"] = @["pie", "relro", "hardened"]
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains1, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains2, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 == fp2
|
|
|
|
test "Different compiler flags produce different fingerprints":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
|
|
let flags1 = CompilerFlags(
|
|
cflags: "-O2 -pipe",
|
|
cxxflags: "-O2 -pipe",
|
|
ldflags: "-Wl,-O1",
|
|
makeflags: "-j4"
|
|
)
|
|
|
|
let flags2 = CompilerFlags(
|
|
cflags: "-O3 -march=native",
|
|
cxxflags: "-O3 -march=native",
|
|
ldflags: "-Wl,-O1",
|
|
makeflags: "-j4"
|
|
)
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, flags1, testToolchain, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, flags2, testToolchain, testTarget
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
test "Different toolchains produce different fingerprints":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
|
|
let toolchain1 = newToolchainInfo("gcc", "13.2.0")
|
|
let toolchain2 = newToolchainInfo("clang", "17.0.0")
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, toolchain1, testTarget
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, toolchain2, testTarget
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
test "Different targets produce different fingerprints":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
|
|
let target1 = newTargetInfo("x86_64", "linux")
|
|
let target2 = newTargetInfo("aarch64", "linux")
|
|
|
|
let fp1 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, target1
|
|
)
|
|
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, target2
|
|
)
|
|
|
|
check fp1 != fp2
|
|
|
|
test "buildVariantFingerprint creates complete object":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
domains["security"] = @["pie", "relro"]
|
|
|
|
let fp = buildVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp.packageName == "nginx"
|
|
check fp.version == "1.28.0"
|
|
check fp.domainFlags == domains
|
|
check fp.compilerFlags.cflags == testCompilerFlags.cflags
|
|
check fp.toolchain == testToolchain
|
|
check fp.target == testTarget
|
|
check fp.hash.len == 20
|
|
check fp.hash.startsWith("blake2b-")
|
|
|
|
test "isValidFingerprint validates format correctly":
|
|
check isValidFingerprint("blake2b-abc123def456") == true
|
|
check isValidFingerprint("blake2b-ABC123DEF456") == true
|
|
check isValidFingerprint("blake2b-0123456789ab") == true
|
|
|
|
# Invalid cases
|
|
check isValidFingerprint("blake2b-") == false
|
|
check isValidFingerprint("blake2b-abc") == false # Too short
|
|
check isValidFingerprint("blake2b-abc123def456xyz") == false # Too long
|
|
check isValidFingerprint("blake3-abc123def456") == false # Wrong prefix
|
|
check isValidFingerprint("abc123def456") == false # No prefix
|
|
check isValidFingerprint("blake2b-xyz123def456") == false # Invalid hex
|
|
|
|
test "extractFingerprintPrefix extracts 12-char prefix":
|
|
let fullHash = "blake2b-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
let prefix = extractFingerprintPrefix(fullHash)
|
|
|
|
check prefix == "blake2b-0123456789ab"
|
|
check prefix.len == 20
|
|
|
|
test "getFingerprintInputString returns deterministic input":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
domains["security"] = @["pie", "relro"]
|
|
|
|
let input = getFingerprintInputString(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check input.contains("nginx")
|
|
check input.contains("1.28.0")
|
|
check input.contains("init:dinit")
|
|
check input.contains("security:")
|
|
check input.contains("pie")
|
|
check input.contains("relro")
|
|
check input.contains("cflags:-O2 -pipe")
|
|
check input.contains("toolchain:gcc-13.2.0")
|
|
check input.contains("target:x86_64-linux")
|
|
|
|
test "Empty domains produce valid fingerprint":
|
|
var domains = initTable[string, seq[string]]()
|
|
|
|
let fp = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp.len == 20
|
|
check fp.startsWith("blake2b-")
|
|
|
|
test "Multiple domains with multiple values":
|
|
var domains = initTable[string, seq[string]]()
|
|
domains["init"] = @["dinit"]
|
|
domains["runtime"] = @["ssl", "http3", "zstd", "ipv6"]
|
|
domains["security"] = @["pie", "relro", "hardened"]
|
|
domains["optimization"] = @["lto", "march-native"]
|
|
domains["network"] = @["ipv6", "wireguard"]
|
|
|
|
let fp = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp.len == 20
|
|
check fp.startsWith("blake2b-")
|
|
|
|
# Verify it's deterministic
|
|
let fp2 = calculateVariantFingerprint(
|
|
"nginx", "1.28.0", domains, testCompilerFlags, testToolchain, testTarget
|
|
)
|
|
|
|
check fp == fp2
|
|
|
|
when isMainModule:
|
|
echo "Running variant fingerprint tests..."
|