Skip to content

Commit

Permalink
Merge pull request #432 from jvalue/feature/test-logger-cache
Browse files Browse the repository at this point in the history
Test-logger extension
  • Loading branch information
f3l1x98 authored Sep 11, 2023
2 parents 42cfefd + d072c1d commit 8e84faf
Show file tree
Hide file tree
Showing 14 changed files with 105 additions and 170 deletions.
5 changes: 5 additions & 0 deletions apps/docs/docs/dev/12-jayvee-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ At the moment this only contains two functions:
- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry.
They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)).

[**test-logger.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/test-logger.ts):
This contains a subclass of the [`DefaultLogger`](https://github.com/jvalue/jayvee/blob/dev/libs/execution/src/lib/logging/default-logger.ts) used for tests which require a `Logger` implementation. The `TestLogger` contains the following tests functionality:
- `getLogs`: retrieve the cached logs that the logger received.
- `clearLogs`: clear the cached logs.

[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts):
`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls).
Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations.
Expand Down
2 changes: 1 addition & 1 deletion libs/execution/src/lib/blocks/block-execution-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@jvalue/jayvee-language-server';

import { ExecutionContext } from '../execution-context';
import { Logger } from '../logger';
import { Logger } from '../logging/logger';
import { IOTypeImplementation, NONE } from '../types';

// eslint-disable-next-line import/no-cycle
Expand Down
2 changes: 1 addition & 1 deletion libs/execution/src/lib/debugging/debug-log-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { internalValueToString } from '@jvalue/jayvee-language-server';

import { Logger } from '../logger';
import { Logger } from '../logging/logger';
import { Workbook } from '../types';
import { FileSystem } from '../types/io-types/filesystem';
import { BinaryFile } from '../types/io-types/filesystem-node-file-binary';
Expand Down
2 changes: 1 addition & 1 deletion libs/execution/src/lib/execution-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
DebugGranularity,
DebugTargets,
} from './debugging/debug-configuration';
import { Logger } from './logger';
import { Logger } from './logging/logger';

export type StackNode =
| BlockDefinition
Expand Down
2 changes: 1 addition & 1 deletion libs/execution/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export * from './types/valuetypes/visitors';

export * from './execution-context';
export * from './extension';
export * from './logger';
export * from './logging';
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
//
// SPDX-License-Identifier: AGPL-3.0-only

import { DiagnosticSeverity, Logger } from '@jvalue/jayvee-execution';
import * as chalk from 'chalk';
import { LangiumDocument } from 'langium';
import { assertUnreachable } from 'langium/lib/utils/errors';
import { Range } from 'vscode-languageserver';
import { uinteger } from 'vscode-languageserver-types';

import { DiagnosticSeverity, Logger } from './logger';

export class DefaultLogger extends Logger {
private readonly TAB_TO_SPACES = 4;

constructor(
private readonly enableDebugLogging: boolean,
private loggingContext?: string,
private depth: number = 0,
protected readonly enableDebugLogging: boolean,
protected loggingContext?: string,
protected depth: number = 0,
) {
super();
}
Expand Down Expand Up @@ -54,7 +55,7 @@ export class DefaultLogger extends Logger {
return '\t'.repeat(this.depth);
}

private getContext(): string {
protected getContext(): string {
return this.loggingContext !== undefined
? chalk.grey(`[${this.loggingContext}] `)
: '';
Expand All @@ -74,7 +75,7 @@ export class DefaultLogger extends Logger {
printFn('');
}

private logDiagnosticMessage(
protected logDiagnosticMessage(
severityName: string,
message: string,
printFn: (message: string) => void,
Expand All @@ -85,7 +86,7 @@ export class DefaultLogger extends Logger {
);
}

private logDiagnosticInfo(
protected logDiagnosticInfo(
range: Range,
document: LangiumDocument,
printFn: (message: string) => void,
Expand Down Expand Up @@ -171,7 +172,7 @@ export class DefaultLogger extends Logger {
}, '');
}

private inferPrintFunction(
protected inferPrintFunction(
severity: DiagnosticSeverity,
): (message: string) => void {
switch (severity) {
Expand All @@ -187,7 +188,7 @@ export class DefaultLogger extends Logger {
}
}

private inferChalkColor(
protected inferChalkColor(
severity: DiagnosticSeverity,
): (message: string) => string {
switch (severity) {
Expand Down
6 changes: 6 additions & 0 deletions libs/execution/src/lib/logging/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg
//
// SPDX-License-Identifier: AGPL-3.0-only

export * from './logger';
export * from './default-logger';
File renamed without changes.
1 change: 1 addition & 0 deletions libs/execution/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

export * from './block-executor-mock';
export * from './utils';
export * from './test-logger';
226 changes: 75 additions & 151 deletions libs/execution/test/test-logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,92 @@

import * as chalk from 'chalk';
import { LangiumDocument } from 'langium';
import { assertUnreachable } from 'langium/lib/utils/errors';
import { Range } from 'vscode-languageserver';
import { uinteger } from 'vscode-languageserver-types';

import { DiagnosticSeverity, Logger } from '../src/lib';
import { DefaultLogger, DiagnosticSeverity } from '../src/lib';

export class TestLogger extends Logger {
private readonly TAB_TO_SPACES = 4;
export interface ClearLogsOptions {
clearInfo: boolean;
clearError: boolean;
clearDebug: boolean;
clearDiagnostic: boolean;
}

export class TestLogger extends DefaultLogger {
private infoLogs: string[] = [];
private errorLogs: string[] = [];
private debugLogs: string[] = [];
private diagnosticLogs: string[] = [];

constructor(
private readonly enableDebugLogging: boolean,
private loggingContext?: string,
enableDebugLogging: boolean,
loggingContext?: string,
private printLogs: boolean = true,
depth = 0,
) {
super();
super(enableDebugLogging, loggingContext, depth);
}

public getLogs(): {
infoLogs: string[];
errorLogs: string[];
debugLogs: string[];
diagnosticLogs: string[];
} {
return {
infoLogs: Array.from(this.infoLogs),
errorLogs: Array.from(this.errorLogs),
debugLogs: Array.from(this.debugLogs),
diagnosticLogs: Array.from(this.diagnosticLogs),
};
}

override setLoggingDepth(): void {
return undefined;
public clearLogs(
options: ClearLogsOptions = {
clearInfo: true,
clearDebug: true,
clearError: true,
clearDiagnostic: true,
},
): void {
if (options.clearInfo) {
this.infoLogs = [];
}
if (options.clearError) {
this.errorLogs = [];
}
if (options.clearDebug) {
this.debugLogs = [];
}
if (options.clearDiagnostic) {
this.diagnosticLogs = [];
}
}

override logInfo(message: string): void {
console.log(`${chalk.bold(this.getContext())}${message}`);
const msg = `${chalk.bold(this.getContext())}${message}`;
this.infoLogs.push(msg);
if (this.printLogs) {
console.log(msg);
}
}

override logDebug(message: string): void {
if (this.enableDebugLogging) {
console.log(`${chalk.bold(this.getContext())}${message}`);
const msg = `${chalk.bold(this.getContext())}${message}`;
this.debugLogs.push(msg);
if (this.printLogs) {
console.log(msg);
}
}
}

override logErr(message: string): void {
console.error(`${chalk.bold(this.getContext())}${chalk.red(message)}`);
}

override setLoggingContext(loggingContext: string | undefined) {
this.loggingContext = loggingContext;
}

private getContext(): string {
return this.loggingContext !== undefined
? chalk.grey(`[${this.loggingContext}] `)
: '';
const msg = `${chalk.bold(this.getContext())}${chalk.red(message)}`;
this.errorLogs.push(msg);
if (this.printLogs) {
console.error(msg);
}
}

protected override logDiagnostic(
Expand All @@ -54,138 +98,18 @@ export class TestLogger extends Logger {
range: Range,
document: LangiumDocument,
) {
const printFn = this.inferPrintFunction(severity);
const printFn = (msg: string) => {
const basePrintFn = this.inferPrintFunction(severity);

this.diagnosticLogs.push(msg);
if (this.printLogs) {
basePrintFn(msg);
}
};
const colorFn = this.inferChalkColor(severity);

this.logDiagnosticMessage(severity, message, printFn, colorFn);
this.logDiagnosticInfo(range, document, printFn, colorFn);
printFn('');
}

private logDiagnosticMessage(
severityName: string,
message: string,
printFn: (message: string) => void,
colorFn: (message: string) => string,
) {
printFn(`${chalk.bold(colorFn(severityName))}: ${message}`);
}

private logDiagnosticInfo(
range: Range,
document: LangiumDocument,
printFn: (message: string) => void,
colorFn: (message: string) => string,
): void {
const startLineNumber = range.start.line + 1;
const endLineNumber = range.end.line + 1;

const fullRange: Range = {
start: {
line: range.start.line,
character: 0,
},
end: {
line: range.end.line,
character: uinteger.MAX_VALUE,
},
};
const text = document.textDocument.getText(fullRange).trimEnd();
const lines = text.split('\n');

const lineNumberLength = Math.floor(Math.log10(endLineNumber)) + 1;

printFn(
`In ${document.uri.path}:${startLineNumber}:${range.start.character + 1}`,
);
lines.forEach((line, i) => {
const lineNumber = startLineNumber + i;
const paddedLineNumber = String(lineNumber).padStart(
lineNumberLength,
' ',
);
printFn(
`${chalk.grey(`${paddedLineNumber} |`)} ${line.replace(
/\t/g,
' '.repeat(this.TAB_TO_SPACES),
)}`,
);

let underlineFrom = 0;
let underlineTo = line.length;
if (lineNumber === startLineNumber) {
underlineFrom = range.start.character;
}
if (lineNumber === endLineNumber) {
underlineTo = range.end.character;
}

const underlineIndent = this.repeatCharAccordingToString(
' ',
line.substring(0, underlineFrom),
this.TAB_TO_SPACES,
);
const underline = this.repeatCharAccordingToString(
'^',
line.substring(underlineFrom, underlineTo),
this.TAB_TO_SPACES,
);

printFn(
`${chalk.grey(
`${' '.repeat(lineNumberLength)} |`,
)} ${underlineIndent}${colorFn(underline)}`,
);
});
}

/**
* Repeats {@link charToRepeat} as many times as {@link accordingTo} is long.
* For each occurrence of \t in {@link accordingTo},
* {@link charToRepeat} is repeated {@link tabRepeats} times instead of once.
*/
private repeatCharAccordingToString(
charToRepeat: string,
accordingTo: string,
tabRepeats: number,
): string {
return Array.from(accordingTo).reduce((prev, cur) => {
const repeatedChar =
cur === '\t' ? charToRepeat.repeat(tabRepeats) : charToRepeat;
return `${prev}${repeatedChar}`;
}, '');
}

private inferPrintFunction(
severity: DiagnosticSeverity,
): (message: string) => void {
switch (severity) {
case DiagnosticSeverity.ERROR:
return console.error;
case DiagnosticSeverity.WARNING:
return console.warn;
case DiagnosticSeverity.INFO:
case DiagnosticSeverity.HINT:
return console.info;
default:
assertUnreachable(severity);
}
}

private inferChalkColor(
severity: DiagnosticSeverity,
): (message: string) => string {
switch (severity) {
case DiagnosticSeverity.ERROR:
return chalk.red;
case DiagnosticSeverity.WARNING:
return chalk.yellow;
case DiagnosticSeverity.INFO:
return chalk.green;
case DiagnosticSeverity.HINT:
return chalk.blue;
default:
assertUnreachable(severity);
}
}
}
Loading

0 comments on commit 8e84faf

Please sign in to comment.