nip/tests/test_build_synthesis.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"