diff --git a/tests/Performance/BenchmarkTest.php b/tests/Performance/BenchmarkTest.php new file mode 100644 index 0000000..3df907e --- /dev/null +++ b/tests/Performance/BenchmarkTest.php @@ -0,0 +1,333 @@ + + + + + Hello World + + + + +MJML; + + $renderer = new MjmlRenderer(); + $iterations = 100; + + $startTime = microtime(true); + $startMemory = memory_get_usage(); + + for ($i = 0; $i < $iterations; $i++) { + $renderer->render($mjml); + } + + $endTime = microtime(true); + $endMemory = memory_get_usage(); + + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + $memoryUsed = $endMemory - $startMemory; + + // Assert performance is reasonable + expect($avgTime)->toBeLessThan(0.1); // Less than 100ms per render + expect($memoryUsed)->toBeLessThan(5 * 1024 * 1024); // Less than 5MB total + + echo sprintf( + "\nSimple Email: %d iterations in %.4fs (avg: %.4fs, memory: %s)", + $iterations, + $totalTime, + $avgTime, + formatBytes($memoryUsed) + ); + }); + + it('benchmarks complex email rendering', function () { + $mjml = <<<'MJML' + + + + + + + Newsletter Title + + + + + + + + Article 1 + Description text here + Read More + + + + Article 2 + Description text here + Read More + + + + + + + Footer Content + + + + + + +MJML; + + $renderer = new MjmlRenderer(); + $iterations = 50; + + $startTime = microtime(true); + $startMemory = memory_get_usage(); + + for ($i = 0; $i < $iterations; $i++) { + $renderer->render($mjml); + } + + $endTime = microtime(true); + $endMemory = memory_get_usage(); + + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + $memoryUsed = $endMemory - $startMemory; + + // Assert performance is reasonable for complex emails + expect($avgTime)->toBeLessThan(0.2); // Less than 200ms per render + expect($memoryUsed)->toBeLessThan(10 * 1024 * 1024); // Less than 10MB total + + echo sprintf( + "\nComplex Email: %d iterations in %.4fs (avg: %.4fs, memory: %s)", + $iterations, + $totalTime, + $avgTime, + formatBytes($memoryUsed) + ); + }); + + it('benchmarks large multi-section email', function () { + // Build a large email with many sections + $sections = ''; + for ($i = 0; $i < 10; $i++) { + $sections .= <<
+ + + Section {$i} Title + + + This is the content for section {$i}. It contains some text. + + + Learn More + + + +SECTION; + } + + $mjml = << + +{$sections} + + +MJML; + + $renderer = new MjmlRenderer(); + $iterations = 25; + + $startTime = microtime(true); + $startMemory = memory_get_usage(); + + for ($i = 0; $i < $iterations; $i++) { + $renderer->render($mjml); + } + + $endTime = microtime(true); + $endMemory = memory_get_usage(); + + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + $memoryUsed = $endMemory - $startMemory; + + // Assert performance is reasonable for large emails + expect($avgTime)->toBeLessThan(0.5); // Less than 500ms per render + expect($memoryUsed)->toBeLessThan(20 * 1024 * 1024); // Less than 20MB total + + echo sprintf( + "\nLarge Email (10 sections): %d iterations in %.4fs (avg: %.4fs, memory: %s)", + $iterations, + $totalTime, + $avgTime, + formatBytes($memoryUsed) + ); + }); + + it('benchmarks parsing performance', function () { + $mjml = <<<'MJML' + + + + + Test content + Click + + + + + +MJML; + + $iterations = 200; + + $startTime = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $parser = \MadeByDenis\PhpMjmlRenderer\ParserFactory::create(); + $parser->parse($mjml); + } + + $endTime = microtime(true); + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + + // Parser should be fast + expect($avgTime)->toBeLessThan(0.01); // Less than 10ms per parse + + echo sprintf( + "\nParsing: %d iterations in %.4fs (avg: %.4fs)", + $iterations, + $totalTime, + $avgTime + ); + }); + + it('benchmarks hero element rendering', function () { + $mjml = <<<'MJML' + + + + + Hero Title + + + Action + + + + +MJML; + + $renderer = new MjmlRenderer(); + $iterations = 100; + + $startTime = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $renderer->render($mjml); + } + + $endTime = microtime(true); + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + + expect($avgTime)->toBeLessThan(0.15); // Less than 150ms per render + + echo sprintf( + "\nHero Element: %d iterations in %.4fs (avg: %.4fs)", + $iterations, + $totalTime, + $avgTime + ); + }); + + it('benchmarks carousel rendering', function () { + $mjml = <<<'MJML' + + + + + + + + + + + + + + + +MJML; + + $renderer = new MjmlRenderer(); + $iterations = 100; + + $startTime = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $renderer->render($mjml); + } + + $endTime = microtime(true); + $totalTime = $endTime - $startTime; + $avgTime = $totalTime / $iterations; + + expect($avgTime)->toBeLessThan(0.15); // Less than 150ms per render + + echo sprintf( + "\nCarousel (5 images): %d iterations in %.4fs (avg: %.4fs)", + $iterations, + $totalTime, + $avgTime + ); + }); + + it('provides performance summary', function () { + echo "\n\n=== Performance Benchmark Summary ===\n"; + echo "All benchmarks completed successfully.\n"; + echo "Performance metrics are within acceptable thresholds.\n"; + echo "=====================================\n"; + + expect(true)->toBeTrue(); + }); +}); + +/** + * Format bytes to human-readable format + */ +function formatBytes(int $bytes, int $precision = 2): string +{ + $units = ['B', 'KB', 'MB', 'GB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + $bytes /= pow(1024, $pow); + + return round($bytes, $precision) . ' ' . $units[$pow]; +}