Skip to content

Commit a9a0893

Browse files
committed
change where packages are installed
1 parent 5ef84f2 commit a9a0893

File tree

7 files changed

+123
-70
lines changed

7 files changed

+123
-70
lines changed

packages/bson-bench/src/base.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as BSON from 'bson';
22
import { readFileSync } from 'fs';
3+
import { join } from 'path';
34
import { performance } from 'perf_hooks';
45
import * as process from 'process';
56

@@ -116,10 +117,12 @@ function run(bson: BSONLib | ConstructibleBSON, config: BenchmarkSpecification)
116117

117118
function listener(message: RunBenchmarkMessage) {
118119
if (message.type === 'runBenchmark') {
119-
const packageSpec = new Package(message.benchmark.library);
120+
const packageSpec = new Package(message.benchmark.library, message.benchmark.installLocation);
120121
let bson: BSONLib;
121122
try {
122-
bson = require(packageSpec.computedModuleName);
123+
bson = require(
124+
join(message.benchmark.installLocation, 'node_modules', packageSpec.computedModuleName)
125+
);
123126
} catch (error) {
124127
reportErrorAndQuit(error as Error);
125128
return;

packages/bson-bench/src/common.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as cp from 'child_process';
22
import { once } from 'events';
3-
import * as path from 'path';
3+
import { join, sep } from 'path';
44

55
import { exists } from './utils';
66

@@ -26,8 +26,10 @@ export class Package {
2626
gitCommitish?: string;
2727
// path to local library
2828
localPath?: string;
29+
installPath: string;
2930

30-
constructor(libSpec: string) {
31+
constructor(libSpec: string, installPath: string) {
32+
this.installPath = installPath;
3133
let match: RegExpExecArray | null;
3234
if ((match = NPM_PACKAGE_REGEX.exec(libSpec))) {
3335
this.type = 'npm';
@@ -44,7 +46,7 @@ export class Package {
4446
this.library = match[1] as 'bson' | 'bson-ext';
4547

4648
this.localPath = match[2];
47-
this.computedModuleName = `${this.library}-local-${this.localPath.replaceAll(path.sep, '_')}`;
49+
this.computedModuleName = `${this.library}-local-${this.localPath.replaceAll(sep, '_')}`;
4850
} else {
4951
throw new Error('unknown package specifier');
5052
}
@@ -55,7 +57,7 @@ export class Package {
5557
*/
5658
check<B extends BSONLib>(): B | undefined {
5759
try {
58-
return require(this.computedModuleName);
60+
return require(join(this.installPath, 'node_modules', this.computedModuleName));
5961
} catch {
6062
return undefined;
6163
}
@@ -90,10 +92,10 @@ export class Package {
9092
break;
9193
}
9294

93-
const npmInstallProcess = cp.exec(
94-
`npm install ${this.computedModuleName}@${source} --no-save`,
95-
{ encoding: 'utf8', cwd: __dirname }
96-
);
95+
const npmInstallProcess = cp.exec(`npm install ${this.computedModuleName}@${source}`, {
96+
encoding: 'utf8',
97+
cwd: this.installPath
98+
});
9799

98100
const exitCode: number = (await once(npmInstallProcess, 'exit'))[0];
99101
if (exitCode !== 0) {
@@ -130,11 +132,12 @@ export type BenchmarkSpecification = {
130132
/** Specifier of the bson or bson-ext library to be used. Can be an npm package, git repository or
131133
* local package */
132134
library: string;
135+
installLocation?: string;
133136
};
134137

135138
export interface RunBenchmarkMessage {
136139
type: 'runBenchmark';
137-
benchmark: BenchmarkSpecification;
140+
benchmark: Omit<BenchmarkSpecification, 'installLocation'> & { installLocation: string };
138141
}
139142

140143
export interface ResultMessage {

packages/bson-bench/src/task.ts

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type ChildProcess, fork } from 'child_process';
22
import { once } from 'events';
3-
import { writeFile } from 'fs/promises';
3+
import { mkdir, rm, writeFile } from 'fs/promises';
4+
import { tmpdir } from 'os';
45
import * as path from 'path';
56

67
import {
@@ -11,25 +12,28 @@ import {
1112
type PerfSendResult,
1213
type ResultMessage
1314
} from './common';
15+
import { exists } from './utils';
1416

1517
/**
1618
* An individual benchmark task that runs in its own Node.js process
1719
*/
1820
export class Task {
1921
result: BenchmarkResult | undefined;
20-
benchmark: BenchmarkSpecification;
22+
benchmark: Omit<BenchmarkSpecification, 'installLocation'> & { installLocation: string };
2123
taskName: string;
2224
testName: string;
2325
/** @internal */
2426
children: ChildProcess[];
2527
/** @internal */
2628
hasRun: boolean;
2729

30+
static packageInstallLocation: string = path.join(tmpdir(), 'bsonBench');
31+
2832
constructor(benchmarkSpec: BenchmarkSpecification) {
2933
this.result = undefined;
30-
this.benchmark = benchmarkSpec;
3134
this.children = [];
3235
this.hasRun = false;
36+
this.benchmark = { ...benchmarkSpec, installLocation: Task.packageInstallLocation };
3337

3438
this.taskName = `${path.basename(this.benchmark.documentPath, 'json')}_${
3539
this.benchmark.operation
@@ -174,31 +178,39 @@ export class Task {
174178

175179
// install required modules before running child process as new Node processes need to know that
176180
// it exists before they can require it.
177-
const pack = new Package(this.benchmark.library);
178-
if (!pack.check()) await pack.install();
179-
// spawn child process
180-
const child = fork(`${__dirname}/base`, {
181-
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
182-
serialization: 'advanced'
183-
});
184-
child.send({ type: 'runBenchmark', benchmark: this.benchmark });
185-
this.children.push(child);
186-
187-
// listen for results or error
188-
const resultOrError: ResultMessage | ErrorMessage = (await once(child, 'message'))[0];
189-
190-
// wait for child to close
191-
await once(child, 'exit');
192-
193-
this.hasRun = true;
194-
switch (resultOrError.type) {
195-
case 'returnResult':
196-
this.result = resultOrError.result;
197-
return resultOrError.result;
198-
case 'returnError':
199-
throw resultOrError.error;
200-
default:
201-
throw new Error('Unexpected result returned from child process');
181+
if (!(await exists(Task.packageInstallLocation))) {
182+
await mkdir(Task.packageInstallLocation);
183+
}
184+
185+
try {
186+
const pack = new Package(this.benchmark.library, Task.packageInstallLocation);
187+
if (!pack.check()) await pack.install();
188+
// spawn child process
189+
const child = fork(`${__dirname}/base`, {
190+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
191+
serialization: 'advanced'
192+
});
193+
child.send({ type: 'runBenchmark', benchmark: this.benchmark });
194+
this.children.push(child);
195+
196+
// listen for results or error
197+
const resultOrError: ResultMessage | ErrorMessage = (await once(child, 'message'))[0];
198+
199+
// wait for child to close
200+
await once(child, 'exit');
201+
202+
this.hasRun = true;
203+
switch (resultOrError.type) {
204+
case 'returnResult':
205+
this.result = resultOrError.result;
206+
return resultOrError.result;
207+
case 'returnError':
208+
throw resultOrError.error;
209+
default:
210+
throw new Error('Unexpected result returned from child process');
211+
}
212+
} finally {
213+
await rm(Task.packageInstallLocation, { recursive: true, force: true });
202214
}
203215
}
204216
}

packages/bson-bench/test/unit/common.test.ts

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { expect } from 'chai';
2-
import { sep } from 'path';
2+
import { mkdir, rm } from 'fs/promises';
3+
import { tmpdir } from 'os';
4+
import { join, sep } from 'path';
35

46
import { Package } from '../../lib/common';
57
import { clearTestedDeps } from '../utils';
@@ -8,20 +10,32 @@ describe('common functionality', function () {
810
const BSON_PATH = process.env.BSON_PATH;
911

1012
context('Package', function () {
11-
beforeEach(clearTestedDeps);
12-
after(clearTestedDeps);
13+
let installDir: string;
14+
15+
after(async function () {
16+
await rm(installDir, { recursive: true, force: true });
17+
});
18+
19+
beforeEach(async function () {
20+
await clearTestedDeps(installDir);
21+
});
22+
23+
before(async function () {
24+
installDir = join(tmpdir(), 'bsonBenchTest');
25+
await mkdir(installDir);
26+
});
1327

1428
context('constructor()', function () {
1529
context('when given a correctly formatted npm package', function () {
1630
it('sets computedModuleName correctly', function () {
17-
const pack = new Package('[email protected]');
31+
const pack = new Package('[email protected]', installDir);
1832
expect(pack).to.haveOwnProperty('computedModuleName', 'bson-6.0.0');
1933
});
2034
});
2135

2236
context('when given a correctly formatted git repository', function () {
2337
it('sets computedModuleName correctly', function () {
24-
const pack = new Package('bson#eb98b8c39d6d5ba4ce7231ab9e0f29495d74b994');
38+
const pack = new Package('bson#eb98b8c39d6d5ba4ce7231ab9e0f29495d74b994', installDir);
2539
expect(pack).to.haveOwnProperty(
2640
'computedModuleName',
2741
'bson-git-eb98b8c39d6d5ba4ce7231ab9e0f29495d74b994'
@@ -31,13 +45,16 @@ describe('common functionality', function () {
3145

3246
context('when trying to install an npm package apart from bson or bson-ext', function () {
3347
it('throws an error', function () {
34-
expect(() => new Package('[email protected]')).to.throw(Error, /unknown package specifier/);
48+
expect(() => new Package('[email protected]', installDir)).to.throw(
49+
Error,
50+
/unknown package specifier/
51+
);
3552
});
3653
});
3754

3855
context('when trying to install a git package apart from bson or bson-ext', function () {
3956
it('throws an error', function () {
40-
expect(() => new Package('notBson#abcdabcdabcd')).to.throw(
57+
expect(() => new Package('notBson#abcdabcdabcd', installDir)).to.throw(
4158
Error,
4259
/unknown package specifier/
4360
);
@@ -50,7 +67,7 @@ describe('common functionality', function () {
5067
console.log('Skipping since BSON_PATH is undefined');
5168
this.skip();
5269
}
53-
const pack = new Package(`bson:${BSON_PATH}`);
70+
const pack = new Package(`bson:${BSON_PATH}`, installDir);
5471
expect(pack).to.haveOwnProperty(
5572
'computedModuleName',
5673
`bson-local-${BSON_PATH.replaceAll(sep, '_')}`
@@ -62,14 +79,14 @@ describe('common functionality', function () {
6279
context('#check()', function () {
6380
context('when package is not installed', function () {
6481
it('returns undefined', function () {
65-
const pack = new Package('bson@6');
82+
const pack = new Package('bson@6', installDir);
6683
expect(pack.check()).to.be.undefined;
6784
});
6885
});
6986

7087
context('when package is installed', function () {
7188
it('returns the module', async function () {
72-
const pack = new Package('[email protected]');
89+
const pack = new Package('[email protected]', installDir);
7390
await pack.install();
7491
expect(pack.check()).to.not.be.undefined;
7592
});
@@ -80,23 +97,23 @@ describe('common functionality', function () {
8097
context('when given a correctly formatted npm package that exists', function () {
8198
for (const lib of ['[email protected]', '[email protected]', 'bson@latest', 'bson-ext@latest']) {
8299
it(`installs ${lib} successfully`, async function () {
83-
const pack = new Package(lib);
100+
const pack = new Package(lib, installDir);
84101
await pack.install();
85102
});
86103
}
87104
});
88105

89106
context('when given a correctly formatted npm package that does not exist', function () {
90107
it('throws an error', async function () {
91-
const bson9000 = new Package('bson@9000');
108+
const bson9000 = new Package('bson@9000', installDir);
92109
const error = await bson9000.install().catch(error => error);
93110
expect(error).to.be.instanceOf(Error);
94111
});
95112
});
96113

97114
context('when given a correctly formatted git package using commit that exists', function () {
98115
it('installs successfully', async function () {
99-
const bson6Git = new Package('bson#58c002d');
116+
const bson6Git = new Package('bson#58c002d', installDir);
100117
const maybeError = await bson6Git.install().catch(error => error);
101118
expect(maybeError).to.be.undefined;
102119
});
@@ -107,7 +124,10 @@ describe('common functionality', function () {
107124
function () {
108125
// TODO: NODE-6361: Unskip and fix this test.
109126
it.skip('throws an error', async function () {
110-
const bson6Git = new Package('bson#58c002d87bca9bbe7c7001cc6acae54e90a951bcf');
127+
const bson6Git = new Package(
128+
'bson#58c002d87bca9bbe7c7001cc6acae54e90a951bcf',
129+
installDir
130+
);
111131
const maybeError = await bson6Git.install().catch(error => error);
112132
expect(maybeError).to.be.instanceOf(Error);
113133
});
@@ -118,7 +138,7 @@ describe('common functionality', function () {
118138
'when given a correctly formatted git package using git tag that exists',
119139
function () {
120140
it('installs successfully', async function () {
121-
const bson6Git = new Package('bson#v6.0.0');
141+
const bson6Git = new Package('bson#v6.0.0', installDir);
122142
const maybeError = await bson6Git.install().catch(error => error);
123143
expect(maybeError).to.be.undefined;
124144
});
@@ -129,7 +149,7 @@ describe('common functionality', function () {
129149
'when given a correctly formatted git package using git tag that does not exist',
130150
function () {
131151
it('throws an error', async function () {
132-
const bson6Git = new Package('bson#v999.999.9');
152+
const bson6Git = new Package('bson#v999.999.9', installDir);
133153
const maybeError = await bson6Git.install().catch(error => error);
134154
expect(maybeError).to.be.instanceOf(Error);
135155
});
@@ -143,7 +163,7 @@ describe('common functionality', function () {
143163
this.skip();
144164
}
145165

146-
const bsonLocal = new Package(`bson:${BSON_PATH}`);
166+
const bsonLocal = new Package(`bson:${BSON_PATH}`, installDir);
147167
const maybeError = await bsonLocal.install().catch(error => error);
148168
expect(maybeError).to.not.be.instanceOf(Error, maybeError.message);
149169
});
@@ -152,7 +172,8 @@ describe('common functionality', function () {
152172
context('when given a path that does not exist', function () {
153173
it('throws an error', async function () {
154174
const bsonLocal = new Package(
155-
`bson:/highly/unlikely/path/to/exist/that/should/point/to/bson`
175+
`bson:/highly/unlikely/path/to/exist/that/should/point/to/bson`,
176+
installDir
156177
);
157178
const maybeError = await bsonLocal.install().catch(error => error);
158179
expect(maybeError).to.be.instanceOf(Error);

packages/bson-bench/test/unit/suite.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { expect } from 'chai';
22
import { readFile } from 'fs/promises';
33

4-
import { Suite } from '../../lib';
4+
import { Suite, Task } from '../../lib';
55
import { exists } from '../../src/utils';
66
import { clearTestedDeps } from '../utils';
77

88
describe('Suite', function () {
9-
beforeEach(clearTestedDeps);
10-
after(clearTestedDeps);
9+
beforeEach(async function () {
10+
await clearTestedDeps(Task.packageInstallLocation);
11+
});
12+
13+
after(async function () {
14+
await clearTestedDeps(Task.packageInstallLocation);
15+
});
1116

1217
describe('#task()', function () {
1318
it('returns the Suite it was called on', function () {

packages/bson-bench/test/unit/task.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ import { clearTestedDeps } from '../utils';
1010
const LOCAL_BSON = path.join(__dirname, '..', '..', 'node_modules', 'bson');
1111

1212
describe('Task', function () {
13-
beforeEach(clearTestedDeps);
14-
after(clearTestedDeps);
13+
beforeEach(async function () {
14+
await clearTestedDeps(Task.packageInstallLocation);
15+
});
16+
17+
after(async function () {
18+
await clearTestedDeps(Task.packageInstallLocation);
19+
});
1520

1621
const BSON_PATH = process.env.BSON_PATH;
1722
const BSON_EXT_PATH = process.env.BSON_EXT_PATH;

0 commit comments

Comments
 (0)