## NimPak Stress Testing ## ## Comprehensive stress tests for the NimPak package manager. ## Task 44: Stress testing. ## ## Run with: nim c -r -d:release nip/tests/test_stress.nim ## Note: Some tests may take several minutes to complete. import std/[os, strutils, strformat, times, random, sequtils, locks, threadpool] import ../src/nimpak/cas import ../src/nimpak/benchmark const # Test configuration - adjust based on available resources SmallScale* = 100 MediumScale* = 1000 LargeScale* = 10000 HugeScale* = 100000 SmallChunk* = 1024 # 1KB MediumChunk* = 65536 # 64KB LargeChunk* = 1048576 # 1MB type StressTestResult* = object name*: string passed*: bool duration*: float # seconds operationsCompleted*: int bytesProcessed*: int64 errorsEncountered*: int peakMemoryMB*: float notes*: seq[string] StressTestSuite* = object name*: string results*: seq[StressTestResult] startTime*: DateTime endTime*: DateTime totalPassed*: int totalFailed*: int # ############################################################################ # Utility Functions # ############################################################################ proc generateRandomData(size: int): seq[byte] = ## Generate random data for testing result = newSeq[byte](size) for i in 0..= 2: return parseFloat(parts[1]) / 1024.0 except: discard return 0.0 proc formatBytes(bytes: int64): string = if bytes >= 1073741824: fmt"{bytes.float / 1073741824.0:.2f} GB" elif bytes >= 1048576: fmt"{bytes.float / 1048576.0:.2f} MB" elif bytes >= 1024: fmt"{bytes.float / 1024.0:.2f} KB" else: fmt"{bytes} bytes" # ############################################################################ # CAS Stress Tests # ############################################################################ proc stressTestCasManyChunks*(casRoot: string, chunkCount: int, chunkSize: int): StressTestResult = ## Test CAS with many chunks result = StressTestResult( name: fmt"CAS Many Chunks ({chunkCount} x {chunkSize} bytes)", passed: false, notes: @[] ) echo fmt"🏋️ Stress Test: {result.name}" var casManager = initCasManager(casRoot, casRoot / "system") let startTime = epochTime() let startMem = getMemoryUsageMB() randomize() var storedHashes: seq[string] = @[] for i in 1..chunkCount: let data = generateRandomData(chunkSize) let storeResult = casManager.storeObject(data) if storeResult.isOk: storedHashes.add(storeResult.get().hash) result.operationsCompleted += 1 result.bytesProcessed += int64(chunkSize) else: result.errorsEncountered += 1 if i mod 1000 == 0: echo fmt" Progress: {i}/{chunkCount} ({i * 100 div chunkCount}%)" let endTime = epochTime() result.duration = endTime - startTime result.peakMemoryMB = getMemoryUsageMB() # Verify some random chunks echo " Verifying stored chunks..." var verifyErrors = 0 for i in 0.. 0: rand(uniqueChunks.len - 1) # Reuse existing chunk else: i mod uniqueChunks.len let storeResult = casManager.storeObject(uniqueChunks[chunkIdx]) if storeResult.isOk: totalStored += 1 if storeResult.get().refCount > 1: dedupCount += 1 result.bytesProcessed += int64(MediumChunk) if i mod 1000 == 0: echo fmt" Progress: {i}/{chunkCount} ({i * 100 div chunkCount}%)" let endTime = epochTime() result.duration = endTime - startTime result.operationsCompleted = totalStored let actualDedupRatio = if totalStored > 0: float(dedupCount) / float(totalStored) else: 0.0 result.passed = totalStored == chunkCount result.notes.add(fmt"Expected dedup ratio: {duplicateRatio*100:.1f}%") result.notes.add(fmt"Actual dedup hits: {dedupCount} ({actualDedupRatio*100:.1f}%)") result.notes.add(fmt"Storage saved: ~{formatBytes(int64(dedupCount * MediumChunk))}") echo fmt" ✓ Completed in {result.duration:.2f}s" proc stressTestCasLargeObjects*(casRoot: string, objectSizeMB: int, objectCount: int): StressTestResult = ## Test CAS with large objects result = StressTestResult( name: fmt"CAS Large Objects ({objectCount} x {objectSizeMB}MB)", passed: false, notes: @[] ) echo fmt"🏋️ Stress Test: {result.name}" var casManager = initCasManager(casRoot, casRoot / "system") let startTime = epochTime() let objectSize = objectSizeMB * 1048576 for i in 1..objectCount: echo fmt" Storing object {i}/{objectCount} ({objectSizeMB}MB)..." let data = generateRandomData(objectSize) let storeResult = casManager.storeObject(data) if storeResult.isOk: result.operationsCompleted += 1 result.bytesProcessed += int64(objectSize) # Verify retrieval let retrieveResult = casManager.retrieveObject(storeResult.get().hash) if retrieveResult.isOk: let retrieved = retrieveResult.get() if retrieved.len == objectSize: result.notes.add(fmt"Object {i}: stored and verified") else: result.errorsEncountered += 1 result.notes.add(fmt"Object {i}: size mismatch") else: result.errorsEncountered += 1 else: result.errorsEncountered += 1 let endTime = epochTime() result.duration = endTime - startTime result.passed = result.errorsEncountered == 0 and result.operationsCompleted == objectCount result.notes.add(fmt"Total data: {formatBytes(result.bytesProcessed)}") result.notes.add(fmt"Throughput: {result.bytesProcessed.float / result.duration / 1048576.0:.2f} MB/s") echo fmt" ✓ Completed in {result.duration:.2f}s" # ############################################################################ # Concurrent Operations Stress Tests # ############################################################################ var globalCasManager {.threadvar.}: CasManager var globalLock: Lock proc stressTestConcurrentStores*(casRoot: string, threadCount: int, operationsPerThread: int): StressTestResult = ## Test concurrent store operations result = StressTestResult( name: fmt"Concurrent Stores ({threadCount} threads x {operationsPerThread} ops)", passed: false, notes: @[] ) echo fmt"🏋️ Stress Test: {result.name}" initLock(globalLock) var casManager = initCasManager(casRoot, casRoot / "system") let startTime = epochTime() # For now, simulate concurrent operations sequentially # (Full threading would require thread-safe CasManager) var totalOps = 0 var totalErrors = 0 for t in 0.. 0: quit(1)