Skip to content

Commit 7ab2eb3

Browse files
Merge pull request #600 from jvalue/590-current-dir-as-workdir
Configure interpreter to use current directory as workdir
2 parents 1a1e9bb + bde524a commit 7ab2eb3

File tree

6 files changed

+100
-35
lines changed

6 files changed

+100
-35
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg
2+
//
3+
// SPDX-License-Identifier: AGPL-3.0-only
4+
5+
/**
6+
* Gets the current directory path.
7+
* We use this method for emulating tests in other directory to configure the correct working directory.
8+
*/
9+
export function getCurrentDir(): string {
10+
return process.cwd();
11+
}

apps/interpreter/src/examples-smoke-test.spec.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@
33
// SPDX-License-Identifier: AGPL-3.0-only
44

55
import path from 'node:path';
6+
import { fileURLToPath } from 'node:url';
67

78
import { processExitMockImplementation } from '@jvalue/jayvee-execution/test';
89
import {
910
PostgresLoaderExecutorMock,
1011
SQLiteLoaderExecutorMock,
1112
} from '@jvalue/jayvee-extensions/rdbms/test';
1213
import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test';
13-
import {
14-
createJayveeServices,
15-
initializeWorkspace,
16-
} from '@jvalue/jayvee-language-server';
17-
import { NodeFileSystem } from 'langium/node';
1814
import nock from 'nock';
1915
import { type MockInstance, vi } from 'vitest';
2016

@@ -44,9 +40,22 @@ vi.mock('sqlite3', () => {
4440
};
4541
});
4642

47-
describe('jv example smoke tests', () => {
48-
const baseDir = path.resolve(__dirname, '../../../example/');
43+
// simulate as if we were starting the jv cli in the example dir
44+
vi.mock('./current-dir', () => {
45+
const currentDirMock = () =>
46+
path.join(
47+
path.dirname(fileURLToPath(import.meta.url)), // relative to this test file
48+
'..',
49+
'..',
50+
'..',
51+
'example',
52+
);
53+
return {
54+
getCurrentDir: currentDirMock,
55+
};
56+
});
4957

58+
describe('jv example smoke tests', () => {
5059
const defaultOptions: RunOptions = {
5160
pipeline: '.*',
5261
env: new Map<string, string>(),
@@ -61,10 +70,7 @@ describe('jv example smoke tests', () => {
6170
let postgresLoaderMock: PostgresLoaderExecutorMock;
6271
let sqliteLoaderMock: SQLiteLoaderExecutorMock;
6372

64-
beforeAll(async () => {
65-
const services = createJayveeServices(NodeFileSystem).Jayvee;
66-
await initializeWorkspace(services);
67-
73+
beforeAll(() => {
6874
exitSpy = vi
6975
.spyOn(process, 'exit')
7076
.mockImplementation(processExitMockImplementation);
@@ -99,7 +105,7 @@ describe('jv example smoke tests', () => {
99105
});
100106
sqliteLoaderMock.setup();
101107

102-
await runAction(path.resolve(baseDir, 'cars.jv'), {
108+
await runAction('cars.jv', {
103109
...defaultOptions,
104110
});
105111

@@ -131,7 +137,7 @@ describe('jv example smoke tests', () => {
131137
postgresLoaderMock.setup();
132138
sqliteLoaderMock.setup();
133139

134-
await runAction(path.resolve(baseDir, 'electric-vehicles.jv'), {
140+
await runAction('electric-vehicles.jv', {
135141
...defaultOptions,
136142
env: new Map<string, string>([
137143
['DB_HOST', 'mock'],
@@ -194,7 +200,7 @@ describe('jv example smoke tests', () => {
194200
});
195201
sqliteLoaderMock.setup();
196202

197-
await runAction(path.resolve(baseDir, 'gtfs-rt.jv'), {
203+
await runAction('gtfs-rt.jv', {
198204
...defaultOptions,
199205
});
200206

@@ -222,7 +228,7 @@ describe('jv example smoke tests', () => {
222228
});
223229
sqliteLoaderMock.setup();
224230

225-
await runAction(path.resolve(baseDir, 'gtfs-static.jv'), {
231+
await runAction('gtfs-static.jv', {
226232
...defaultOptions,
227233
});
228234

apps/interpreter/src/parse-only.spec.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import fs from 'node:fs';
66
import path from 'node:path';
77
import process from 'node:process';
8+
import { fileURLToPath } from 'node:url';
89

910
import { type JayveeInterpreter } from '@jvalue/jayvee-interpreter-lib';
1011

@@ -20,11 +21,27 @@ const interpreterMock: JayveeInterpreter = {
2021

2122
vi.stubGlobal('DefaultJayveeInterpreter', interpreterMock);
2223

24+
const dirPathOfThisTest = path.dirname(fileURLToPath(import.meta.url));
25+
const pathExamplesRelativeToThisTest = path.join('..', '..', '..', 'example');
26+
27+
// simulate as if we were starting the jv cli in the example dir
28+
vi.mock('./current-dir', () => {
29+
const currentDirMock = () =>
30+
path.join(dirPathOfThisTest, pathExamplesRelativeToThisTest);
31+
return {
32+
getCurrentDir: currentDirMock,
33+
};
34+
});
35+
2336
describe('Parse Only', () => {
24-
const pathToValidModel = path.resolve(__dirname, '../../../example/cars.jv');
25-
const pathToInvalidModel = path.resolve(
26-
__dirname,
27-
'../test/assets/broken-model.jv',
37+
const pathToValidModelFromExamplesDir = 'cars.jv';
38+
const pathToInvalidModelFromExamplesDir = path.join(
39+
'..',
40+
'apps',
41+
'interpreter',
42+
'test',
43+
'assets',
44+
'broken-model.jv',
2845
);
2946

3047
const defaultOptions: RunOptions = {
@@ -51,7 +68,7 @@ describe('Parse Only', () => {
5168

5269
it('should exit with 0 on a valid option', async () => {
5370
await expect(
54-
runAction(pathToValidModel, {
71+
runAction(pathToValidModelFromExamplesDir, {
5572
...defaultOptions,
5673
parseOnly: true,
5774
}),
@@ -62,10 +79,15 @@ describe('Parse Only', () => {
6279
});
6380

6481
it('should exit with 1 on error', async () => {
65-
expect(fs.existsSync(pathToInvalidModel)).toBe(true);
82+
const modelPathRelativeToThisTest = path.join(
83+
dirPathOfThisTest,
84+
pathExamplesRelativeToThisTest,
85+
pathToInvalidModelFromExamplesDir,
86+
);
87+
expect(fs.existsSync(modelPathRelativeToThisTest)).toBe(true);
6688

6789
await expect(
68-
runAction(pathToInvalidModel, {
90+
runAction(pathToInvalidModelFromExamplesDir, {
6991
...defaultOptions,
7092
parseOnly: true,
7193
}),

apps/interpreter/src/run-action.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//
33
// SPDX-License-Identifier: AGPL-3.0-only
44

5+
import path from 'node:path';
56
import process from 'node:process';
67

78
import {
@@ -16,6 +17,7 @@ import {
1617
type JayveeServices,
1718
} from '@jvalue/jayvee-language-server';
1819

20+
import { getCurrentDir } from './current-dir';
1921
import { parsePipelineMatcherRegExp, parseRunOptions } from './run-options';
2022

2123
export async function runAction(
@@ -33,20 +35,24 @@ export async function runAction(
3335
return process.exit(ExitCode.FAILURE);
3436
}
3537

38+
const currentDir = getCurrentDir();
39+
const workingDir = currentDir;
40+
const absoluteFilePath = path.join(currentDir, filePath);
41+
3642
const interpreter = new DefaultJayveeInterpreter({
3743
pipelineMatcher: (pipelineDefinition) =>
3844
pipelineRegExp.test(pipelineDefinition.name),
3945
env: options.env,
4046
debug: options.debug,
4147
debugGranularity: options.debugGranularity,
4248
debugTarget: options.debugTarget,
43-
});
49+
}).addWorkspace(workingDir);
4450

4551
if (options.parseOnly === true) {
46-
return await runParseOnly(filePath, interpreter);
52+
return await runParseOnly(absoluteFilePath, interpreter);
4753
}
4854

49-
const exitCode = await interpreter.interpretFile(filePath);
55+
const exitCode = await interpreter.interpretFile(absoluteFilePath);
5056
process.exit(exitCode);
5157
}
5258

libs/interpreter-lib/src/interpreter.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
// eslint-disable-next-line unicorn/prefer-node-protocol
66
import { strict as assert } from 'assert';
7+
import path from 'node:path';
78

89
import {
910
type DebugGranularity,
@@ -28,9 +29,11 @@ import {
2829
type RuntimeParameterProvider,
2930
type WrapperFactoryProvider,
3031
createJayveeServices,
32+
initializeWorkspace,
3133
internalValueToString,
3234
} from '@jvalue/jayvee-language-server';
3335
import chalk from 'chalk';
36+
import { type WorkspaceFolder } from 'langium';
3437
import { NodeFileSystem } from 'langium/node';
3538

3639
import { LoggerFactory } from './logging';
@@ -80,7 +83,7 @@ export interface JayveeInterpreter {
8083
* Parses a model without executing it.
8184
* Also sets up the environment so that the model can be properly executed.
8285
*
83-
* @param extractAstNodeFn method that extracts the AST node; should also initialize the workspace correctly.
86+
* @param extractAstNodeFn method that extracts the AST node
8487
* @returns the parsed Jayvee model, or undefined on failure.
8588
*/
8689
parseModel(
@@ -94,6 +97,8 @@ export interface JayveeInterpreter {
9497
export class DefaultJayveeInterpreter implements JayveeInterpreter {
9598
private readonly services: JayveeServices;
9699
private readonly loggerFactory: LoggerFactory;
100+
private readonly workspaces: WorkspaceFolder[] = [];
101+
private isWorkspaceInitialized = false;
97102

98103
constructor(private readonly options: InterpreterOptions) {
99104
this.services = createJayveeServices(NodeFileSystem).Jayvee;
@@ -102,7 +107,18 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
102107
this.loggerFactory = new LoggerFactory(options.debug);
103108
}
104109

110+
addWorkspace(uri: string): DefaultJayveeInterpreter {
111+
this.isWorkspaceInitialized = false;
112+
this.workspaces.push({
113+
name: 'projectRoot',
114+
uri: path.resolve(uri),
115+
});
116+
return this;
117+
}
118+
105119
async interpretModel(model: JayveeModel): Promise<ExitCode> {
120+
await this.prepareInterpretation();
121+
106122
const interpretationExitCode = await this.interpretJayveeModel(
107123
model,
108124
new StdExecExtension(),
@@ -112,6 +128,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
112128
}
113129

114130
async interpretFile(filePath: string): Promise<ExitCode> {
131+
await this.prepareInterpretation();
132+
115133
const extractAstNodeFn = async (
116134
services: JayveeServices,
117135
loggerFactory: LoggerFactory,
@@ -131,6 +149,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
131149
}
132150

133151
async interpretString(modelString: string): Promise<ExitCode> {
152+
await this.prepareInterpretation();
153+
134154
const extractAstNodeFn = async (
135155
services: JayveeServices,
136156
loggerFactory: LoggerFactory,
@@ -155,6 +175,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
155175
loggerFactory: LoggerFactory,
156176
) => Promise<JayveeModel>,
157177
): Promise<JayveeModel | undefined> {
178+
await this.prepareInterpretation();
179+
158180
try {
159181
const model = await extractAstNodeFn(this.services, this.loggerFactory);
160182
return model;
@@ -274,6 +296,13 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
274296
logExecutionDuration(startTime, executionContext.logger);
275297
return ExitCode.SUCCESS;
276298
}
299+
300+
private async prepareInterpretation(): Promise<void> {
301+
if (!this.isWorkspaceInitialized) {
302+
await initializeWorkspace(this.services, this.workspaces);
303+
this.isWorkspaceInitialized = true;
304+
}
305+
}
277306
}
278307

279308
export function logPipelineOverview(

libs/interpreter-lib/src/parsing-util.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ export async function extractDocumentFromFile(
4141
return Promise.reject(ExitCode.FAILURE);
4242
}
4343

44-
const workingDirPath = path.dirname(filePath);
45-
46-
await initializeWorkspace(services, [
47-
{
48-
name: 'projectRoot',
49-
uri: path.resolve(workingDirPath),
50-
},
51-
]);
52-
5344
const document = services.shared.workspace.LangiumDocuments.getDocument(
5445
URI.file(path.resolve(filePath)),
5546
);

0 commit comments

Comments
 (0)