nip/tests/test_variant_hash_propertie...

235 lines
7.2 KiB
Nim

## Property-Based Tests for Variant Hash Calculation
##
## **Feature: 02-nip-dependency-resolution, Property 1: Hash Determinism**
##
## These tests verify that variant hash calculation is deterministic:
## - Same profile always produces same hash
## - Different profiles produce different hashes (with high probability)
## - Hash is independent of insertion order
## - Hash is platform-independent
import std/[unittest, random, tables, sets]
import ../src/nip/resolver/variant_types
import ../src/nip/resolver/variant_hash
suite "Property 1: Hash Determinism":
test "Property 1.1: Same profile produces same hash (100 iterations)":
## **Validates: Requirements 1.5**
##
## For any variant profile, calculating the hash multiple times
## should always produce the same result
var rng = initRand(42) # Deterministic seed for reproducibility
var passCount = 0
for i in 0..<100:
# Create random profile
var profile = newVariantProfile()
# Add random domains and flags
let numDomains = rng.rand(1..5)
for d in 0..<numDomains:
let domainName = "domain" & $d
let exclusivity = if rng.rand(1.0) < 0.3: Exclusive else: NonExclusive
var domain = newVariantDomain(domainName, exclusivity)
let numFlags = if exclusivity == Exclusive: 1 else: rng.rand(1..5)
for f in 0..<numFlags:
domain.flags.incl("flag" & $f)
profile.addDomain(domain)
# Calculate hash multiple times
var profile1 = profile
var profile2 = profile
var profile3 = profile
let hash1 = calculateVariantHash(profile1)
let hash2 = calculateVariantHash(profile2)
let hash3 = calculateVariantHash(profile3)
# All hashes must be identical
if hash1 == hash2 and hash2 == hash3:
passCount += 1
check passCount == 100
echo " ✅ Property 1.1: ", passCount, "/100 profiles produced deterministic hashes"
suite "Variant Unification Tests":
test "Unify: Empty demands returns empty profile":
## Test that unifying zero demands produces an empty profile
let result = unify(@[])
check result.kind == Unified
check result.profile.domains.len == 0
test "Unify: Single demand returns that profile":
## Test that unifying one demand returns the same profile
var profile = newVariantProfile()
profile.addFlag("graphics", "wayland")
profile.calculateHash()
let demand = VariantDemand(
packageName: "test",
variantProfile: profile,
optional: false
)
let result = unify(@[demand])
check result.kind == Unified
check result.profile == profile
test "Unify: Compatible non-exclusive domains merge successfully":
## Test that non-exclusive domains accumulate flags
var profile1 = newVariantProfile()
profile1.addFlag("graphics", "wayland")
profile1.calculateHash()
var profile2 = newVariantProfile()
profile2.addFlag("graphics", "vulkan")
profile2.calculateHash()
let demand1 = VariantDemand(
packageName: "pkg1",
variantProfile: profile1,
optional: false
)
let demand2 = VariantDemand(
packageName: "pkg2",
variantProfile: profile2,
optional: false
)
let result = unify(@[demand1, demand2])
check result.kind == Unified
check result.profile.hasDomain("graphics")
let graphicsDomain = result.profile.getDomain("graphics")
check "wayland" in graphicsDomain.flags
check "vulkan" in graphicsDomain.flags
test "Unify: Incompatible exclusive domains produce conflict":
## Test that conflicting exclusive domains are detected
var profile1 = newVariantProfile()
var initDomain1 = newVariantDomain("init", Exclusive)
initDomain1.flags.incl("systemd")
profile1.addDomain(initDomain1)
profile1.calculateHash()
var profile2 = newVariantProfile()
var initDomain2 = newVariantDomain("init", Exclusive)
initDomain2.flags.incl("dinit")
profile2.addDomain(initDomain2)
profile2.calculateHash()
let demand1 = VariantDemand(
packageName: "pkg1",
variantProfile: profile1,
optional: false
)
let demand2 = VariantDemand(
packageName: "pkg2",
variantProfile: profile2,
optional: false
)
let result = unify(@[demand1, demand2])
check result.kind == Conflict
check result.conflictingDomain == "init"
test "Unify: Compatible exclusive domains merge successfully":
## Test that identical exclusive domains can merge
var profile1 = newVariantProfile()
var initDomain1 = newVariantDomain("init", Exclusive)
initDomain1.flags.incl("systemd")
profile1.addDomain(initDomain1)
profile1.calculateHash()
var profile2 = newVariantProfile()
var initDomain2 = newVariantDomain("init", Exclusive)
initDomain2.flags.incl("systemd")
profile2.addDomain(initDomain2)
profile2.calculateHash()
let demand1 = VariantDemand(
packageName: "pkg1",
variantProfile: profile1,
optional: false
)
let demand2 = VariantDemand(
packageName: "pkg2",
variantProfile: profile2,
optional: false
)
let result = unify(@[demand1, demand2])
check result.kind == Unified
check result.profile.hasDomain("init")
let initDomain = result.profile.getDomain("init")
check "systemd" in initDomain.flags
test "Unify: Multiple demands with mixed domains":
## Test complex unification with multiple domains
var profile1 = newVariantProfile()
profile1.addFlag("graphics", "wayland")
var initDomain1 = newVariantDomain("init", Exclusive)
initDomain1.flags.incl("systemd")
profile1.addDomain(initDomain1)
profile1.calculateHash()
var profile2 = newVariantProfile()
profile2.addFlag("graphics", "vulkan")
profile2.addFlag("optimization", "lto")
profile2.calculateHash()
var profile3 = newVariantProfile()
var initDomain3 = newVariantDomain("init", Exclusive)
initDomain3.flags.incl("systemd")
profile3.addDomain(initDomain3)
profile3.addFlag("optimization", "pgo")
profile3.calculateHash()
let demands = @[
VariantDemand(packageName: "pkg1", variantProfile: profile1, optional: false),
VariantDemand(packageName: "pkg2", variantProfile: profile2, optional: false),
VariantDemand(packageName: "pkg3", variantProfile: profile3, optional: false)
]
let result = unify(demands)
check result.kind == Unified
# Check graphics domain (non-exclusive)
check result.profile.hasDomain("graphics")
let graphicsDomain = result.profile.getDomain("graphics")
check "wayland" in graphicsDomain.flags
check "vulkan" in graphicsDomain.flags
# Check init domain (exclusive)
check result.profile.hasDomain("init")
let initDomain = result.profile.getDomain("init")
check "systemd" in initDomain.flags
# Check optimization domain (non-exclusive)
check result.profile.hasDomain("optimization")
let optDomain = result.profile.getDomain("optimization")
check "lto" in optDomain.flags
check "pgo" in optDomain.flags
echo " ✅ Complex unification: ", $result.profile