Skip to content

Commit eb94497

Browse files
authored
Merge pull request #34 from dingo-d/test/performance-benchmarks
Add performance benchmark suite
2 parents 9f4ae1c + eb4e44d commit eb94497

File tree

1 file changed

+333
-0
lines changed

1 file changed

+333
-0
lines changed
Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
<?php
2+
3+
namespace MadeByDenis\PhpMjmlRenderer\Tests\Performance;
4+
5+
use MadeByDenis\PhpMjmlRenderer\Renderer\MjmlRenderer;
6+
7+
describe('Performance Benchmarks', function () {
8+
it('benchmarks simple email rendering', function () {
9+
$mjml = <<<'MJML'
10+
<mjml>
11+
<mj-body>
12+
<mj-section>
13+
<mj-column>
14+
<mj-text>Hello World</mj-text>
15+
</mj-column>
16+
</mj-section>
17+
</mj-body>
18+
</mjml>
19+
MJML;
20+
21+
$renderer = new MjmlRenderer();
22+
$iterations = 100;
23+
24+
$startTime = microtime(true);
25+
$startMemory = memory_get_usage();
26+
27+
for ($i = 0; $i < $iterations; $i++) {
28+
$renderer->render($mjml);
29+
}
30+
31+
$endTime = microtime(true);
32+
$endMemory = memory_get_usage();
33+
34+
$totalTime = $endTime - $startTime;
35+
$avgTime = $totalTime / $iterations;
36+
$memoryUsed = $endMemory - $startMemory;
37+
38+
// Assert performance is reasonable
39+
expect($avgTime)->toBeLessThan(0.1); // Less than 100ms per render
40+
expect($memoryUsed)->toBeLessThan(5 * 1024 * 1024); // Less than 5MB total
41+
42+
echo sprintf(
43+
"\nSimple Email: %d iterations in %.4fs (avg: %.4fs, memory: %s)",
44+
$iterations,
45+
$totalTime,
46+
$avgTime,
47+
formatBytes($memoryUsed)
48+
);
49+
});
50+
51+
it('benchmarks complex email rendering', function () {
52+
$mjml = <<<'MJML'
53+
<mjml>
54+
<mj-body>
55+
<mj-section background-color="#f0f0f0">
56+
<mj-column>
57+
<mj-image src="https://example.com/logo.png" width="200px"></mj-image>
58+
<mj-text font-size="24px" font-weight="700" align="center">
59+
Newsletter Title
60+
</mj-text>
61+
<mj-divider border-color="#333"></mj-divider>
62+
</mj-column>
63+
</mj-section>
64+
<mj-section>
65+
<mj-column width="50%">
66+
<mj-image src="https://example.com/img1.jpg"></mj-image>
67+
<mj-text font-weight="700">Article 1</mj-text>
68+
<mj-text>Description text here</mj-text>
69+
<mj-button href="https://example.com/1">Read More</mj-button>
70+
</mj-column>
71+
<mj-column width="50%">
72+
<mj-image src="https://example.com/img2.jpg"></mj-image>
73+
<mj-text font-weight="700">Article 2</mj-text>
74+
<mj-text>Description text here</mj-text>
75+
<mj-button href="https://example.com/2">Read More</mj-button>
76+
</mj-column>
77+
</mj-section>
78+
<mj-wrapper background-color="#3498db">
79+
<mj-section>
80+
<mj-column>
81+
<mj-text color="#ffffff" align="center">
82+
Footer Content
83+
</mj-text>
84+
</mj-column>
85+
</mj-section>
86+
</mj-wrapper>
87+
</mj-body>
88+
</mjml>
89+
MJML;
90+
91+
$renderer = new MjmlRenderer();
92+
$iterations = 50;
93+
94+
$startTime = microtime(true);
95+
$startMemory = memory_get_usage();
96+
97+
for ($i = 0; $i < $iterations; $i++) {
98+
$renderer->render($mjml);
99+
}
100+
101+
$endTime = microtime(true);
102+
$endMemory = memory_get_usage();
103+
104+
$totalTime = $endTime - $startTime;
105+
$avgTime = $totalTime / $iterations;
106+
$memoryUsed = $endMemory - $startMemory;
107+
108+
// Assert performance is reasonable for complex emails
109+
expect($avgTime)->toBeLessThan(0.2); // Less than 200ms per render
110+
expect($memoryUsed)->toBeLessThan(10 * 1024 * 1024); // Less than 10MB total
111+
112+
echo sprintf(
113+
"\nComplex Email: %d iterations in %.4fs (avg: %.4fs, memory: %s)",
114+
$iterations,
115+
$totalTime,
116+
$avgTime,
117+
formatBytes($memoryUsed)
118+
);
119+
});
120+
121+
it('benchmarks large multi-section email', function () {
122+
// Build a large email with many sections
123+
$sections = '';
124+
for ($i = 0; $i < 10; $i++) {
125+
$sections .= <<<SECTION
126+
<mj-section>
127+
<mj-column>
128+
<mj-text font-size="20px" font-weight="700">
129+
Section {$i} Title
130+
</mj-text>
131+
<mj-text>
132+
This is the content for section {$i}. It contains some text.
133+
</mj-text>
134+
<mj-button href="https://example.com/section{$i}">
135+
Learn More
136+
</mj-button>
137+
</mj-column>
138+
</mj-section>
139+
SECTION;
140+
}
141+
142+
$mjml = <<<MJML
143+
<mjml>
144+
<mj-body>
145+
{$sections}
146+
</mj-body>
147+
</mjml>
148+
MJML;
149+
150+
$renderer = new MjmlRenderer();
151+
$iterations = 25;
152+
153+
$startTime = microtime(true);
154+
$startMemory = memory_get_usage();
155+
156+
for ($i = 0; $i < $iterations; $i++) {
157+
$renderer->render($mjml);
158+
}
159+
160+
$endTime = microtime(true);
161+
$endMemory = memory_get_usage();
162+
163+
$totalTime = $endTime - $startTime;
164+
$avgTime = $totalTime / $iterations;
165+
$memoryUsed = $endMemory - $startMemory;
166+
167+
// Assert performance is reasonable for large emails
168+
expect($avgTime)->toBeLessThan(0.5); // Less than 500ms per render
169+
expect($memoryUsed)->toBeLessThan(20 * 1024 * 1024); // Less than 20MB total
170+
171+
echo sprintf(
172+
"\nLarge Email (10 sections): %d iterations in %.4fs (avg: %.4fs, memory: %s)",
173+
$iterations,
174+
$totalTime,
175+
$avgTime,
176+
formatBytes($memoryUsed)
177+
);
178+
});
179+
180+
it('benchmarks parsing performance', function () {
181+
$mjml = <<<'MJML'
182+
<mjml>
183+
<mj-body>
184+
<mj-section>
185+
<mj-column>
186+
<mj-text>Test content</mj-text>
187+
<mj-button href="https://example.com">Click</mj-button>
188+
<mj-divider></mj-divider>
189+
</mj-column>
190+
</mj-section>
191+
</mj-body>
192+
</mjml>
193+
MJML;
194+
195+
$iterations = 200;
196+
197+
$startTime = microtime(true);
198+
199+
for ($i = 0; $i < $iterations; $i++) {
200+
$parser = \MadeByDenis\PhpMjmlRenderer\ParserFactory::create();
201+
$parser->parse($mjml);
202+
}
203+
204+
$endTime = microtime(true);
205+
$totalTime = $endTime - $startTime;
206+
$avgTime = $totalTime / $iterations;
207+
208+
// Parser should be fast
209+
expect($avgTime)->toBeLessThan(0.01); // Less than 10ms per parse
210+
211+
echo sprintf(
212+
"\nParsing: %d iterations in %.4fs (avg: %.4fs)",
213+
$iterations,
214+
$totalTime,
215+
$avgTime
216+
);
217+
});
218+
219+
it('benchmarks hero element rendering', function () {
220+
$mjml = <<<'MJML'
221+
<mjml>
222+
<mj-body>
223+
<mj-hero
224+
mode="fixed-height"
225+
height="400px"
226+
background-url="https://example.com/bg.jpg"
227+
background-color="#2a2a2a"
228+
>
229+
<mj-text
230+
padding="20px"
231+
color="#ffffff"
232+
font-size="32px"
233+
align="center"
234+
font-weight="700"
235+
>
236+
Hero Title
237+
</mj-text>
238+
<mj-button href="https://example.com" background-color="#ff6600">
239+
Action
240+
</mj-button>
241+
</mj-hero>
242+
</mj-body>
243+
</mjml>
244+
MJML;
245+
246+
$renderer = new MjmlRenderer();
247+
$iterations = 100;
248+
249+
$startTime = microtime(true);
250+
251+
for ($i = 0; $i < $iterations; $i++) {
252+
$renderer->render($mjml);
253+
}
254+
255+
$endTime = microtime(true);
256+
$totalTime = $endTime - $startTime;
257+
$avgTime = $totalTime / $iterations;
258+
259+
expect($avgTime)->toBeLessThan(0.15); // Less than 150ms per render
260+
261+
echo sprintf(
262+
"\nHero Element: %d iterations in %.4fs (avg: %.4fs)",
263+
$iterations,
264+
$totalTime,
265+
$avgTime
266+
);
267+
});
268+
269+
it('benchmarks carousel rendering', function () {
270+
$mjml = <<<'MJML'
271+
<mjml>
272+
<mj-body>
273+
<mj-section>
274+
<mj-column>
275+
<mj-carousel>
276+
<mj-carousel-image src="https://example.com/1.jpg" />
277+
<mj-carousel-image src="https://example.com/2.jpg" />
278+
<mj-carousel-image src="https://example.com/3.jpg" />
279+
<mj-carousel-image src="https://example.com/4.jpg" />
280+
<mj-carousel-image src="https://example.com/5.jpg" />
281+
</mj-carousel>
282+
</mj-column>
283+
</mj-section>
284+
</mj-body>
285+
</mjml>
286+
MJML;
287+
288+
$renderer = new MjmlRenderer();
289+
$iterations = 100;
290+
291+
$startTime = microtime(true);
292+
293+
for ($i = 0; $i < $iterations; $i++) {
294+
$renderer->render($mjml);
295+
}
296+
297+
$endTime = microtime(true);
298+
$totalTime = $endTime - $startTime;
299+
$avgTime = $totalTime / $iterations;
300+
301+
expect($avgTime)->toBeLessThan(0.15); // Less than 150ms per render
302+
303+
echo sprintf(
304+
"\nCarousel (5 images): %d iterations in %.4fs (avg: %.4fs)",
305+
$iterations,
306+
$totalTime,
307+
$avgTime
308+
);
309+
});
310+
311+
it('provides performance summary', function () {
312+
echo "\n\n=== Performance Benchmark Summary ===\n";
313+
echo "All benchmarks completed successfully.\n";
314+
echo "Performance metrics are within acceptable thresholds.\n";
315+
echo "=====================================\n";
316+
317+
expect(true)->toBeTrue();
318+
});
319+
});
320+
321+
/**
322+
* Format bytes to human-readable format
323+
*/
324+
function formatBytes(int $bytes, int $precision = 2): string
325+
{
326+
$units = ['B', 'KB', 'MB', 'GB'];
327+
$bytes = max($bytes, 0);
328+
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
329+
$pow = min($pow, count($units) - 1);
330+
$bytes /= pow(1024, $pow);
331+
332+
return round($bytes, $precision) . ' ' . $units[$pow];
333+
}

0 commit comments

Comments
 (0)