Skip to content

Commit c66a0ec

Browse files
add tests
1 parent a8b6f5e commit c66a0ec

5 files changed

Lines changed: 233 additions & 7 deletions

File tree

src/HeapSnapshotManager.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ export class HeapSnapshotManager {
9999
return uid;
100100
}
101101

102+
#getCachedSnapshot(filePath: string) {
103+
const absolutePath = path.resolve(filePath);
104+
const cached = this.#snapshots.get(absolutePath);
105+
if (!cached) {
106+
throw new Error(`Snapshot not loaded for ${filePath}`);
107+
}
108+
return cached;
109+
}
110+
102111
async #loadSnapshot(absolutePath: string): Promise<{
103112
snapshot: DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotProxy;
104113
worker: DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy;
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exports[`HeapSnapshotFormatter > toString > formats data as CSV and sorts by self size 1`] = `
2-
className,count,selfSize,maxRetainedSize
3-
"ObjectA",10,100,1000
4-
"ObjectB",5,50,500
2+
uid,className,count,selfSize,maxRetainedSize
3+
1,"ObjectA",10,100,1000
4+
2,"ObjectB",5,50,500
55
`;

tests/formatters/HeapSnapshotFormatter.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {describe, it} from 'node:test';
99

1010
import {HeapSnapshotFormatter} from '../../src/formatters/HeapSnapshotFormatter.js';
1111
import type {DevTools} from '../../src/third_party/index.js';
12+
import {stableIdSymbol} from '../../src/utils/id.js';
1213

1314
describe('HeapSnapshotFormatter', () => {
1415
const mockAggregates: Record<
@@ -22,6 +23,7 @@ describe('HeapSnapshotFormatter', () => {
2223
maxRet: 1000,
2324
distance: 1,
2425
idxs: [],
26+
[stableIdSymbol]: 1,
2527
} as unknown as DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo,
2628
ObjectB: {
2729
name: 'ObjectB',
@@ -30,6 +32,7 @@ describe('HeapSnapshotFormatter', () => {
3032
maxRet: 500,
3133
distance: 2,
3234
idxs: [],
35+
[stableIdSymbol]: 2,
3336
} as unknown as DevTools.HeapSnapshotModel.HeapSnapshotModel.AggregatedInfo,
3437
};
3538

@@ -47,12 +50,14 @@ describe('HeapSnapshotFormatter', () => {
4750
const result = formatter.toJSON();
4851
assert.deepStrictEqual(result, [
4952
{
53+
uid: 1,
5054
className: 'ObjectA',
5155
count: 10,
5256
selfSize: 100,
5357
retainedSize: 1000,
5458
},
5559
{
60+
uid: 2,
5661
className: 'ObjectB',
5762
count: 5,
5863
selfSize: 50,
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
exports[`memory > get_memory_snapshot_details > with default options 1`] = `
2+
## Heap Snapshot Data
3+
Showing 1-157 of 157 (Page 1 of 1).
4+
uid,className,count,selfSize,maxRetainedSize
5+
2,"(system)",3205,199264,655004
6+
16,"(object shape)",2878,181480,184320
7+
11,"Function",3581,111464,237120
8+
42,"Window (global*) / https://example.com",2,81068,101832
9+
94,"Window (prototype) / https://example.com",4,53336,53712
10+
1,"(compiled code)",713,42812,46988
11+
3,"(array)",34,41416,117472
12+
6,"(string)",846,27880,27880
13+
109,"Document",1,8448,8700
14+
105,"HTMLElement",1,4728,4768
15+
104,"Element",1,4224,4540
16+
15,"Object (global*)",2,3856,13488
17+
10,"HTMLDocument",1,3288,6192
18+
89,"system / Context",116,2544,3488
19+
103,"Node",1,1764,1804
20+
49,"TypedArray",48,1424,8784
21+
50,"Error",48,1344,7872
22+
106,"HTMLBodyElement (prototype) / https://example.com",1,1188,1228
23+
19,"Array",8,1056,7728
24+
126,"Performance",1,1040,1784
25+
32,"String",4,960,6056
26+
54,"Object",22,928,1168
27+
130,"StyleEngine",1,912,1104
28+
9,"Window / https://example.com",1,904,1944
29+
55,"{constructor, toString, toDateString, toTimeString, toISOString, toUTCString, toGMTString, getDate, setDate, getDay}",4,848,8848
30+
47,"Math",4,816,7792
31+
39,"{constructor, buffer, get buffer, byteLength, get byteLength, byteOffset, get byteOffset, length, get length}",4,624,7008
32+
118,"CSSStyleRule",8,576,1024
33+
119,"CSSStyleSheet",3,528,976
34+
44,"console",4,488,6152
35+
68,"DataView",4,480,5504
36+
76,"{constructor, getColumnNumber, getEnclosingColumnNumber, getEnclosingLineNumber, getEvalOrigin, getFileName, getFunction}",4,480,4448
37+
110,"Text",6,480,480
38+
37,"WebAssembly",4,416,1904
39+
96,"Window (internal cache) / https://example.com",4,400,20256
40+
135,"EventListener",10,400,400
41+
40,"{constructor}",14,392,1560
42+
30,"Set",4,384,1616
43+
27,"Map",4,336,1504
44+
43,"Atomics",4,336,3056
45+
45,"Intl",4,336,4192
46+
46,"Reflect",4,336,2480
47+
67,"{at, copyWithin, entries, fill, find, findIndex, findLast, findLastIndex, flat, flatMap, includes, keys, toReversed}",4,336,1344
48+
134,"FontFaceSet",1,328,328
49+
133,"StylePropertyMap",8,320,320
50+
139,"MutationObserver",2,320,320
51+
25,"{<symbol Symbol.iterator>, constructor, get constructor, set constructor, reduce, toArray, forEach, some, every, find}",4,288,3600
52+
28,"{constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf}",4,288,1128
53+
62,"Intl.Locale",4,288,4672
54+
33,"WeakMap",4,240,960
55+
36,"{constructor, exec, dotAll, get dotAll, flags, get flags, global, get global, hasIndices, get hasIndices, ignoreCase}",4,240,576
56+
48,"Number",4,240,1488
57+
91,"DisposableStack",4,240,3040
58+
92,"AsyncDisposableStack",4,240,3040
59+
4,"{<symbol handler_function>, <symbol feature_name>}",8,224,352
60+
131,"ScriptedAnimationController",1,208,208
61+
123,"IntersectionObserver",1,200,240
62+
7,"(number)",16,192,192
63+
17,"{isTraceCategoryEnabled, trace, getContinuationPreservedEmbedderData, setContinuationPreservedEmbedderData, console}",4,192,1120
64+
21,"Generator",4,192,1008
65+
23,"AsyncGenerator",4,192,1008
66+
34,"JSON",4,192,1008
67+
35,"Promise",4,192,1000
68+
53,"WeakSet",4,192,416
69+
56,"Intl.NumberFormat",4,192,1536
70+
59,"Intl.PluralRules",4,192,1168
71+
60,"Intl.RelativeTimeFormat",4,192,1168
72+
61,"Intl.ListFormat",4,192,1168
73+
65,"Intl.DurationFormat",4,192,1168
74+
66,"Intl.DateTimeFormat",4,192,1536
75+
69,"BigInt",4,192,1168
76+
70,"Symbol",4,192,1376
77+
72,"ArrayBuffer",4,192,2160
78+
79,"WebAssembly.Table",4,192,768
79+
80,"WebAssembly.Memory",4,192,768
80+
120,"NavigationHistoryEntry",1,192,192
81+
129,"StyleSheetCollection",1,192,192
82+
146,"DocumentTimeline",1,184,184
83+
116,"PerformancePaintTiming",2,176,176
84+
145,"<p>",2,176,176
85+
122,"ServiceWorkerContainer",1,168,168
86+
125,"Navigator",1,168,168
87+
155,"PerformanceNavigationTiming",1,168,168
88+
95,"MutationObserver (prototype) / https://example.com",1,156,196
89+
127,"Navigation",1,152,384
90+
71,"Boolean",4,144,752
91+
117,"PerformanceResourceTiming",1,136,136
92+
147,"<a>",1,136,192
93+
152,"LargestContentfulPaint",1,136,136
94+
38,"Tag",4,128,128
95+
115,"<style>",1,128,264
96+
136,"CustomElementRegistry",1,128,128
97+
138,"CSSRuleList",4,128,128
98+
141,"Storage",2,128,128
99+
13,"<body>",1,112,112
100+
18,"Array Iterator",4,112,720
101+
20,"{constructor, name, message, toString}",4,112,704
102+
22,"{<symbol Symbol.asyncIterator>, <symbol Symbol.asyncDispose>}",4,112,864
103+
24,"Iterator Helper",4,112,720
104+
26,"Map Iterator",4,112,720
105+
29,"Set Iterator",4,112,720
106+
31,"String Iterator",4,112,720
107+
51,"WeakRef",4,112,768
108+
52,"FinalizationRegistry",4,112,928
109+
57,"Intl.Collator",4,112,976
110+
58,"{constructor, <symbol Symbol.toStringTag>, resolvedOptions, adoptText, get adoptText, first, get first, next, get next}",4,112,1808
111+
63,"Intl.DisplayNames",4,112,928
112+
64,"Intl.Segmenter",4,112,928
113+
73,"Async-from-Sync Iterator",4,112,880
114+
74,"AsyncFunction",4,112,608
115+
75,"AsyncGeneratorFunction",4,112,656
116+
77,"WebAssembly.Module",4,112,448
117+
78,"WebAssembly.Instance",4,112,544
118+
81,"WebAssembly.Global",4,112,592
119+
82,"WebAssembly.Tag",4,112,448
120+
83,"WebAssembly.Exception",4,112,544
121+
84,"GeneratorFunction",4,112,656
122+
85,"{containing, <symbol Symbol.iterator>}",4,112,832
123+
86,"Segmenter String Iterator",4,112,720
124+
87,"{next, return}",4,112,672
125+
88,"RegExp String Iterator",4,112,720
126+
90,"SharedArrayBuffer",4,112,1552
127+
93,"WebAssembly.Suspending",4,112,592
128+
121,"DOMTokenList",2,112,112
129+
98,"HTMLDocument (prototype) / https://example.com",1,108,148
130+
148,"HTMLCollection",1,104,104
131+
150,"ScriptRunner",1,104,104
132+
5,"RegExp",1,100,176
133+
112,"<meta>",1,96,96
134+
114,"<title>",1,96,176
135+
101,"EventTarget",2,92,436
136+
111,"<div>",1,88,168
137+
113,"<head>",1,88,152
138+
140,"<html>",1,88,88
139+
144,"<h1>",1,88,88
140+
149,"Screen",1,88,88
141+
153,"DocumentType",1,88,88
142+
156,"VisibilityStateEntry",1,88,88
143+
132,"MutationObserver::Delegate",2,64,224
144+
137,"MutationObserverRegistration",1,64,64
145+
100,"{loadTimes, csi}",2,56,580
146+
102,"WindowProperties",2,56,280
147+
151,"FragmentDirective",1,56,56
148+
41,"(concatenated string)",2,40,88
149+
99,"{parse, stringify}",2,40,384
150+
124,"IntersectionObserverDelegate",1,40,40
151+
128,"NavigationActivation",1,40,40
152+
154,"PerformanceTiming",1,40,40
153+
157,"Pending activities",1,40,40
154+
14,"Object (global)",2,32,112
155+
142,"VisualViewport",1,32,32
156+
143,"Viewport",1,32,32
157+
8,"Window (global) / https://example.com",1,16,56
158+
12,"HTMLBodyElement",1,16,16
159+
97,"HTMLDocument (internal cache) / https://example.com",1,16,16
160+
107,"MutationObserver (internal cache) / https://example.com",1,16,16
161+
108,"HTMLBodyElement (internal cache) / https://example.com",1,16,16
162+
`;
163+
164+
exports[`memory > load_memory_snapshot > with default options 1`] = `
165+
## Heap Snapshot Data
166+
Statistics: {
167+
"total": 1121496,
168+
"native": {
169+
"total": 333392,
170+
"typedArrays": 0
171+
},
172+
"v8heap": {
173+
"total": 788104,
174+
"code": 42812,
175+
"jsArrays": 1312,
176+
"strings": 27920,
177+
"system": 199264
178+
}
179+
}
180+
Static Data: {
181+
"nodeCount": 27466,
182+
"rootNodeIndex": 0,
183+
"totalSize": 1121496,
184+
"maxJSObjectId": 54005
185+
}
186+
`;

tests/tools/memory.test.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {describe, it} from 'node:test';
1414
import {
1515
takeMemorySnapshot,
1616
exploreMemorySnapshot,
17+
getMemorySnapshotDetails,
1718
} from '../../src/tools/memory.js';
1819
import {withMcpContext} from '../utils.js';
1920

@@ -41,7 +42,7 @@ describe('memory', () => {
4142
});
4243

4344
describe('load_memory_snapshot', () => {
44-
it('with default options', async () => {
45+
it('with default options', async t => {
4546
await withMcpContext(async (response, context) => {
4647
const filePath = join(
4748
process.cwd(),
@@ -65,9 +66,34 @@ describe('memory', () => {
6566
.map(c => (c.type === 'text' ? c.text : ''))
6667
.join('\n');
6768

68-
// Check if response contains Statistics or Static Data
69-
assert.ok(output.includes('Statistics:'));
70-
assert.ok(output.includes('Static Data:'));
69+
t.assert.snapshot?.(output);
70+
});
71+
});
72+
});
73+
74+
describe('get_memory_snapshot_details', () => {
75+
it('with default options', async t => {
76+
await withMcpContext(async (response, context) => {
77+
const filePath = join(
78+
process.cwd(),
79+
'tests/fixtures/example.heapsnapshot',
80+
);
81+
82+
await getMemorySnapshotDetails.handler(
83+
{params: {filePath}},
84+
response,
85+
context,
86+
);
87+
88+
const responseData = await response.handle(
89+
getMemorySnapshotDetails.name,
90+
context,
91+
);
92+
const output = responseData.content
93+
.map(c => (c.type === 'text' ? c.text : ''))
94+
.join('\n');
95+
96+
t.assert.snapshot?.(output);
7197
});
7298
});
7399
});

0 commit comments

Comments
 (0)