6
6
// tslint:disable:no-object-literal-type-assertion
7
7
8
8
import { expect } from 'chai' ;
9
+ import * as path from 'path' ;
10
+ import * as sinon from 'sinon' ;
11
+ import { anything , instance , mock , when } from 'ts-mockito' ;
12
+ import { CancellationToken , CancellationTokenSource , TextDocument , Uri } from 'vscode' ;
13
+ import { Product } from '../../client/common/types' ;
14
+ import { ServiceContainer } from '../../client/ioc/container' ;
9
15
import { parseLine } from '../../client/linters/baseLinter' ;
10
- import { REGEX } from '../../client/linters/mypy' ;
11
- import { ILintMessage } from '../../client/linters/types' ;
16
+ import { LinterManager } from '../../client/linters/linterManager' ;
17
+ import { MyPy , REGEX } from '../../client/linters/mypy' ;
18
+ import { ILinterManager , ILintMessage , LintMessageSeverity } from '../../client/linters/types' ;
19
+ import { MockOutputChannel } from '../mockClasses' ;
12
20
13
21
// This following is a real-world example. See gh=2380.
14
- // tslint:disable-next-line :no-multiline-string
22
+ // tslint:disable:no-multiline-string no-any max-func-body-length
15
23
const output = `
16
24
provider.pyi:10: error: Incompatible types in assignment (expression has type "str", variable has type "int")
17
25
provider.pyi:11: error: Name 'not_declared_var' is not defined
@@ -29,23 +37,23 @@ suite('Linting - MyPy', () => {
29
37
line : 10 ,
30
38
type : 'error' ,
31
39
provider : 'mypy'
32
- } as ILintMessage ] ,
40
+ } as ILintMessage ] ,
33
41
[ lines [ 2 ] , {
34
42
code : undefined ,
35
43
message : 'Name \'not_declared_var\' is not defined' ,
36
44
column : 0 ,
37
45
line : 11 ,
38
46
type : 'error' ,
39
47
provider : 'mypy'
40
- } as ILintMessage ] ,
48
+ } as ILintMessage ] ,
41
49
[ lines [ 3 ] , {
42
50
code : undefined ,
43
51
message : 'Expression has type "Any"' ,
44
52
column : 21 ,
45
53
line : 12 ,
46
54
type : 'error' ,
47
55
provider : 'mypy'
48
- } as ILintMessage ]
56
+ } as ILintMessage ]
49
57
] ;
50
58
for ( const [ line , expected ] of tests ) {
51
59
const msg = parseLine ( line , REGEX , 'mypy' ) ;
@@ -54,3 +62,72 @@ suite('Linting - MyPy', () => {
54
62
}
55
63
} ) ;
56
64
} ) ;
65
+
66
+ suite ( 'Test Linter' , ( ) => {
67
+ class TestMyPyLinter extends MyPy {
68
+ // tslint:disable: no-unnecessary-override
69
+ public async runLinter ( document : TextDocument , cancellation : CancellationToken ) : Promise < ILintMessage [ ] > {
70
+ return super . runLinter ( document , cancellation ) ;
71
+ }
72
+ public getWorkspaceRootPath ( document : TextDocument ) : string {
73
+ return super . getWorkspaceRootPath ( document ) ;
74
+ }
75
+ public async run ( args : string [ ] , document : TextDocument , cancellation : CancellationToken , regEx : string = REGEX ) : Promise < ILintMessage [ ] > {
76
+ return super . run ( args , document , cancellation , regEx ) ;
77
+ }
78
+ public parseMessagesSeverity ( error : string , severity : any ) : LintMessageSeverity {
79
+ return super . parseMessagesSeverity ( error , severity ) ;
80
+ }
81
+ }
82
+
83
+ let linter : TestMyPyLinter ;
84
+ let getWorkspaceRootPathStub : sinon . SinonStub < [ TextDocument ] , string > ;
85
+ let runStub : sinon . SinonStub < [ string [ ] , TextDocument , CancellationToken , ( string | undefined ) ?] , Promise < ILintMessage [ ] > > ;
86
+ const token = new CancellationTokenSource ( ) . token ;
87
+ teardown ( ( ) => sinon . restore ( ) ) ;
88
+ setup ( ( ) => {
89
+ const linterManager = mock ( LinterManager ) ;
90
+ when ( linterManager . getLinterInfo ( anything ( ) ) ) . thenReturn ( { product : Product . mypy } as any ) ;
91
+ const serviceContainer = mock ( ServiceContainer ) ;
92
+ when ( serviceContainer . get < ILinterManager > ( ILinterManager ) ) . thenReturn ( instance ( linterManager ) ) ;
93
+ getWorkspaceRootPathStub = sinon . stub ( TestMyPyLinter . prototype , 'getWorkspaceRootPath' ) ;
94
+ runStub = sinon . stub ( TestMyPyLinter . prototype , 'run' ) ;
95
+ linter = new TestMyPyLinter ( instance ( mock ( MockOutputChannel ) ) , instance ( serviceContainer ) ) ;
96
+ } ) ;
97
+
98
+ test ( 'Get cwd based on document' , async ( ) => {
99
+ const fileUri = Uri . file ( path . join ( 'a' , 'b' , 'c' , 'd' , 'e' , 'filename.py' ) ) ;
100
+ const cwd = path . join ( 'a' , 'b' , 'c' ) ;
101
+ const doc = { uri : fileUri } as any as TextDocument ;
102
+ getWorkspaceRootPathStub . callsFake ( ( ) => cwd ) ;
103
+ runStub . callsFake ( ( ) => Promise . resolve ( [ ] ) ) ;
104
+
105
+ await linter . runLinter ( doc , token ) ;
106
+
107
+ expect ( getWorkspaceRootPathStub . callCount ) . to . equal ( 1 ) ;
108
+ expect ( getWorkspaceRootPathStub . args [ 0 ] ) . to . deep . equal ( [ doc ] ) ;
109
+ } ) ;
110
+ test ( 'Pass relative path of document to linter' , async ( ) => {
111
+ const fileUri = Uri . file ( path . join ( 'a' , 'b' , 'c' , 'd' , 'e' , 'filename.py' ) ) ;
112
+ const cwd = path . join ( 'a' , 'b' , 'c' ) ;
113
+ const doc = { uri : fileUri } as any as TextDocument ;
114
+ getWorkspaceRootPathStub . callsFake ( ( ) => cwd ) ;
115
+ runStub . callsFake ( ( ) => Promise . resolve ( [ ] ) ) ;
116
+
117
+ await linter . runLinter ( doc , token ) ;
118
+
119
+ expect ( runStub . callCount ) . to . equal ( 1 ) ;
120
+ expect ( runStub . args [ 0 ] ) . to . deep . equal ( [ [ path . relative ( cwd , fileUri . fsPath ) ] , doc , token , REGEX ] ) ;
121
+ } ) ;
122
+ test ( 'Return empty messages' , async ( ) => {
123
+ const fileUri = Uri . file ( path . join ( 'a' , 'b' , 'c' , 'd' , 'e' , 'filename.py' ) ) ;
124
+ const cwd = path . join ( 'a' , 'b' , 'c' ) ;
125
+ const doc = { uri : fileUri } as any as TextDocument ;
126
+ getWorkspaceRootPathStub . callsFake ( ( ) => cwd ) ;
127
+ runStub . callsFake ( ( ) => Promise . resolve ( [ ] ) ) ;
128
+
129
+ const messages = await linter . runLinter ( doc , token ) ;
130
+
131
+ expect ( messages ) . to . be . deep . equal ( [ ] ) ;
132
+ } ) ;
133
+ } ) ;
0 commit comments