@memlab/api 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/API.js +4 -4
- package/dist/__tests__/API/E2EMemoryLeakFilter.test.js +25 -16
- package/dist/__tests__/API/E2EResultReader.test.js +3 -3
- package/dist/__tests__/API/E2EShapeUnboundGrowthAnalysis.test.js +1 -1
- package/dist/__tests__/API/E2EStringAnalysis.test.js +1 -1
- package/dist/__tests__/heap/lib/HeapParserTestUtils.d.ts.map +1 -1
- package/dist/__tests__/heap/lib/HeapParserTestUtils.js +22 -15
- package/package.json +6 -6
package/dist/API.js
CHANGED
|
@@ -127,7 +127,7 @@ function takeSnapshots() {
|
|
|
127
127
|
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
128
128
|
const config = getConfigFromRunOptions(options);
|
|
129
129
|
const state = APIStateManager_1.default.getAndUpdateState(config, options);
|
|
130
|
-
const testPlanner = new e2e_1.TestPlanner();
|
|
130
|
+
const testPlanner = new e2e_1.TestPlanner({ config });
|
|
131
131
|
const { evalInBrowserAfterInitLoad } = options;
|
|
132
132
|
yield testInBrowser({ testPlanner, config, evalInBrowserAfterInitLoad });
|
|
133
133
|
const ret = BrowserInteractionResultReader_1.default.from(config.workDir);
|
|
@@ -287,9 +287,9 @@ function getConfigFromRunOptions(options) {
|
|
|
287
287
|
if ('webWorker' in options) {
|
|
288
288
|
config.isAnalyzingMainThread = false;
|
|
289
289
|
const value = options.webWorker;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
// config is a singleton shared by all API calls in the process, so the
|
|
291
|
+
// title has to be cleared when this run targets any web worker
|
|
292
|
+
config.targetWorkerTitle = typeof value === 'string' ? value : null;
|
|
293
293
|
}
|
|
294
294
|
else {
|
|
295
295
|
config.isAnalyzingMainThread = true;
|
|
@@ -27,6 +27,9 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
27
27
|
const index_1 = require("../../index");
|
|
28
28
|
const E2ETestSettings_1 = require("./lib/E2ETestSettings");
|
|
29
29
|
beforeEach(E2ETestSettings_1.testSetup);
|
|
30
|
+
function hasPathEdgeWithName(leak, edgeName) {
|
|
31
|
+
return Object.keys(leak).some(key => key.includes(`--${edgeName} (`));
|
|
32
|
+
}
|
|
30
33
|
function injectDetachedDOMElements() {
|
|
31
34
|
// @ts-ignore
|
|
32
35
|
window.injectHookForLink4 = () => {
|
|
@@ -74,8 +77,11 @@ test('self-defined retainer trace filter work as expected (part 1)', () => __awa
|
|
|
74
77
|
app: () => 'test-spa',
|
|
75
78
|
url: () => '',
|
|
76
79
|
action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
|
|
77
|
-
retainerReferenceFilter: (edge) => {
|
|
78
|
-
|
|
80
|
+
retainerReferenceFilter: (edge, _snapshot, isReferenceUsedByDefault) => {
|
|
81
|
+
if (edge.name_or_index === '_path_1') {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return isReferenceUsedByDefault;
|
|
79
85
|
},
|
|
80
86
|
};
|
|
81
87
|
const workDir = path_1.default.join(os_1.default.tmpdir(), 'memlab-api-test', (0, E2ETestSettings_1.getUniqueID)());
|
|
@@ -85,12 +91,12 @@ test('self-defined retainer trace filter work as expected (part 1)', () => __awa
|
|
|
85
91
|
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
86
92
|
workDir,
|
|
87
93
|
});
|
|
88
|
-
// detected
|
|
89
|
-
expect(result.leaks.length).
|
|
90
|
-
// expect
|
|
91
|
-
expect(result.leaks.every(leak => !
|
|
92
|
-
// expect some of the
|
|
93
|
-
expect(result.leaks.some(leak =>
|
|
94
|
+
// detected leak trace clusters
|
|
95
|
+
expect(result.leaks.length).toBeGreaterThanOrEqual(1);
|
|
96
|
+
// expect none of the retainer trace paths to go through _path_1
|
|
97
|
+
expect(result.leaks.every(leak => !hasPathEdgeWithName(leak, '_path_1'))).toBe(true);
|
|
98
|
+
// expect some of the retainer trace paths to go through _path_2
|
|
99
|
+
expect(result.leaks.some(leak => hasPathEdgeWithName(leak, '_path_2'))).toBe(true);
|
|
94
100
|
const reader = result.runResult;
|
|
95
101
|
expect(path_1.default.resolve(reader.getRootDirectory())).toBe(path_1.default.resolve(workDir));
|
|
96
102
|
}), E2ETestSettings_1.testTimeout);
|
|
@@ -99,8 +105,11 @@ test('self-defined retainer trace filter work as expected (part 2)', () => __awa
|
|
|
99
105
|
app: () => 'test-spa',
|
|
100
106
|
url: () => '',
|
|
101
107
|
action: (page) => __awaiter(void 0, void 0, void 0, function* () { return yield page.click('[data-testid="link-4"]'); }),
|
|
102
|
-
retainerReferenceFilter: (edge) => {
|
|
103
|
-
|
|
108
|
+
retainerReferenceFilter: (edge, _snapshot, isReferenceUsedByDefault) => {
|
|
109
|
+
if (edge.name_or_index === '_path_2') {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return isReferenceUsedByDefault;
|
|
104
113
|
},
|
|
105
114
|
};
|
|
106
115
|
const workDir = path_1.default.join(os_1.default.tmpdir(), 'memlab-api-test', (0, E2ETestSettings_1.getUniqueID)());
|
|
@@ -110,12 +119,12 @@ test('self-defined retainer trace filter work as expected (part 2)', () => __awa
|
|
|
110
119
|
evalInBrowserAfterInitLoad: injectDetachedDOMElements,
|
|
111
120
|
workDir,
|
|
112
121
|
});
|
|
113
|
-
// detected
|
|
114
|
-
expect(result.leaks.length).
|
|
115
|
-
// expect
|
|
116
|
-
expect(result.leaks.every(leak => !
|
|
117
|
-
// expect some of the
|
|
118
|
-
expect(result.leaks.some(leak =>
|
|
122
|
+
// detected leak trace clusters
|
|
123
|
+
expect(result.leaks.length).toBeGreaterThanOrEqual(1);
|
|
124
|
+
// expect none of the retainer trace paths to go through _path_2
|
|
125
|
+
expect(result.leaks.every(leak => !hasPathEdgeWithName(leak, '_path_2'))).toBe(true);
|
|
126
|
+
// expect some of the retainer trace paths to go through _path_1
|
|
127
|
+
expect(result.leaks.some(leak => hasPathEdgeWithName(leak, '_path_1'))).toBe(true);
|
|
119
128
|
const reader = result.runResult;
|
|
120
129
|
expect(path_1.default.resolve(reader.getRootDirectory())).toBe(path_1.default.resolve(workDir));
|
|
121
130
|
}), E2ETestSettings_1.testTimeout);
|
|
@@ -53,9 +53,9 @@ function checkResultReader(result) {
|
|
|
53
53
|
expect(runMeta.app).toBe('test-spa');
|
|
54
54
|
result.cleanup();
|
|
55
55
|
expect(fs_1.default.existsSync(workDir)).toBe(false);
|
|
56
|
-
expect(() => result.getRootDirectory()).
|
|
57
|
-
expect(() => result.getSnapshotFileDir()).
|
|
58
|
-
expect(() => result.getSnapshotFiles()).
|
|
56
|
+
expect(() => result.getRootDirectory()).toThrow();
|
|
57
|
+
expect(() => result.getSnapshotFileDir()).toThrow();
|
|
58
|
+
expect(() => result.getSnapshotFiles()).toThrow();
|
|
59
59
|
}
|
|
60
60
|
test('result data/file reader is working as expected', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
61
61
|
const result = yield (0, index_1.warmupAndTakeSnapshots)({
|
|
@@ -84,7 +84,7 @@ function testIncorrectUseage(result) {
|
|
|
84
84
|
// expect incorrect use of heap analysis to throw
|
|
85
85
|
const snapshotFile = result.getSnapshotFiles().pop();
|
|
86
86
|
const analysis = new index_1.ShapeUnboundGrowthAnalysis();
|
|
87
|
-
expect(() => __awaiter(this, void 0, void 0, function* () { return yield analysis.analyzeSnapshotFromFile(snapshotFile); })).rejects.
|
|
87
|
+
expect(() => __awaiter(this, void 0, void 0, function* () { return yield analysis.analyzeSnapshotFromFile(snapshotFile); })).rejects.toThrow();
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
function testAnalysisWithSpecifiedWorkDir(result) {
|
|
@@ -54,7 +54,7 @@ test('String analysis works as expected', () => __awaiter(void 0, void 0, void 0
|
|
|
54
54
|
// expect incorrect use of heap analysis to throw
|
|
55
55
|
const snapshotDir = result.getSnapshotFileDir();
|
|
56
56
|
analysis = new index_1.StringAnalysis();
|
|
57
|
-
expect(() => __awaiter(void 0, void 0, void 0, function* () { return yield analysis.analyzeSnapshotsInDirectory(snapshotDir); })).rejects.
|
|
57
|
+
expect(() => __awaiter(void 0, void 0, void 0, function* () { return yield analysis.analyzeSnapshotsInDirectory(snapshotDir); })).rejects.toThrow();
|
|
58
58
|
}), E2ETestSettings_1.testTimeout);
|
|
59
59
|
test('analyze function works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
60
|
const result = yield (0, index_1.warmupAndTakeSnapshots)({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeapParserTestUtils.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/lib/HeapParserTestUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"HeapParserTestUtils.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/heap/lib/HeapParserTestUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAWhD,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,IAAI,EACxB,eAAe,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,OAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -23,6 +23,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.isExpectedSnapshot = isExpectedSnapshot;
|
|
25
25
|
const fs_1 = __importDefault(require("fs"));
|
|
26
|
+
const os_1 = __importDefault(require("os"));
|
|
26
27
|
const path_1 = __importDefault(require("path"));
|
|
27
28
|
const core_1 = require("@memlab/core");
|
|
28
29
|
const puppeteer = core_1.config.isFB
|
|
@@ -69,21 +70,27 @@ const TEST_URL = 'about:blank';
|
|
|
69
70
|
function dumpHeap(snapshotFile, leakInjector) {
|
|
70
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
72
|
core_1.utils.tryToMutePuppeteerWarning();
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
const userDataDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'memlab-heap-test-'));
|
|
74
|
+
try {
|
|
75
|
+
const browser = yield puppeteer.launch(Object.assign(Object.assign({}, core_1.config.puppeteerConfig), { userDataDir }));
|
|
76
|
+
const page = yield browser.newPage();
|
|
77
|
+
// set page size
|
|
78
|
+
yield page.setViewport({
|
|
79
|
+
width: 1680,
|
|
80
|
+
height: 1050,
|
|
81
|
+
deviceScaleFactor: 1,
|
|
82
|
+
});
|
|
83
|
+
// visit page
|
|
84
|
+
yield page.goto(TEST_URL);
|
|
85
|
+
// insert a memory leak object
|
|
86
|
+
yield page.evaluate(leakInjector);
|
|
87
|
+
// take a heap snapshot
|
|
88
|
+
yield saveSnapshotToFile(page, snapshotFile);
|
|
89
|
+
yield browser.close();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
fs_1.default.rmSync(userDataDir, { recursive: true, force: true });
|
|
93
|
+
}
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
96
|
let fileId = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memlab/api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "memlab API",
|
|
6
6
|
"author": "Liang Gong <lgong@meta.com>",
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@memlab/core": "^2.0.
|
|
31
|
-
"@memlab/e2e": "^2.0.
|
|
32
|
-
"@memlab/heap-analysis": "^2.0.
|
|
30
|
+
"@memlab/core": "^2.0.5",
|
|
31
|
+
"@memlab/e2e": "^2.0.5",
|
|
32
|
+
"@memlab/heap-analysis": "^2.0.5",
|
|
33
33
|
"ansi": "^0.3.1",
|
|
34
34
|
"babar": "^0.2.0",
|
|
35
35
|
"chalk": "^4.0.0",
|
|
36
36
|
"fs-extra": "^4.0.2",
|
|
37
37
|
"minimist": "^1.2.8",
|
|
38
|
-
"puppeteer": "
|
|
39
|
-
"puppeteer-core": "
|
|
38
|
+
"puppeteer": "24.31.0",
|
|
39
|
+
"puppeteer-core": "24.31.0",
|
|
40
40
|
"string-width": "^4.2.0",
|
|
41
41
|
"util.promisify": "^1.1.1",
|
|
42
42
|
"xvfb": "^0.4.0"
|