Initial Commit
This commit is contained in:
127
packages/devtools/tooling/profiler/src/reporter/reporter.test.ts
Normal file
127
packages/devtools/tooling/profiler/src/reporter/reporter.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
PROFILER_CODES,
|
||||
type ProfilerError,
|
||||
} from "../error-codes/error-codes.js";
|
||||
import type { ProfileEntry, ProfileMark } from "../types/profiler.types.js";
|
||||
import { ProfileReporter } from "./reporter.js";
|
||||
|
||||
function entry(
|
||||
label: string,
|
||||
duration: number,
|
||||
category?: string
|
||||
): ProfileEntry {
|
||||
return { label, startTime: 0, endTime: duration, duration, category };
|
||||
}
|
||||
|
||||
describe("ProfileReporter", () => {
|
||||
it("throws INVALID_OPTIONS for non-empty bad percentiles", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
new ProfileReporter({ percentiles: [200] });
|
||||
throw new Error("expected throw");
|
||||
} catch (err) {
|
||||
expect((err as ProfilerError).code).toBe(
|
||||
PROFILER_CODES.INVALID_OPTIONS
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws INVALID_OPTIONS for empty percentiles", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
new ProfileReporter({ percentiles: [] });
|
||||
throw new Error("expected throw");
|
||||
} catch (err) {
|
||||
expect((err as ProfilerError).code).toBe(
|
||||
PROFILER_CODES.INVALID_OPTIONS
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("computes percentiles", { timeout: 5000 }, () => {
|
||||
const r = new ProfileReporter({
|
||||
percentiles: [50, 90],
|
||||
clock: () => 0,
|
||||
});
|
||||
const stats = r.computeStats([10, 20, 30, 40, 50]);
|
||||
expect(stats.count).toBe(5);
|
||||
expect(stats.min).toBe(10);
|
||||
expect(stats.max).toBe(50);
|
||||
expect(stats.mean).toBe(30);
|
||||
expect(stats.median).toBe(30);
|
||||
expect(stats.percentiles[50]).toBe(30);
|
||||
expect(stats.percentiles[90]).toBeGreaterThan(30);
|
||||
});
|
||||
|
||||
it("handles empty entries", { timeout: 5000 }, () => {
|
||||
const r = new ProfileReporter();
|
||||
const stats = r.computeStats([]);
|
||||
expect(stats).toEqual({
|
||||
count: 0,
|
||||
min: 0,
|
||||
max: 0,
|
||||
mean: 0,
|
||||
median: 0,
|
||||
percentiles: {},
|
||||
totalDuration: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("groups by category and uncategorized falls back", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const r = new ProfileReporter({ clock: () => 0 });
|
||||
const report = r.generateReport([
|
||||
entry("a", 10, "render"),
|
||||
entry("b", 20, "render"),
|
||||
entry("c", 30),
|
||||
]);
|
||||
expect(report.byCategory.render?.count).toBe(2);
|
||||
expect(report.byCategory.uncategorized?.count).toBe(1);
|
||||
});
|
||||
|
||||
it("flags alerts above threshold", { timeout: 5000 }, () => {
|
||||
const r = new ProfileReporter({ alertThreshold: 20, clock: () => 0 });
|
||||
const report = r.generateReport([entry("ok", 10), entry("bad", 30)]);
|
||||
expect(report.alerts).toHaveLength(1);
|
||||
expect(report.alerts[0]?.label).toBe("bad");
|
||||
});
|
||||
|
||||
it("formatReport produces stable text", { timeout: 5000 }, () => {
|
||||
const r = new ProfileReporter({ clock: () => 0, alertThreshold: 1 });
|
||||
const report = r.generateReport([entry("slow", 10)]);
|
||||
const text = r.formatReport(report);
|
||||
expect(text).toContain("Profile Report");
|
||||
expect(text).toContain("Alerts (1)");
|
||||
expect(text).toContain("slow");
|
||||
});
|
||||
|
||||
it("formatReport with no alerts skips alerts section", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const r = new ProfileReporter({ clock: () => 0 });
|
||||
const text = r.formatReport(r.generateReport([entry("ok", 10)]));
|
||||
expect(text).not.toContain("Alerts");
|
||||
});
|
||||
|
||||
it("computePercentile handles single-element exact index", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const r = new ProfileReporter();
|
||||
const stats = r.computeStats([42]);
|
||||
expect(stats.median).toBe(42);
|
||||
});
|
||||
|
||||
it("ignores entries without duration", { timeout: 5000 }, () => {
|
||||
const r = new ProfileReporter();
|
||||
const report = r.generateReport(
|
||||
[{ label: "still", startTime: 0 }, entry("ok", 10)],
|
||||
[{ label: "mark", timestamp: 1 }] as readonly ProfileMark[]
|
||||
);
|
||||
expect(report.stats.count).toBe(1);
|
||||
expect(report.marks).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user