Skip to content

Commit 5007d09

Browse files
ericsnowcurrentlyDonJayamanne
authored andcommitted
gh-2696: Add a unit test for the MyPy output regex. (#2764)
for #2696
1 parent 55410f6 commit 5007d09

File tree

5 files changed

+81
-21
lines changed

5 files changed

+81
-21
lines changed

news/2 Fixes/2696.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a unit test for the MyPy output regex.

src/client/linters/baseLinter.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { IPythonToolExecutionService } from '../common/process/types';
66
import { ExecutionInfo, IConfigurationService, ILogger, IPythonSettings, Product } from '../common/types';
77
import { IServiceContainer } from '../ioc/types';
88
import { ErrorHandler } from './errorHandlers/errorHandler';
9-
import { ILinter, ILinterInfo, ILinterManager, ILintMessage, LintMessageSeverity } from './types';
9+
import {
10+
ILinter, ILinterInfo, ILinterManager, ILintMessage,
11+
LinterId, LintMessageSeverity
12+
} from './types';
1013

1114
// tslint:disable-next-line:no-require-imports no-var-requires
1215
const namedRegexp = require('named-js-regexp');
@@ -31,6 +34,32 @@ export function matchNamedRegEx(data, regex): IRegexGroup | undefined {
3134
return undefined;
3235
}
3336

37+
export function parseLine(
38+
line: string,
39+
regex: string,
40+
linterID: LinterId,
41+
colOffset: number = 0
42+
): ILintMessage | undefined {
43+
const match = matchNamedRegEx(line, regex)!;
44+
if (!match) {
45+
return;
46+
}
47+
48+
// tslint:disable-next-line:no-any
49+
match.line = Number(<any>match.line);
50+
// tslint:disable-next-line:no-any
51+
match.column = Number(<any>match.column);
52+
53+
return {
54+
code: match.code,
55+
message: match.message,
56+
column: isNaN(match.column) || match.column <= 0 ? 0 : match.column - colOffset,
57+
line: match.line,
58+
type: match.type,
59+
provider: linterID
60+
};
61+
}
62+
3463
export abstract class BaseLinter implements ILinter {
3564
protected readonly configService: IConfigurationService;
3665

@@ -128,24 +157,7 @@ export abstract class BaseLinter implements ILinter {
128157
}
129158

130159
private parseLine(line: string, regEx: string): ILintMessage | undefined {
131-
const match = matchNamedRegEx(line, regEx)!;
132-
if (!match) {
133-
return;
134-
}
135-
136-
// tslint:disable-next-line:no-any
137-
match.line = Number(<any>match.line);
138-
// tslint:disable-next-line:no-any
139-
match.column = Number(<any>match.column);
140-
141-
return {
142-
code: match.code,
143-
message: match.message,
144-
column: isNaN(match.column) || match.column <= 0 ? 0 : match.column - this.columnOffset,
145-
line: match.line,
146-
type: match.type,
147-
provider: this.info.id
148-
};
160+
return parseLine(line, regEx, this.info.id, this.columnOffset);
149161
}
150162

151163
private parseLines(outputLines: string[], regEx: string): ILintMessage[] {

src/client/linters/mypy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { IServiceContainer } from '../ioc/types';
55
import { BaseLinter } from './baseLinter';
66
import { ILintMessage } from './types';
77

8-
const REGEX = '(?<file>.+):(?<line>\\d+): (?<type>\\w+): (?<message>.*)\\r?(\\n|$)';
8+
export const REGEX = '(?<file>.+):(?<line>\\d+): (?<type>\\w+): (?<message>.*)\\r?(\\n|$)';
99

1010
export class MyPy extends BaseLinter {
1111
constructor(outputChannel: OutputChannel, serviceContainer: IServiceContainer) {

src/client/linters/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface ILinterManager {
4545
export interface ILintMessage {
4646
line: number;
4747
column: number;
48-
code: string;
48+
code: string | undefined;
4949
message: string;
5050
type: string;
5151
severity?: LintMessageSeverity;

src/test/linters/mypy.unit.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
// tslint:disable:no-object-literal-type-assertion
7+
8+
import { expect } from 'chai';
9+
import { parseLine } from '../../client/linters/baseLinter';
10+
import { REGEX } from '../../client/linters/mypy';
11+
import { ILintMessage } from '../../client/linters/types';
12+
13+
// This following is a real-world example. See gh=2380.
14+
// tslint:disable-next-line:no-multiline-string
15+
const output = `
16+
provider.pyi:10: error: Incompatible types in assignment (expression has type "str", variable has type "int")
17+
provider.pyi:11: error: Name 'not_declared_var' is not defined
18+
`;
19+
20+
suite('Linting - MyPy', () => {
21+
test('regex', async () => {
22+
const lines = output.split('\n');
23+
const tests: [string, ILintMessage][] = [
24+
[lines[1], {
25+
code: undefined,
26+
message: 'Incompatible types in assignment (expression has type "str", variable has type "int")',
27+
column: 0,
28+
line: 10,
29+
type: 'error',
30+
provider: 'mypy'
31+
} as ILintMessage],
32+
[lines[2], {
33+
code: undefined,
34+
message: 'Name \'not_declared_var\' is not defined',
35+
column: 0,
36+
line: 11,
37+
type: 'error',
38+
provider: 'mypy'
39+
} as ILintMessage]
40+
];
41+
for (const [line, expected] of tests) {
42+
const msg = parseLine(line, REGEX, 'mypy');
43+
44+
expect(msg).to.deep.equal(expected);
45+
}
46+
});
47+
});

0 commit comments

Comments
 (0)