Skip to content

Commit 4063095

Browse files
committed
Adds performance benchmarks
1 parent 00b9834 commit 4063095

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

packages/babel-plugin-component-annotate/test/test-plugin.test.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
import { transform } from "@babel/core";
2727
import plugin from "../src/index";
2828

29+
const getTime = (): number => {
30+
if (typeof performance !== "undefined" && performance.now) {
31+
return performance.now();
32+
}
33+
return Date.now();
34+
};
35+
2936
const BananasPizzaAppStandardInput = `import React, { Component } from 'react';
3037
import { StyleSheet, Text, TextInput, View, Image, UIManager } from 'react-native';
3138
@@ -1741,3 +1748,140 @@ export default function TestComponent() {
17411748
expect(result?.code).toMatchSnapshot();
17421749
});
17431750
});
1751+
1752+
describe("Fragment Performance Benchmarks", () => {
1753+
interface TestFile {
1754+
filename: string;
1755+
code: string;
1756+
}
1757+
1758+
interface BenchmarkResult {
1759+
avg: number;
1760+
min: number;
1761+
max: number;
1762+
median: number;
1763+
}
1764+
1765+
const createTestProject = (fileCount: number, fragmentRatio = 0.4): TestFile[] => {
1766+
const files: TestFile[] = [];
1767+
1768+
for (let i = 0; i < fileCount; i++) {
1769+
const hasFragment = Math.random() < fragmentRatio;
1770+
const patterns = [
1771+
`import React, { Fragment } from 'react';`,
1772+
`import { Fragment } from 'react';`,
1773+
`import * as React from 'react';`,
1774+
`import React from 'react';\nconst { Fragment } = React;`,
1775+
`import MyReact from 'react';\nconst { Fragment: F } = MyReact;`,
1776+
];
1777+
1778+
const pattern = patterns[Math.floor(Math.random() * patterns.length)] as string;
1779+
const fragmentType = getFragmentType(pattern);
1780+
1781+
// Add imports to other files for realistic import traversal
1782+
const imports: string[] = [];
1783+
const numImports = Math.min(3, i);
1784+
for (let j = 0; j < numImports; j++) {
1785+
imports.push(`import Component${j} from './component${j}';`);
1786+
}
1787+
1788+
const code = `${pattern}
1789+
${imports.join("\n")}
1790+
1791+
export default function Component${i}() {
1792+
return ${
1793+
hasFragment
1794+
? `<${fragmentType}><div>Content ${i}</div></${fragmentType}>`
1795+
: `<div>Component ${i}</div>`
1796+
};
1797+
}`;
1798+
1799+
files.push({ filename: `component${i}.js`, code });
1800+
}
1801+
1802+
return files;
1803+
};
1804+
1805+
const getFragmentType = (pattern: string): string => {
1806+
if (pattern.includes("Fragment: F")) return "F";
1807+
if (pattern.includes("Fragment }")) return "Fragment";
1808+
if (pattern.includes("* as React")) return "React.Fragment";
1809+
if (pattern.includes("const { Fragment }")) return "Fragment";
1810+
return "Fragment";
1811+
};
1812+
1813+
const runBenchmark = (files: TestFile[], iterations = 10): BenchmarkResult => {
1814+
const times: number[] = [];
1815+
1816+
for (let i = 0; i < iterations; i++) {
1817+
const start = getTime();
1818+
1819+
// Transform all files (simulating your plugin processing)
1820+
files.forEach((file: TestFile) => {
1821+
const result = transform(file.code, {
1822+
filename: `/${file.filename}`,
1823+
configFile: false,
1824+
presets: ["@babel/preset-react"],
1825+
plugins: [plugin],
1826+
});
1827+
if (!result?.code) throw new Error("Transform failed");
1828+
});
1829+
1830+
const end = getTime();
1831+
times.push(end - start);
1832+
}
1833+
1834+
const sortedTimes = [...times].sort((a, b) => a - b);
1835+
return {
1836+
avg: times.reduce((a, b) => a + b) / times.length,
1837+
min: Math.min(...times),
1838+
max: Math.max(...times),
1839+
median: sortedTimes[Math.floor(sortedTimes.length / 2)],
1840+
};
1841+
};
1842+
1843+
// Test different project sizes
1844+
const scenarios = [
1845+
{ name: "small" as const, files: 20, expectedMaxTime: 50 },
1846+
{ name: "medium" as const, files: 50, expectedMaxTime: 200 },
1847+
{ name: "large" as const, files: 200, expectedMaxTime: 500 },
1848+
];
1849+
1850+
scenarios.forEach((scenario) => {
1851+
it(`should perform well on ${scenario.name} projects (${scenario.files} files)`, () => {
1852+
const testFiles = createTestProject(scenario.files);
1853+
const results = runBenchmark(testFiles);
1854+
1855+
expect(results.avg).toBeGreaterThan(0);
1856+
expect(results.avg).toBeLessThan(scenario.expectedMaxTime);
1857+
expect(results.median).toBeLessThan(scenario.expectedMaxTime * 0.8);
1858+
});
1859+
});
1860+
1861+
it("should handle fragment-heavy projects efficiently", () => {
1862+
const fragmentHeavyFiles = createTestProject(25, 0.8);
1863+
const normalFiles = createTestProject(25, 0.2);
1864+
1865+
const fragmentResults = runBenchmark(fragmentHeavyFiles, 5);
1866+
const normalResults = runBenchmark(normalFiles, 5);
1867+
1868+
const overhead = (fragmentResults.avg - normalResults.avg) / normalResults.avg;
1869+
1870+
expect(overhead).toBeLessThan(0.5); // Less than 50% overhead
1871+
expect(fragmentResults.avg).toBeLessThan(300);
1872+
});
1873+
1874+
it("should not consume excessive memory", () => {
1875+
const testFiles = createTestProject(50);
1876+
1877+
if (global.gc) global.gc();
1878+
1879+
const memBefore = process.memoryUsage().heapUsed;
1880+
runBenchmark(testFiles, 1);
1881+
const memAfter = process.memoryUsage().heapUsed;
1882+
1883+
const memoryDelta = (memAfter - memBefore) / 1024 / 1024;
1884+
1885+
expect(memoryDelta).toBeLessThan(50);
1886+
});
1887+
});

0 commit comments

Comments
 (0)