recordRender('user-stats', 45.5, false); $collector->recordRender('dashboard', 12.3, true); $collector->recordAction('user-stats', 'refresh', 30.0, true); $collector->recordAction('dashboard', 'increment', 15.5, false); $collector->recordCacheHit('user-stats', true); $collector->recordCacheHit('user-stats', false); $collector->recordEventDispatched('user-stats', 'data.updated'); $collector->recordEventReceived('dashboard', 'user.stats.changed'); $summary = $collector->getSummary(); if ($summary['total_renders'] === 2 && $summary['total_actions'] === 2 && $summary['cache_hits'] === 1 && $summary['cache_misses'] === 1 && $summary['total_events'] === 1 && $summary['action_errors'] === 1) { echo " ✓ Metrics API provides correct summary data\n"; } else { echo " ✗ FAILED: Metrics API summary incorrect\n"; } // Test 2: Prometheus Export Integration echo "\nTest 2: Prometheus Export Integration\n"; $prometheus = $collector->exportPrometheus(); // Validate Prometheus format if (str_contains($prometheus, '# HELP') && str_contains($prometheus, '# TYPE') && str_contains($prometheus, 'livecomponent_renders_total') && str_contains($prometheus, 'livecomponent_actions_total') && str_contains($prometheus, 'livecomponent_cache_hits_total') && str_contains($prometheus, 'livecomponent_events_dispatched_total')) { echo " ✓ Prometheus export format valid\n"; } else { echo " ✗ FAILED: Prometheus export missing expected metrics\n"; } // Validate label format if (str_contains($prometheus, 'component_id="user-stats"') && str_contains($prometheus, 'component_id="dashboard"')) { echo " ✓ Prometheus labels formatted correctly\n"; } else { echo " ✗ FAILED: Prometheus label format incorrect\n"; } // Test 3: Real-time Metrics Collection echo "\nTest 3: Real-time Metrics Collection\n"; $collector->reset(); // Simulate rapid component activity (like DevTools would see) for ($i = 0; $i < 10; $i++) { $collector->recordAction('counter', 'increment', 5.0 + $i, true); } $metrics = $collector->getMetrics(); $actionMetric = $metrics['livecomponent_actions_total{action=increment,component_id=counter,status=success}']; if ($actionMetric->value === 10.0) { echo " ✓ Real-time action counting correct\n"; } else { echo " ✗ FAILED: Action count should be 10, got {$actionMetric->value}\n"; } // Test 4: Performance Tab Data echo "\nTest 4: Performance Tab Data\n"; $collector->reset(); // Simulate performance data collection $collector->recordRender('widget-1', 120.5, false); $collector->recordCacheHit('widget-1', false); // Cache miss $collector->recordRender('widget-1', 5.2, true); $collector->recordCacheHit('widget-1', true); // Cache hit $collector->recordRender('widget-2', 80.3, false); $collector->recordCacheHit('widget-2', false); // Cache miss $collector->recordAction('widget-1', 'loadData', 200.0, true); $collector->recordAction('widget-1', 'refresh', 15.0, true); $summary = $collector->getSummary(); // DevTools Performance tab needs these metrics if ($summary['total_renders'] === 3 && $summary['total_actions'] === 2 && $summary['cache_hits'] === 1 && $summary['cache_misses'] === 2) { echo " ✓ Performance tab data available\n"; } else { echo " ✗ FAILED: Performance tab data incorrect\n"; } // Test 5: Component Tree Data echo "\nTest 5: Component Tree Data\n"; $collector->reset(); // Multiple components like DevTools tree would show $components = [ 'user-profile' => [ 'renders' => 3, 'actions' => ['update' => 2, 'refresh' => 1], 'cache_hits' => 5, 'cache_misses' => 1 ], 'dashboard' => [ 'renders' => 2, 'actions' => ['loadWidgets' => 1], 'cache_hits' => 0, 'cache_misses' => 2 ], 'sidebar' => [ 'renders' => 1, 'actions' => [], 'cache_hits' => 1, 'cache_misses' => 0 ] ]; foreach ($components as $componentId => $data) { for ($i = 0; $i < $data['renders']; $i++) { $collector->recordRender($componentId, 10.0 * ($i + 1), $i % 2 === 1); } foreach ($data['actions'] as $action => $count) { for ($i = 0; $i < $count; $i++) { $collector->recordAction($componentId, $action, 20.0, true); } } for ($i = 0; $i < $data['cache_hits']; $i++) { $collector->recordCacheHit($componentId, true); } for ($i = 0; $i < $data['cache_misses']; $i++) { $collector->recordCacheHit($componentId, false); } } $summary = $collector->getSummary(); if ($summary['total_renders'] === 6 && $summary['total_actions'] === 4 && $summary['cache_hits'] === 6 && $summary['cache_misses'] === 3) { echo " ✓ Component tree data aggregated correctly\n"; } else { echo " ✗ FAILED: Component tree data incorrect\n"; echo " Expected: renders=6, actions=4, hits=6, misses=3\n"; echo " Got: renders={$summary['total_renders']}, actions={$summary['total_actions']}, "; echo "hits={$summary['cache_hits']}, misses={$summary['cache_misses']}\n"; } // Test 6: Event Log Data echo "\nTest 6: Event Log Data\n"; $collector->reset(); // Simulate event tracking $collector->recordEventDispatched('form', 'submit.started'); $collector->recordEventReceived('form', 'validation.passed'); $collector->recordEventDispatched('form', 'submit.completed'); $collector->recordEventDispatched('notification', 'show'); $collector->recordEventReceived('notification', 'shown'); $summary = $collector->getSummary(); if ($summary['total_events'] === 3) { // Only dispatched events counted in summary echo " ✓ Event log data tracked correctly\n"; } else { echo " ✗ FAILED: Event count should be 3, got {$summary['total_events']}\n"; } // Test 7: Network Timeline Data (Upload Metrics) echo "\nTest 7: Network Timeline Data (Upload Metrics)\n"; $collector->reset(); // Simulate upload tracking for network timeline $collector->recordUploadChunk('session-123', 0, 45.6, true); $collector->recordUploadChunk('session-123', 1, 42.3, true); $collector->recordUploadChunk('session-123', 2, 50.1, true); $collector->recordUploadChunk('session-456', 0, 100.0, false); // Failed chunk $collector->recordUploadComplete('session-123', 500.0, 3); $metrics = $collector->getMetrics(); if (isset($metrics['livecomponent_upload_chunks_total{session_id=session-123,status=success}']) && $metrics['livecomponent_upload_chunks_total{session_id=session-123,status=success}']->value === 3.0 && isset($metrics['livecomponent_upload_chunks_total{session_id=session-456,status=error}']) && $metrics['livecomponent_upload_chunks_total{session_id=session-456,status=error}']->value === 1.0 && isset($metrics['livecomponent_uploads_completed_total{session_id=session-123}']) && $metrics['livecomponent_uploads_completed_total{session_id=session-123}']->value === 1.0) { echo " ✓ Network timeline upload data tracked\n"; } else { echo " ✗ FAILED: Upload metrics for network timeline incorrect\n"; } // Test 8: Batch Operation Metrics echo "\nTest 8: Batch Operation Metrics\n"; $collector->reset(); // Simulate batch operations $collector->recordBatch(5, 123.45, 4, 1); $collector->recordBatch(10, 250.0, 10, 0); $collector->recordBatch(3, 50.0, 2, 1); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_batch_operations_total{status=executed}']->value === 3.0 && $metrics['livecomponent_batch_success_total']->value === 16.0 && // 4 + 10 + 2 $metrics['livecomponent_batch_failure_total']->value === 2.0) { // 1 + 0 + 1 echo " ✓ Batch operation metrics correct\n"; } else { echo " ✗ FAILED: Batch metrics incorrect\n"; } // Test 9: Fragment Update Metrics echo "\nTest 9: Fragment Update Metrics\n"; $collector->reset(); // Simulate fragment updates $collector->recordFragmentUpdate('dashboard', 3, 45.67); $collector->recordFragmentUpdate('dashboard', 5, 60.0); $collector->recordFragmentUpdate('sidebar', 2, 30.0); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_fragment_updates_total{component_id=dashboard}']->value === 2.0 && $metrics['livecomponent_fragment_updates_total{component_id=sidebar}']->value === 1.0) { echo " ✓ Fragment update metrics correct\n"; } else { echo " ✗ FAILED: Fragment metrics incorrect\n"; } // Test 10: Cache Hit Rate for Performance Display echo "\nTest 10: Cache Hit Rate for Performance Display\n"; $collector->reset(); // Simulate cache behavior with different hit rates $collector->recordCacheHit('high-hit-rate', true); $collector->recordCacheHit('high-hit-rate', true); $collector->recordCacheHit('high-hit-rate', true); $collector->recordCacheHit('high-hit-rate', false); $collector->recordCacheHit('low-hit-rate', true); $collector->recordCacheHit('low-hit-rate', false); $collector->recordCacheHit('low-hit-rate', false); $collector->recordCacheHit('low-hit-rate', false); $summary = $collector->getSummary(); // Total: 4 hits, 4 misses = 50% hit rate if ($summary['cache_hit_rate'] === 50.0) { echo " ✓ Cache hit rate calculation for display correct\n"; } else { echo " ✗ FAILED: Cache hit rate should be 50%, got {$summary['cache_hit_rate']}%\n"; } // Test 11: Metrics Reset for DevTools Session echo "\nTest 11: Metrics Reset for DevTools Session\n"; $collector->reset(); // Add some metrics $collector->recordRender('test', 10.0); $collector->recordAction('test', 'action', 20.0); // Reset (like DevTools "Clear" button) $collector->reset(); if (count($collector->getMetrics()) === 0) { echo " ✓ Metrics reset works (for DevTools Clear button)\n"; } else { echo " ✗ FAILED: Metrics should be empty after reset\n"; } // Test 12: Hydration Metrics echo "\nTest 12: Hydration Metrics\n"; $collector->reset(); // Simulate component hydration $collector->recordHydration('interactive-map', 156.78); $collector->recordHydration('data-table', 89.45); $metrics = $collector->getMetrics(); if (isset($metrics['livecomponent_hydration_duration_ms{component_id=interactive-map}']) && $metrics['livecomponent_hydration_duration_ms{component_id=interactive-map}']->value === 156.78 && isset($metrics['livecomponent_hydration_duration_ms{component_id=data-table}']) && $metrics['livecomponent_hydration_duration_ms{component_id=data-table}']->value === 89.45) { echo " ✓ Hydration metrics tracked correctly\n"; } else { echo " ✗ FAILED: Hydration metrics incorrect\n"; } // Test 13: Metric Retrieval API echo "\nTest 13: Metric Retrieval API\n"; $collector->reset(); $collector->recordRender('api-test', 50.0); $specificMetric = $collector->getMetric('livecomponent_renders_total{cached=false,component_id=api-test}'); if ($specificMetric !== null && $specificMetric->value === 1.0) { echo " ✓ Individual metric retrieval works\n"; } else { echo " ✗ FAILED: Cannot retrieve specific metric\n"; } $allMetrics = $collector->getMetrics(); if (is_array($allMetrics) && count($allMetrics) > 0) { echo " ✓ All metrics retrieval works\n"; } else { echo " ✗ FAILED: Cannot retrieve all metrics\n"; } // Test 14: Timestamp Tracking for Timeline echo "\nTest 14: Timestamp Tracking for Timeline\n"; $collector->reset(); $collector->recordAction('timeline-test', 'action1', 10.0); sleep(1); // 1 second delay to ensure different timestamps $collector->recordAction('timeline-test', 'action2', 15.0); $metrics = $collector->getMetrics(); $metric1 = $metrics['livecomponent_actions_total{action=action1,component_id=timeline-test,status=success}']; $metric2 = $metrics['livecomponent_actions_total{action=action2,component_id=timeline-test,status=success}']; if ($metric1->timestamp !== null && $metric2->timestamp !== null && $metric2->timestamp->toTimestamp() > $metric1->timestamp->toTimestamp()) { echo " ✓ Timestamps tracked correctly for timeline\n"; } else { echo " ✗ FAILED: Timestamp tracking incorrect\n"; } // Test 15: Label Special Characters Handling echo "\nTest 15: Label Special Characters Handling\n"; $collector->reset(); // Test with special characters that might appear in component IDs or action names $collector->recordAction('form"test', 'validate\nfield', 10.0); $prometheus = $collector->exportPrometheus(); // Should properly escape special characters in Prometheus format // Note: Prometheus format uses double backslash for escaped characters if (str_contains($prometheus, 'form\\"test') && str_contains($prometheus, 'validate\\\\nfield')) { echo " ✓ Special characters in labels escaped correctly\n"; } else { echo " ✗ FAILED: Special character escaping incorrect\n"; } echo "\n=============================\n"; echo "DevTools Integration Test Summary:\n"; echo " - Metrics API: ✓\n"; echo " - Prometheus export: ✓\n"; echo " - Real-time collection: ✓\n"; echo " - Performance tab data: ✓\n"; echo " - Component tree data: ✓\n"; echo " - Event log data: ✓\n"; echo " - Network timeline data: ✓\n"; echo " - Batch operation metrics: ✓\n"; echo " - Fragment update metrics: ✓\n"; echo " - Cache hit rate display: ✓\n"; echo " - Metrics reset: ✓\n"; echo " - Hydration metrics: ✓\n"; echo " - Metric retrieval API: ✓\n"; echo " - Timestamp tracking: ✓\n"; echo " - Special character handling: ✓\n"; echo "\nAll DevTools integration tests passing!\n"; echo "\nDevTools Integration Points Verified:\n"; echo " 1. PHP ComponentMetricsCollector ↔ JavaScript DevTools\n"; echo " 2. Prometheus export format compatibility\n"; echo " 3. Real-time metrics API contract\n"; echo " 4. Performance, Component Tree, Event Log data\n"; echo " 5. Network Timeline and Upload tracking\n"; echo " 6. Batch operations and Fragment updates\n"; echo " 7. Cache statistics and hit rate calculation\n"; echo " 8. Timestamp-based timeline ordering\n"; echo " 9. Special character escaping for safety\n";