Skip to content

Commit

Permalink
Merge pull request #600 from jvalue/590-current-dir-as-workdir
Browse files Browse the repository at this point in the history
Configure interpreter to use current directory as workdir
  • Loading branch information
georg-schwarz authored Jul 15, 2024
2 parents 1a1e9bb + bde524a commit 7ab2eb3
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 35 deletions.
11 changes: 11 additions & 0 deletions apps/interpreter/src/current-dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg
//
// SPDX-License-Identifier: AGPL-3.0-only

/**
* Gets the current directory path.
* We use this method for emulating tests in other directory to configure the correct working directory.
*/
export function getCurrentDir(): string {
return process.cwd();
}
36 changes: 21 additions & 15 deletions apps/interpreter/src/examples-smoke-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
// SPDX-License-Identifier: AGPL-3.0-only

import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { processExitMockImplementation } from '@jvalue/jayvee-execution/test';
import {
PostgresLoaderExecutorMock,
SQLiteLoaderExecutorMock,
} from '@jvalue/jayvee-extensions/rdbms/test';
import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test';
import {
createJayveeServices,
initializeWorkspace,
} from '@jvalue/jayvee-language-server';
import { NodeFileSystem } from 'langium/node';
import nock from 'nock';
import { type MockInstance, vi } from 'vitest';

Expand Down Expand Up @@ -44,9 +40,22 @@ vi.mock('sqlite3', () => {
};
});

describe('jv example smoke tests', () => {
const baseDir = path.resolve(__dirname, '../../../example/');
// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(
path.dirname(fileURLToPath(import.meta.url)), // relative to this test file
'..',
'..',
'..',
'example',
);
return {
getCurrentDir: currentDirMock,
};
});

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

beforeAll(async () => {
const services = createJayveeServices(NodeFileSystem).Jayvee;
await initializeWorkspace(services);

beforeAll(() => {
exitSpy = vi
.spyOn(process, 'exit')
.mockImplementation(processExitMockImplementation);
Expand Down Expand Up @@ -99,7 +105,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'cars.jv'), {
await runAction('cars.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -131,7 +137,7 @@ describe('jv example smoke tests', () => {
postgresLoaderMock.setup();
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'electric-vehicles.jv'), {
await runAction('electric-vehicles.jv', {
...defaultOptions,
env: new Map<string, string>([
['DB_HOST', 'mock'],
Expand Down Expand Up @@ -194,7 +200,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'gtfs-rt.jv'), {
await runAction('gtfs-rt.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -222,7 +228,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'gtfs-static.jv'), {
await runAction('gtfs-static.jv', {
...defaultOptions,
});

Expand Down
36 changes: 29 additions & 7 deletions apps/interpreter/src/parse-only.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';

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

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

vi.stubGlobal('DefaultJayveeInterpreter', interpreterMock);

const dirPathOfThisTest = path.dirname(fileURLToPath(import.meta.url));
const pathExamplesRelativeToThisTest = path.join('..', '..', '..', 'example');

// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(dirPathOfThisTest, pathExamplesRelativeToThisTest);
return {
getCurrentDir: currentDirMock,
};
});

describe('Parse Only', () => {
const pathToValidModel = path.resolve(__dirname, '../../../example/cars.jv');
const pathToInvalidModel = path.resolve(
__dirname,
'../test/assets/broken-model.jv',
const pathToValidModelFromExamplesDir = 'cars.jv';
const pathToInvalidModelFromExamplesDir = path.join(
'..',
'apps',
'interpreter',
'test',
'assets',
'broken-model.jv',
);

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

it('should exit with 0 on a valid option', async () => {
await expect(
runAction(pathToValidModel, {
runAction(pathToValidModelFromExamplesDir, {
...defaultOptions,
parseOnly: true,
}),
Expand All @@ -62,10 +79,15 @@ describe('Parse Only', () => {
});

it('should exit with 1 on error', async () => {
expect(fs.existsSync(pathToInvalidModel)).toBe(true);
const modelPathRelativeToThisTest = path.join(
dirPathOfThisTest,
pathExamplesRelativeToThisTest,
pathToInvalidModelFromExamplesDir,
);
expect(fs.existsSync(modelPathRelativeToThisTest)).toBe(true);

await expect(
runAction(pathToInvalidModel, {
runAction(pathToInvalidModelFromExamplesDir, {
...defaultOptions,
parseOnly: true,
}),
Expand Down
12 changes: 9 additions & 3 deletions apps/interpreter/src/run-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only

import path from 'node:path';
import process from 'node:process';

import {
Expand All @@ -16,6 +17,7 @@ import {
type JayveeServices,
} from '@jvalue/jayvee-language-server';

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

export async function runAction(
Expand All @@ -33,20 +35,24 @@ export async function runAction(
return process.exit(ExitCode.FAILURE);
}

const currentDir = getCurrentDir();
const workingDir = currentDir;
const absoluteFilePath = path.join(currentDir, filePath);

const interpreter = new DefaultJayveeInterpreter({
pipelineMatcher: (pipelineDefinition) =>
pipelineRegExp.test(pipelineDefinition.name),
env: options.env,
debug: options.debug,
debugGranularity: options.debugGranularity,
debugTarget: options.debugTarget,
});
}).addWorkspace(workingDir);

if (options.parseOnly === true) {
return await runParseOnly(filePath, interpreter);
return await runParseOnly(absoluteFilePath, interpreter);
}

const exitCode = await interpreter.interpretFile(filePath);
const exitCode = await interpreter.interpretFile(absoluteFilePath);
process.exit(exitCode);
}

Expand Down
31 changes: 30 additions & 1 deletion libs/interpreter-lib/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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

import {
type DebugGranularity,
Expand All @@ -28,9 +29,11 @@ import {
type RuntimeParameterProvider,
type WrapperFactoryProvider,
createJayveeServices,
initializeWorkspace,
internalValueToString,
} from '@jvalue/jayvee-language-server';
import chalk from 'chalk';
import { type WorkspaceFolder } from 'langium';
import { NodeFileSystem } from 'langium/node';

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

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

addWorkspace(uri: string): DefaultJayveeInterpreter {
this.isWorkspaceInitialized = false;
this.workspaces.push({
name: 'projectRoot',
uri: path.resolve(uri),
});
return this;
}

async interpretModel(model: JayveeModel): Promise<ExitCode> {
await this.prepareInterpretation();

const interpretationExitCode = await this.interpretJayveeModel(
model,
new StdExecExtension(),
Expand All @@ -112,6 +128,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
}

async interpretFile(filePath: string): Promise<ExitCode> {
await this.prepareInterpretation();

const extractAstNodeFn = async (
services: JayveeServices,
loggerFactory: LoggerFactory,
Expand All @@ -131,6 +149,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
}

async interpretString(modelString: string): Promise<ExitCode> {
await this.prepareInterpretation();

const extractAstNodeFn = async (
services: JayveeServices,
loggerFactory: LoggerFactory,
Expand All @@ -155,6 +175,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
loggerFactory: LoggerFactory,
) => Promise<JayveeModel>,
): Promise<JayveeModel | undefined> {
await this.prepareInterpretation();

try {
const model = await extractAstNodeFn(this.services, this.loggerFactory);
return model;
Expand Down Expand Up @@ -274,6 +296,13 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
logExecutionDuration(startTime, executionContext.logger);
return ExitCode.SUCCESS;
}

private async prepareInterpretation(): Promise<void> {
if (!this.isWorkspaceInitialized) {
await initializeWorkspace(this.services, this.workspaces);
this.isWorkspaceInitialized = true;
}
}
}

export function logPipelineOverview(
Expand Down
9 changes: 0 additions & 9 deletions libs/interpreter-lib/src/parsing-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,6 @@ export async function extractDocumentFromFile(
return Promise.reject(ExitCode.FAILURE);
}

const workingDirPath = path.dirname(filePath);

await initializeWorkspace(services, [
{
name: 'projectRoot',
uri: path.resolve(workingDirPath),
},
]);

const document = services.shared.workspace.LangiumDocuments.getDocument(
URI.file(path.resolve(filePath)),
);
Expand Down

0 comments on commit 7ab2eb3

Please sign in to comment.