334 lines
9.8 KiB
Nim
334 lines
9.8 KiB
Nim
## Unit Tests for Build Synthesis
|
|
##
|
|
## Tests for the build synthesis module which creates deterministic builds
|
|
## from unified variant profiles and stores them in the CAS.
|
|
|
|
import std/[unittest, options, tables, os, tempfiles, strutils, times]
|
|
import ../src/nip/resolver/build_synthesis
|
|
import ../src/nip/resolver/variant_types
|
|
|
|
suite "Build Synthesis Tests":
|
|
|
|
test "Build hash calculation is deterministic":
|
|
## Test that the same configuration always produces the same hash
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.addFlag("security", "hardened")
|
|
profile.calculateHash()
|
|
|
|
# Create build config
|
|
let config = newBuildConfig(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
compilerVersion = "gcc-13.2.0",
|
|
compilerFlags = @["-O2", "-march=native"],
|
|
configureFlags = @["--with-http_ssl_module"],
|
|
targetArchitecture = "x86_64",
|
|
libc = "musl",
|
|
allocator = "jemalloc"
|
|
)
|
|
|
|
# Calculate hash twice
|
|
let hash1 = calculateBuildHash(config)
|
|
let hash2 = calculateBuildHash(config)
|
|
|
|
# Hashes should be identical
|
|
check hash1 == hash2
|
|
check hash1.startsWith("xxh3-")
|
|
|
|
test "Different configurations produce different hashes":
|
|
## Test that different configurations produce different hashes
|
|
|
|
# Create first variant profile
|
|
var profile1 = newVariantProfile()
|
|
profile1.addFlag("optimization", "lto")
|
|
profile1.calculateHash()
|
|
|
|
# Create second variant profile (different)
|
|
var profile2 = newVariantProfile()
|
|
profile2.addFlag("optimization", "o3")
|
|
profile2.calculateHash()
|
|
|
|
# Create configs with different profiles
|
|
let config1 = newBuildConfig(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile1,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
let config2 = newBuildConfig(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile2,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
# Hashes should be different
|
|
let hash1 = calculateBuildHash(config1)
|
|
let hash2 = calculateBuildHash(config2)
|
|
|
|
check hash1 != hash2
|
|
|
|
test "Build synthesis creates valid result":
|
|
## Test that build synthesis creates a valid BuildSynthesisResult
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize build
|
|
let result = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
# Verify result
|
|
check result.buildHash.startsWith("xxh3-")
|
|
check result.casID == result.buildHash
|
|
check result.buildConfig.packageName == "nginx"
|
|
check result.buildConfig.packageVersion == "1.24.0"
|
|
|
|
test "CAS storage and retrieval":
|
|
## Test storing and retrieving builds from CAS
|
|
|
|
# Create temporary CAS directory
|
|
let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
|
|
createDir(casRoot)
|
|
|
|
try:
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize build
|
|
let result = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
# Store in CAS
|
|
let casID = storeBuildInCAS(result, casRoot)
|
|
|
|
# Verify CAS ID
|
|
check casID == result.casID
|
|
check casID.startsWith("xxh3-")
|
|
|
|
# Retrieve from CAS
|
|
let retrieved = retrieveBuildFromCAS(casID, casRoot)
|
|
|
|
# Verify retrieved build
|
|
check retrieved.buildHash == result.buildHash
|
|
check retrieved.buildConfig.packageName == "nginx"
|
|
check retrieved.buildConfig.packageVersion == "1.24.0"
|
|
|
|
finally:
|
|
# Clean up
|
|
removeDir(casRoot)
|
|
|
|
test "Build hash verification":
|
|
## Test verifying build hashes
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Create config
|
|
let config = newBuildConfig(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
# Calculate hash
|
|
let hash = calculateBuildHash(config)
|
|
|
|
# Verify hash
|
|
check verifyBuildHash(hash, config) == true
|
|
|
|
# Verify with wrong hash fails
|
|
check verifyBuildHash("xxh3-wronghash", config) == false
|
|
|
|
test "Build identity comparison":
|
|
## Test comparing builds for identity
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize two builds with same config
|
|
let result1 = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
let result2 = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
# Builds should be identical
|
|
check isBuildIdentical(result1, result2) == true
|
|
|
|
test "Different source hashes produce different builds":
|
|
## Test that different source hashes produce different build hashes
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize builds with different source hashes
|
|
let result1 = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123"
|
|
)
|
|
|
|
let result2 = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-def456"
|
|
)
|
|
|
|
# Build hashes should be different
|
|
check result1.buildHash != result2.buildHash
|
|
|
|
test "Canonical representation is deterministic":
|
|
## Test that canonical representation is deterministic
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.addFlag("security", "hardened")
|
|
profile.calculateHash()
|
|
|
|
# Create config
|
|
let config = newBuildConfig(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
compilerFlags = @["-O2", "-march=native", "-fPIC"],
|
|
configureFlags = @["--with-ssl", "--with-http2"]
|
|
)
|
|
|
|
# Get canonical representation twice
|
|
let canonical1 = config.toCanonical()
|
|
let canonical2 = config.toCanonical()
|
|
|
|
# Should be identical
|
|
check canonical1 == canonical2
|
|
|
|
# Should contain all components
|
|
check canonical1.contains("nginx")
|
|
check canonical1.contains("1.24.0")
|
|
check canonical1.contains("blake3-abc123")
|
|
check canonical1.contains("gcc-13.2.0")
|
|
|
|
test "Build synthesis with custom compiler flags":
|
|
## Test build synthesis with custom compiler flags
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "o3")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize with custom flags
|
|
let result = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
compilerVersion = "clang-17.0.0",
|
|
compilerFlags = @["-O3", "-march=native", "-flto"],
|
|
configureFlags = @["--with-http_ssl_module", "--with-http_v2_module"]
|
|
)
|
|
|
|
# Verify custom flags are in config
|
|
check result.buildConfig.compilerVersion == "clang-17.0.0"
|
|
check result.buildConfig.compilerFlags.contains("-O3")
|
|
check result.buildConfig.compilerFlags.contains("-flto")
|
|
check result.buildConfig.configureFlags.contains("--with-http_ssl_module")
|
|
|
|
test "Build synthesis with different architectures":
|
|
## Test build synthesis with different target architectures
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize for x86_64
|
|
let resultX86 = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
targetArchitecture = "x86_64"
|
|
)
|
|
|
|
# Synthesize for aarch64
|
|
let resultARM = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
targetArchitecture = "aarch64"
|
|
)
|
|
|
|
# Build hashes should be different
|
|
check resultX86.buildHash != resultARM.buildHash
|
|
check resultX86.buildConfig.targetArchitecture == "x86_64"
|
|
check resultARM.buildConfig.targetArchitecture == "aarch64"
|
|
|
|
test "Build synthesis with different libc":
|
|
## Test build synthesis with different libc types
|
|
|
|
# Create variant profile
|
|
var profile = newVariantProfile()
|
|
profile.addFlag("optimization", "lto")
|
|
profile.calculateHash()
|
|
|
|
# Synthesize with musl
|
|
let resultMusl = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
libc = "musl"
|
|
)
|
|
|
|
# Synthesize with glibc
|
|
let resultGlibc = synthesizeBuild(
|
|
packageName = "nginx",
|
|
packageVersion = "1.24.0",
|
|
variantProfile = profile,
|
|
sourceHash = "blake3-abc123",
|
|
libc = "glibc"
|
|
)
|
|
|
|
# Build hashes should be different
|
|
check resultMusl.buildHash != resultGlibc.buildHash
|
|
check resultMusl.buildConfig.libc == "musl"
|
|
check resultGlibc.buildConfig.libc == "glibc"
|