98% * - Database Query: <30ms * - Memory Usage: <8MB * - CSS Injection: <50ms * - FOUC Prevention: >98% */ final class PerformanceBenchmarkTest extends TestCase { private CacheManager $cacheManager; private QueryOptimizer $queryOptimizer; private MemoryManager $memoryManager; private ResponseOptimizer $responseOptimizer; private CssInjectionService $cssInjectionService; private PerformanceTracker $performanceTracker; // Performance targets (same as in PerformanceTracker) private const TARGETS = [ 'page_load_overhead' => 1.5, // <1.5% overhead 'ajax_response_time' => 75, // <75ms 'cache_hit_ratio' => 98, // >98% 'database_query_time' => 30, // <30ms 'memory_usage' => 8388608, // <8MB 'css_injection_time' => 50, // <50ms 'fouc_prevention_rate' => 98 // >98% ]; protected function setUp(): void { parent::setUp(); // Initialize performance components $this->cacheManager = CacheManager::getInstance(); $this->memoryManager = MemoryManager::getInstance(); $this->queryOptimizer = new QueryOptimizer($this->cacheManager); $this->responseOptimizer = new ResponseOptimizer($this->cacheManager); $this->cssInjectionService = new CssInjectionService($this->cacheManager); $this->performanceTracker = new PerformanceTracker( $this->cacheManager, $this->queryOptimizer, $this->memoryManager, $this->responseOptimizer ); // Warm up caches for consistent testing $this->warmUpSystem(); } /** * Test CSS injection performance target (<50ms) * * @test */ public function testCssInjectionPerformanceTarget(): void { $restrictions = $this->generateTestRestrictions(50); $iterations = 10; $executionTimes = []; for ($i = 0; $i < $iterations; $i++) { $startTime = microtime(true); $result = $this->cssInjectionService->injectRestrictionCss($restrictions, [ 'page_type' => 'appointment_form', 'use_cache' => true, 'enable_fouc_prevention' => true ]); $executionTime = (microtime(true) - $startTime) * 1000; $executionTimes[] = $executionTime; $this->assertTrue($result['css_generated'], 'CSS should be generated successfully'); $this->assertTrue($result['fouc_prevention'], 'FOUC prevention should be enabled'); } $averageTime = array_sum($executionTimes) / count($executionTimes); $maxTime = max($executionTimes); $this->assertLessThan( self::TARGETS['css_injection_time'], $averageTime, "CSS injection average time ({$averageTime}ms) should be less than " . self::TARGETS['css_injection_time'] . "ms" ); $this->assertLessThan( self::TARGETS['css_injection_time'] * 1.5, // Allow 50% buffer for max time $maxTime, "CSS injection max time ({$maxTime}ms) should be within acceptable range" ); // Test FOUC prevention rate $foucPreventionResults = array_column($executionTimes, function() { return true; }); $foucPreventionRate = (count($foucPreventionResults) / $iterations) * 100; $this->assertGreaterThan( self::TARGETS['fouc_prevention_rate'], $foucPreventionRate, "FOUC prevention rate ({$foucPreventionRate}%) should be greater than " . self::TARGETS['fouc_prevention_rate'] . "%" ); } /** * Test AJAX response optimization target (<75ms) * * @test */ public function testAjaxResponsePerformanceTarget(): void { $testData = $this->generateTestResponseData(1000); // 1000 items $iterations = 20; $executionTimes = []; for ($i = 0; $i < $iterations; $i++) { $startTime = microtime(true); $optimizedResponse = $this->responseOptimizer->optimizeResponse($testData, [ 'use_cache' => true, 'compress' => true, 'remove_nulls' => true, 'optimize_numbers' => true ]); $executionTime = (microtime(true) - $startTime) * 1000; $executionTimes[] = $executionTime; $this->assertArrayHasKey('data', $optimizedResponse); $this->assertArrayHasKey('success', $optimizedResponse); $this->assertTrue($optimizedResponse['success']); } $averageTime = array_sum($executionTimes) / count($executionTimes); $percentile95 = $this->calculatePercentile($executionTimes, 95); $this->assertLessThan( self::TARGETS['ajax_response_time'], $averageTime, "AJAX response average time ({$averageTime}ms) should be less than " . self::TARGETS['ajax_response_time'] . "ms" ); $this->assertLessThan( self::TARGETS['ajax_response_time'] * 1.3, // 95th percentile can be 30% higher $percentile95, "AJAX response 95th percentile ({$percentile95}ms) should be within acceptable range" ); } /** * Test cache performance target (>98% hit ratio) * * @test */ public function testCachePerformanceTarget(): void { // Pre-populate cache with test data $cacheKeys = []; for ($i = 0; $i < 100; $i++) { $key = "test_key_{$i}"; $data = $this->generateTestData($i); $this->cacheManager->set($key, $data, 3600); $cacheKeys[] = $key; } // Test cache hits $hits = 0; $totalRequests = 1000; for ($i = 0; $i < $totalRequests; $i++) { // 90% requests should hit existing cache keys if ($i < $totalRequests * 0.9) { $key = $cacheKeys[array_rand($cacheKeys)]; } else { $key = "non_existent_key_{$i}"; } $result = $this->cacheManager->get($key); if ($result !== null) { $hits++; } } $hitRatio = ($hits / $totalRequests) * 100; $this->assertGreaterThan( self::TARGETS['cache_hit_ratio'], $hitRatio, "Cache hit ratio ({$hitRatio}%) should be greater than " . self::TARGETS['cache_hit_ratio'] . "%" ); // Test cache performance metrics $cacheMetrics = $this->cacheManager->getMetrics(); $this->assertArrayHasKey('hit_rate', $cacheMetrics); $this->assertArrayHasKey('average_response_time', $cacheMetrics); $this->assertLessThan(5, $cacheMetrics['average_response_time'], 'Cache average response time should be under 5ms'); } /** * Test database query performance target (<30ms) * * @test */ public function testDatabaseQueryPerformanceTarget(): void { $iterations = 50; $executionTimes = []; for ($i = 0; $i < $iterations; $i++) { $startTime = microtime(true); // Test various query types switch ($i % 4) { case 0: $result = $this->queryOptimizer->getRestrictions([ 'type' => 'doctor', 'active' => true ]); break; case 1: $result = $this->queryOptimizer->getDoctorAvailability(1, [ 'start' => date('Y-m-d'), 'end' => date('Y-m-d', strtotime('+7 days')) ]); break; case 2: $result = $this->queryOptimizer->getRestrictions([ 'type' => 'service', 'target_id' => rand(1, 100) ]); break; case 3: $result = $this->queryOptimizer->getRestrictions(); break; } $executionTime = (microtime(true) - $startTime) * 1000; $executionTimes[] = $executionTime; $this->assertIsArray($result); } $averageTime = array_sum($executionTimes) / count($executionTimes); $maxTime = max($executionTimes); $this->assertLessThan( self::TARGETS['database_query_time'], $averageTime, "Database query average time ({$averageTime}ms) should be less than " . self::TARGETS['database_query_time'] . "ms" ); $this->assertLessThan( self::TARGETS['database_query_time'] * 2, // Max time can be 2x average $maxTime, "Database query max time ({$maxTime}ms) should be within acceptable range" ); // Test query optimization metrics $queryMetrics = $this->queryOptimizer->getPerformanceMetrics(); $this->assertArrayHasKey('cache_hit_rate', $queryMetrics); $this->assertArrayHasKey('average_execution_time', $queryMetrics); $this->assertGreaterThan(80, $queryMetrics['cache_hit_rate'], 'Query cache hit rate should be above 80%'); } /** * Test memory usage target (<8MB) * * @test */ public function testMemoryUsageTarget(): void { $initialMemory = memory_get_usage(true); // Simulate heavy operations $operations = [ 'css_generation' => 50, 'ajax_responses' => 100, 'cache_operations' => 200, 'database_queries' => 75 ]; foreach ($operations as $operation => $count) { for ($i = 0; $i < $count; $i++) { switch ($operation) { case 'css_generation': $this->cssInjectionService->generateCriticalCss( $this->generateTestRestrictions(10) ); break; case 'ajax_responses': $this->responseOptimizer->optimizeResponse( $this->generateTestResponseData(50), ['use_cache' => false] // Force processing ); break; case 'cache_operations': $key = "memory_test_{$i}"; $data = $this->generateTestData($i); $this->cacheManager->set($key, $data); $this->cacheManager->get($key); break; case 'database_queries': $this->queryOptimizer->getRestrictions([ 'type' => 'doctor', 'target_id' => $i ]); break; } } } $memoryStatus = $this->memoryManager->checkMemoryStatus(); $currentMemory = $memoryStatus['current_usage']; $memoryDelta = $currentMemory - $initialMemory; $this->assertLessThan( self::TARGETS['memory_usage'], $currentMemory, "Current memory usage ({$currentMemory} bytes) should be less than " . self::TARGETS['memory_usage'] . " bytes" ); $this->assertLessThan( self::TARGETS['memory_usage'] * 0.5, // Memory growth should be less than 4MB $memoryDelta, "Memory growth ({$memoryDelta} bytes) should be minimal" ); // Test memory cleanup $this->memoryManager->optimizeMemoryUsage(); $cleanupStatus = $this->memoryManager->checkMemoryStatus(); $this->assertLessThanOrEqual( $currentMemory, $cleanupStatus['current_usage'], 'Memory usage should not increase after cleanup' ); } /** * Test page load overhead target (<1.5%) * * @test */ public function testPageLoadOverheadTarget(): void { // Baseline measurement (without plugin) $baselineIterations = 20; $baselineTimes = []; for ($i = 0; $i < $baselineIterations; $i++) { $startTime = microtime(true); // Simulate baseline page load operations $this->simulateBaselinePageLoad(); $executionTime = (microtime(true) - $startTime) * 1000; $baselineTimes[] = $executionTime; } $baselineAverage = array_sum($baselineTimes) / count($baselineTimes); // Plugin measurement (with plugin active) $pluginIterations = 20; $pluginTimes = []; for ($i = 0; $i < $pluginIterations; $i++) { $startTime = microtime(true); // Simulate page load with plugin operations $this->simulatePluginPageLoad(); $executionTime = (microtime(true) - $startTime) * 1000; $pluginTimes[] = $executionTime; } $pluginAverage = array_sum($pluginTimes) / count($pluginTimes); $overhead = (($pluginAverage - $baselineAverage) / $baselineAverage) * 100; $this->assertLessThan( self::TARGETS['page_load_overhead'], $overhead, "Page load overhead ({$overhead}%) should be less than " . self::TARGETS['page_load_overhead'] . "%" ); // Additional validation $this->assertLessThan(200, $pluginAverage, 'Plugin page load time should be under 200ms'); $this->assertGreaterThan(0, $baselineAverage, 'Baseline measurement should be valid'); } /** * Test batch operations performance * * @test */ public function testBatchOperationsPerformance(): void { $batchRequests = []; for ($i = 0; $i < 10; $i++) { $batchRequests[] = [ 'action' => 'get_restrictions', 'params' => ['type' => 'doctor', 'target_id' => $i] ]; } $startTime = microtime(true); $batchResult = $this->responseOptimizer->batchRequests($batchRequests); $executionTime = (microtime(true) - $startTime) * 1000; $this->assertArrayHasKey('responses', $batchResult); $this->assertArrayHasKey('execution_time', $batchResult); $this->assertEquals(10, $batchResult['requests_count']); // Batch should be more efficient than individual requests $expectedIndividualTime = count($batchRequests) * 20; // 20ms per request estimate $this->assertLessThan($expectedIndividualTime, $executionTime, 'Batch processing should be more efficient'); // Each response in batch should be fast $avgResponseTime = $batchResult['execution_time'] / $batchResult['requests_count']; $this->assertLessThan(50, $avgResponseTime, 'Average batch response time should be under 50ms'); } /** * Test comprehensive performance dashboard * * @test */ public function testPerformanceDashboard(): void { // Generate some activity for the dashboard for ($i = 0; $i < 20; $i++) { $this->performanceTracker->recordMetric('test_metric', rand(10, 100)); $this->performanceTracker->recordMetric('ajax_response_time', rand(30, 70)); $this->performanceTracker->recordMetric('cache_hit_ratio', rand(95, 100)); } $dashboard = $this->performanceTracker->getPerformanceDashboard(); $this->assertArrayHasKey('summary', $dashboard); $this->assertArrayHasKey('targets_status', $dashboard); $this->assertArrayHasKey('component_metrics', $dashboard); $this->assertArrayHasKey('recommendations', $dashboard); // Validate component metrics $components = $dashboard['component_metrics']; $this->assertArrayHasKey('cache', $components); $this->assertArrayHasKey('database', $components); $this->assertArrayHasKey('memory', $components); $this->assertArrayHasKey('ajax', $components); // Validate targets status $targets = $dashboard['targets_status']; foreach (self::TARGETS as $targetName => $targetValue) { if (isset($targets[$targetName])) { $this->assertArrayHasKey('target', $targets[$targetName]); $this->assertArrayHasKey('current', $targets[$targetName]); $this->assertArrayHasKey('achieved', $targets[$targetName]); } } } /** * Test performance regression detection * * @test */ public function testPerformanceRegressionDetection(): void { // Simulate normal performance metrics for ($i = 0; $i < 50; $i++) { $this->performanceTracker->recordMetric('ajax_response_time', rand(40, 60)); } // Simulate performance regression for ($i = 0; $i < 10; $i++) { $this->performanceTracker->recordMetric('ajax_response_time', rand(90, 120)); } $trends = $this->performanceTracker->analyzePerformanceTrends([ ['label' => 'Last Hour', 'seconds' => 3600] ]); $this->assertArrayHasKey('trends_by_period', $trends); $this->assertArrayHasKey('Last Hour', $trends['trends_by_period']); $lastHourTrend = $trends['trends_by_period']['Last Hour']; $this->assertArrayHasKey('regression_alerts', $lastHourTrend); $this->assertArrayHasKey('improvement_rate', $lastHourTrend); } /** * Helper methods for testing */ private function warmUpSystem(): void { // Pre-populate caches for ($i = 0; $i < 10; $i++) { $this->cacheManager->set("warmup_key_{$i}", $this->generateTestData($i)); } // Execute some queries to warm up optimizer $this->queryOptimizer->getRestrictions(['type' => 'doctor']); // Initialize memory pools $this->memoryManager->checkMemoryStatus(); } private function generateTestRestrictions(int $count): array { $restrictions = []; for ($i = 0; $i < $count; $i++) { $restrictions[] = [ 'id' => $i, 'type' => rand(0, 1) ? 'doctor' : 'service', 'target_id' => rand(1, 100), 'is_active' => true, 'hide_method' => 'display' ]; } return $restrictions; } private function generateTestResponseData(int $itemCount): array { $data = []; for ($i = 0; $i < $itemCount; $i++) { $data[] = [ 'id' => $i, 'name' => "Test Item {$i}", 'status' => 'active', 'metadata' => [ 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), 'tags' => ['tag1', 'tag2', 'tag3'] ] ]; } return ['items' => $data, 'total' => $itemCount]; } private function generateTestData(int $seed): array { return [ 'id' => $seed, 'data' => str_repeat("test_data_{$seed}_", 10), 'timestamp' => time(), 'random' => rand(1, 1000) ]; } private function calculatePercentile(array $values, int $percentile): float { sort($values); $index = ceil(($percentile / 100) * count($values)) - 1; return $values[$index] ?? 0; } private function simulateBaselinePageLoad(): void { // Simulate basic WordPress operations without plugin for ($i = 0; $i < 5; $i++) { $data = str_repeat('baseline_operation_', 100); unset($data); } usleep(rand(50, 100) * 1000); // 50-100ms simulation } private function simulatePluginPageLoad(): void { // Simulate page load with plugin operations $restrictions = $this->generateTestRestrictions(5); $this->cssInjectionService->generateCriticalCss($restrictions); // Cache some data $this->cacheManager->set('page_load_test', $this->generateTestData(1)); $this->cacheManager->get('page_load_test'); usleep(rand(50, 100) * 1000); // 50-100ms simulation } }