Skip to content

Commit

Permalink
feat: make memory-leak-detector connection info configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
BobrImperator committed Sep 10, 2024
1 parent 0d01911 commit 56bd1aa
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 35 deletions.
74 changes: 71 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Memory Leak Detector is a tiny node server that exposes an HTTP API for making s

It requires a path to the source code so it can scan codebase for `class`.

`pnpm memory-leak-detector ./app`
`pnpm memory-leak-detector --path ./app`

## Example app

Expand All @@ -22,11 +22,47 @@ An example app can be found [here](https://github.com/mainmatter/memory-leak-det
Install the `memory-leak-detector` dependency

- `pnpm add -D memory-leak-detector`
- Edit your test runner's option to run Chrome with `--remote-debugging-port=9222` option.
Example Configuration for Testem.

```js
// testem.js
"use strict";

module.exports = {
test_page: "tests/index.html?hidepassed",
disable_watching: true,
launch_in_ci: ["Chrome"],
launch_in_dev: ["Chrome"],
browser_start_timeout: 120,
browser_args: {
Chrome: {
ci: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? "--no-sandbox" : null,
"--headless",
"--disable-dev-shm-usage",
"--disable-software-rasterizer",
"--mute-audio",
"--remote-debugging-port=9222",
"--window-size=1440,900",
].filter(Boolean),
dev: [
"--disable-dev-shm-usage",
"--disable-software-rasterizer",
"--mute-audio",
"--remote-debugging-port=9222",
"--window-size=1440,900",
].filter(Boolean),
},
},
};
```

Define a script that runs a `memory-leak-detector` node server and listen, run your tests as usual, finally wait for the process results.

```sh
pnpm memory-leak-detector ./app & pid=$!; pnpm test; wait $pid
pnpm memory-leak-detector --path ./app & pid=$!; pnpm test; wait $pid
```

The reason for waiting for that process' pid is that e.g. Qunit doesn't have a good way to dynamically create and run a test at the end of your test suite while we must have a way to fail the CI in case something's wrong.
Expand All @@ -35,7 +71,7 @@ For that reason `detectLeakingClasses` is meant to be used at the end only, beca
That also means that if you don't intend to use `detectLeakingClasses`, your script should not wait for the `memory-leak-detector` to exit i.e.

```sh
pnpm memory-leak-detector ./app & pnpm test
pnpm memory-leak-detector --path ./app & pnpm test
```

### Checking for memory leaks after test suite finishes
Expand Down Expand Up @@ -88,3 +124,35 @@ test("paginating back and forth", async function (assert) {
```
![Checking for specific class in a test](https://github.com/mainmatter/memory-leak-detector/blob/main/assets/memory_leak_2.png)
## Configuration
### memory-leak-detector
- path: <required>, has no default value.
- port: <optional>, defaults to 3000.
### Chrome / Test runner
By default a Google Chrome instance is expected to be running on address `127.0.0.1:9222`.
In case it runs on a different address, you can provide the connection info when calling the HTTP API e.g.
```js
await detectLeakingClasses("title", document.title, {
host: "localhost",
port: 9333,
});
```

```js
const assertions = { MyClass: 10 };
const results = await detectMemoryLeak(
"url",
document.location.href,
assertions,
{
host: "localhost",
port: 9333,
},
);
```
19 changes: 0 additions & 19 deletions packages/memory-leak-detector/cli.js

This file was deleted.

17 changes: 14 additions & 3 deletions packages/memory-leak-detector/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export async function detectMemoryLeak(by, value, assertions) {
// @TODO: Make the server configurable
export async function detectMemoryLeak(
by,
value,
assertions,
browserAddress = { port: 9222, host: "127.0.0.1" },
) {
let response = await fetch(`http://localhost:3000/detect_memory_leak`, {
method: "POST",
headers: {
Expand All @@ -10,6 +14,7 @@ export async function detectMemoryLeak(by, value, assertions) {
by,
value,
},
...browserAddress,
assertions,
}),
});
Expand All @@ -18,7 +23,12 @@ export async function detectMemoryLeak(by, value, assertions) {
return json.results;
}

export async function detectLeakingClasses(by, value) {
export async function detectLeakingClasses(
by,
value,

browserAddress = { port: 9222, host: "127.0.0.1" },
) {
let response = await fetch(`http://localhost:3000/detect_leaking_classes`, {
method: "POST",
headers: {
Expand All @@ -29,6 +39,7 @@ export async function detectLeakingClasses(by, value) {
by,
value,
},
...browserAddress,
}),
});
let json = await response.json();
Expand Down
1 change: 1 addition & 0 deletions packages/memory-leak-detector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@babel/parser": "^7.12.10",
"ast-types": "^0.14.2",
"chrome-remote-interface": "^0.28.2",
"commander": "^12.1.0",
"ember-cli-babel": "^7.22.1",
"ember-cli-htmlbars": "^5.3.1",
"glob": "^11.0.0",
Expand Down
28 changes: 19 additions & 9 deletions packages/memory-leak-detector/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,22 @@ const inventoryClasses = require("./inventory-classes.js");
const express = require("express");
const cors = require("cors");

const { program } = require("commander");

function main() {
const [, , sourceDirectory] = process.argv;
program
.requiredOption(
"--path <path>",
"A path to the codebase in order to scan any class declaration for automated leak detection.",
)
.option(
"--port <port>",
"A port number that `memory-leak-detector` should listen to.",
3000,
);
program.parse();

const cliOptions = program.opts();
const app = express();

app.use(cors());
Expand All @@ -16,27 +30,23 @@ function main() {
app.post(
"/detect_memory_leak",
handleDetectMemoryLinkRequest({
port: 9222,
host: "127.0.0.1",
classes: [],
writeSnapshot: false,
}),
);
app.post(
"/detect_leaking_classes",
checkLeakingClasses({
port: 9222,
host: "127.0.0.1",
allowedToLeak: ["App"],
classes: inventoryClasses(sourceDirectory),
classes: inventoryClasses(cliOptions.path),
writeSnapshot: false,
}),
);

app.post("/close", (_req, _res) => process.exit(0))
app.post("/close", (_req, _res) => process.exit(0));

app.listen(3000, () => {
console.log("Memory Leak Detector listening on port :3000");
app.listen(cliOptions.port, () => {
console.log(`Memory Leak Detector listening on port :${cliOptions.port}`);
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"start": "ember serve",
"test": "concurrently \"pnpm:lint\" \"pnpm:test:*\" --names \"lint,test:\"",
"test:ember": "ember test",
"start:memory-leak-detector": "memory-leak-detector ./app/",
"start:memory-leak-detector": "memory-leak-detector --path ./app/",
"test:memory-leak": "ember test --filter MEMORY_LEAK",
"test:users": "ember test --filter Users",
"test": "ember test --filter !MEMORY_LEAK"
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 56bd1aa

Please sign in to comment.