monitor = new GarbageCollectionMonitor(); }); it('returns current GC status', function () { $status = $this->monitor->getStatus(); expect($status)->toBeInstanceOf(GcStatus::class); expect($status->runs)->toBeInt(); expect($status->collected)->toBeInt(); expect($status->threshold)->toBeGreaterThan(0); expect($status->roots)->toBeInt(); }); it('gets number of GC runs', function () { $runs = $this->monitor->getRuns(); expect($runs)->toBeInt(); expect($runs)->toBeGreaterThanOrEqual(0); }); it('gets number of collected cycles', function () { $collected = $this->monitor->getCollected(); expect($collected)->toBeInt(); expect($collected)->toBeGreaterThanOrEqual(0); }); it('gets GC threshold', function () { $threshold = $this->monitor->getThreshold(); expect($threshold)->toBeInt(); expect($threshold)->toBeGreaterThan(0); }); it('checks if GC is running', function () { $isRunning = $this->monitor->isRunning(); expect($isRunning)->toBeBool(); }); it('checks if GC is enabled', function () { $isEnabled = $this->monitor->isEnabled(); expect($isEnabled)->toBeBool(); }); it('can force garbage collection', function () { // Create some circular references to collect $obj1 = new stdClass(); $obj2 = new stdClass(); $obj1->ref = $obj2; $obj2->ref = $obj1; unset($obj1, $obj2); $result = $this->monitor->forceCollection(); expect($result)->toBeInstanceOf(GcResult::class); expect($result->collected)->toBeInt(); expect($result->duration)->toBeInstanceOf(App\Framework\Core\ValueObjects\Duration::class); expect($result->memoryFreed)->toBeInstanceOf(App\Framework\Core\ValueObjects\Byte::class); expect($result->statusBefore)->toBeInstanceOf(GcStatus::class); expect($result->statusAfter)->toBeInstanceOf(GcStatus::class); expect($result->timestamp)->toBeInstanceOf(App\Framework\Core\ValueObjects\Timestamp::class); }); it('provides efficiency metrics', function () { $metrics = $this->monitor->getEfficiencyMetrics(); expect($metrics)->toHaveKeys([ 'collection_rate', 'threshold_ratio', 'is_healthy', 'runs', 'collected', 'roots', 'threshold', ]); expect($metrics['collection_rate'])->toBeFloat(); expect($metrics['threshold_ratio'])->toBeFloat(); expect($metrics['is_healthy'])->toBeBool(); }); it('can take GC snapshot', function () { $snapshot = $this->monitor->takeSnapshot('test_checkpoint'); expect($snapshot)->toHaveKeys(['label', 'timestamp', 'status', 'efficiency']); expect($snapshot['label'])->toBe('test_checkpoint'); expect($snapshot['status'])->toBeArray(); expect($snapshot['efficiency'])->toBeArray(); }); it('can enable and disable GC', function () { $this->monitor->disable(); expect(gc_enabled())->toBeFalse(); $this->monitor->enable(); expect(gc_enabled())->toBeTrue(); }); }); describe('GcStatus', function () { it('creates from gc_status array', function () { $status = GcStatus::fromGcStatus(gc_status()); expect($status)->toBeInstanceOf(GcStatus::class); expect($status->runs)->toBeInt(); expect($status->collected)->toBeInt(); expect($status->threshold)->toBeInt(); expect($status->roots)->toBeInt(); }); it('creates current GC status', function () { $status = GcStatus::current(); expect($status)->toBeInstanceOf(GcStatus::class); }); it('calculates collection efficiency', function () { $status = new GcStatus(runs: 10, collected: 50, threshold: 10001, roots: 100); $efficiency = $status->getCollectionEfficiency(); expect($efficiency)->toBe(5.0); // 50 / 10 }); it('returns zero efficiency when no runs', function () { $status = new GcStatus(runs: 0, collected: 0, threshold: 10001, roots: 0); expect($status->getCollectionEfficiency())->toBe(0.0); }); it('calculates threshold utilization', function () { $status = new GcStatus(runs: 10, collected: 50, threshold: 1000, roots: 900); $utilization = $status->getThresholdUtilization(); expect($utilization)->toBe(0.9); // 900 / 1000 }); it('checks if near threshold', function () { $nearThreshold = new GcStatus(runs: 10, collected: 50, threshold: 1000, roots: 950); $notNearThreshold = new GcStatus(runs: 10, collected: 50, threshold: 1000, roots: 500); expect($nearThreshold->isNearThreshold(0.9))->toBeTrue(); expect($notNearThreshold->isNearThreshold(0.9))->toBeFalse(); }); it('determines if GC is healthy', function () { $healthy = new GcStatus(runs: 10, collected: 50, threshold: 10000, roots: 100); $unhealthy = new GcStatus(runs: 0, collected: 0, threshold: 1000, roots: 950); expect($healthy->isHealthy())->toBeTrue(); expect($unhealthy->isHealthy())->toBeFalse(); }); it('converts to array', function () { $status = new GcStatus(runs: 10, collected: 50, threshold: 10001, roots: 100); $array = $status->toArray(); expect($array)->toHaveKeys([ 'runs', 'collected', 'threshold', 'roots', 'running', 'collection_efficiency', 'threshold_utilization', 'is_healthy', ]); }); }); describe('GcResult', function () { it('determines if GC was effective', function () { $effective = new GcResult( collected: 10, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(1024), statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 10, threshold: 10001, roots: 90), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); $ineffective = new GcResult( collected: 0, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(0), statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 0, threshold: 10001, roots: 100), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); expect($effective->wasEffective())->toBeTrue(); expect($ineffective->wasEffective())->toBeFalse(); }); it('gets GC overhead in milliseconds', function () { $result = new GcResult( collected: 5, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(12.5), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(2048), statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 5, threshold: 10001, roots: 95), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); expect($result->getOverheadMs())->toBeFloat(); expect($result->getOverheadMs())->toBeGreaterThan(0); }); it('gets memory freed in megabytes', function () { $result = new GcResult( collected: 5, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(1048576), // 1 MB statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 5, threshold: 10001, roots: 95), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); expect($result->getMemoryFreedMB())->toBe(1.0); }); it('calculates efficiency score', function () { $result = new GcResult( collected: 5, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(1048576), statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 5, threshold: 10001, roots: 95), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); $score = $result->getEfficiencyScore(); expect($score)->toBeFloat(); expect($score)->toBeGreaterThanOrEqual(0); expect($score)->toBeLessThanOrEqual(100); }); it('gets status changes', function () { $result = new GcResult( collected: 5, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(1024), statusBefore: new GcStatus(runs: 1, collected: 10, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 15, threshold: 10001, roots: 95), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); $changes = $result->getStatusChanges(); expect($changes)->toHaveKeys(['runs_delta', 'collected_delta', 'roots_delta']); expect($changes['runs_delta'])->toBe(1); expect($changes['collected_delta'])->toBe(5); expect($changes['roots_delta'])->toBe(-5); }); it('converts to array', function () { $result = new GcResult( collected: 5, duration: App\Framework\Core\ValueObjects\Duration::fromMilliseconds(5.0), memoryFreed: App\Framework\Core\ValueObjects\Byte::fromBytes(1024), statusBefore: new GcStatus(runs: 1, collected: 0, threshold: 10001, roots: 100), statusAfter: new GcStatus(runs: 2, collected: 5, threshold: 10001, roots: 95), timestamp: App\Framework\Core\ValueObjects\Timestamp::now() ); $array = $result->toArray(); expect($array)->toHaveKeys([ 'collected', 'duration_ms', 'memory_freed_bytes', 'memory_freed_mb', 'timestamp', 'was_effective', 'efficiency_score', 'status_before', 'status_after', 'status_changes', ]); }); });