diff options
author | Ryan Cumming <[email protected]> | 2019-06-26 11:14:18 +0100 |
---|---|---|
committer | Ryan Cumming <[email protected]> | 2019-06-26 11:31:36 +0100 |
commit | f82ceca0bd8de2a2b0b51c96c5c1678351a7a20a (patch) | |
tree | 89e70d9965f973615dc0dfe978db002d82bf7e25 /editors/code/src/test/rust_diagnostics.test.ts | |
parent | afd18dbcb8147cb83de408b7da310ee187faf3df (diff) |
Initial Visual Studio Code unit tests
As promised in #1439 this is an initial attempt at unit testing the
VSCode extension. There are two separate parts to this: getting the test
framework working and unit testing the code in #1439.
The test framework nearly intact from the VSCode extension generator.
The main thing missing was `test/index.ts` which acts as an entry point
for Mocha. This was simply copied back in. I also needed to open the
test VSCode instance inside a workspace as our file URI generation
depends on a workspace being open.
There are two ways to run the test framework:
1. Opening the extension's source in VSCode, pressing F5 and selecting
the "Extensions Test" debug target.
2. Closing all copies of VSCode and running `npm test`. This is started
from the command line but actually opens a temporary VSCode window to
host the tests.
This doesn't attempt to wire this up to CI. That requires running a
headless X11 server which is a bit daunting. I'll assess the difficulty
of that in a follow-up branch. This PR is at least helpful for local
development without having to induce errors on a Rust project.
For the actual tests this uses snapshots of `rustc` output from a real
Rust project captured from the command line. Except for extracting the
`message` object and reformatting they're copied verbatim into fixture
JSON files.
Only four different types of diagnostics are tested but they represent
the main combinations of code actions and related information possible.
They can be considered the happy path tests; as we encounter
corner-cases we can introduce new tests fixtures.
Diffstat (limited to 'editors/code/src/test/rust_diagnostics.test.ts')
-rw-r--r-- | editors/code/src/test/rust_diagnostics.test.ts | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/editors/code/src/test/rust_diagnostics.test.ts b/editors/code/src/test/rust_diagnostics.test.ts new file mode 100644 index 000000000..5eb064b97 --- /dev/null +++ b/editors/code/src/test/rust_diagnostics.test.ts | |||
@@ -0,0 +1,161 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as fs from 'fs'; | ||
3 | import * as vscode from 'vscode'; | ||
4 | |||
5 | import { | ||
6 | MappedRustDiagnostic, | ||
7 | mapRustDiagnosticToVsCode, | ||
8 | RustDiagnostic | ||
9 | } from '../utils/rust_diagnostics'; | ||
10 | |||
11 | function loadDiagnosticFixture(name: string): RustDiagnostic { | ||
12 | const jsonText = fs | ||
13 | .readFileSync( | ||
14 | // We're actually in our JavaScript output directory, climb out | ||
15 | `${__dirname}/../../src/test/fixtures/rust-diagnostics/${name}.json` | ||
16 | ) | ||
17 | .toString(); | ||
18 | |||
19 | return JSON.parse(jsonText); | ||
20 | } | ||
21 | |||
22 | function mapFixtureToVsCode(name: string): MappedRustDiagnostic { | ||
23 | const rd = loadDiagnosticFixture(name); | ||
24 | const mapResult = mapRustDiagnosticToVsCode(rd); | ||
25 | |||
26 | if (!mapResult) { | ||
27 | return assert.fail('Mapping unexpectedly failed'); | ||
28 | } | ||
29 | return mapResult; | ||
30 | } | ||
31 | |||
32 | describe('mapRustDiagnosticToVsCode', () => { | ||
33 | it('should map an incompatible type for trait error', () => { | ||
34 | const { diagnostic, codeActions } = mapFixtureToVsCode('error/E0053'); | ||
35 | |||
36 | assert.strictEqual( | ||
37 | diagnostic.severity, | ||
38 | vscode.DiagnosticSeverity.Error | ||
39 | ); | ||
40 | assert.strictEqual( | ||
41 | diagnostic.message, | ||
42 | [ | ||
43 | `method \`next\` has an incompatible type for trait`, | ||
44 | `expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>\``, | ||
45 | ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>\`` | ||
46 | ].join('\n') | ||
47 | ); | ||
48 | assert.strictEqual(diagnostic.code, 'E0053'); | ||
49 | assert.strictEqual(diagnostic.tags, undefined); | ||
50 | |||
51 | // No related information | ||
52 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
53 | |||
54 | // There are no code actions available | ||
55 | assert.strictEqual(codeActions.length, 0); | ||
56 | }); | ||
57 | |||
58 | it('should map an unused variable warning', () => { | ||
59 | const { diagnostic, codeActions } = mapFixtureToVsCode( | ||
60 | 'warning/unused_variables' | ||
61 | ); | ||
62 | |||
63 | assert.strictEqual( | ||
64 | diagnostic.severity, | ||
65 | vscode.DiagnosticSeverity.Warning | ||
66 | ); | ||
67 | assert.strictEqual( | ||
68 | diagnostic.message, | ||
69 | [ | ||
70 | 'unused variable: `foo`', | ||
71 | '#[warn(unused_variables)] on by default' | ||
72 | ].join('\n') | ||
73 | ); | ||
74 | assert.strictEqual(diagnostic.code, 'unused_variables'); | ||
75 | assert.deepStrictEqual(diagnostic.tags, [ | ||
76 | vscode.DiagnosticTag.Unnecessary | ||
77 | ]); | ||
78 | |||
79 | // No related information | ||
80 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
81 | |||
82 | // One code action available to prefix the variable | ||
83 | assert.strictEqual(codeActions.length, 1); | ||
84 | const [codeAction] = codeActions; | ||
85 | assert.strictEqual( | ||
86 | codeAction.title, | ||
87 | 'consider prefixing with an underscore: `_foo`' | ||
88 | ); | ||
89 | assert(codeAction.isPreferred); | ||
90 | }); | ||
91 | |||
92 | it('should map a wrong number of parameters error', () => { | ||
93 | const { diagnostic, codeActions } = mapFixtureToVsCode('error/E0061'); | ||
94 | |||
95 | assert.strictEqual( | ||
96 | diagnostic.severity, | ||
97 | vscode.DiagnosticSeverity.Error | ||
98 | ); | ||
99 | assert.strictEqual( | ||
100 | diagnostic.message, | ||
101 | 'this function takes 2 parameters but 3 parameters were supplied' | ||
102 | ); | ||
103 | assert.strictEqual(diagnostic.code, 'E0061'); | ||
104 | assert.strictEqual(diagnostic.tags, undefined); | ||
105 | |||
106 | // One related information for the original definition | ||
107 | const relatedInformation = diagnostic.relatedInformation; | ||
108 | if (!relatedInformation) { | ||
109 | return assert.fail('Related information unexpectedly undefined'); | ||
110 | } | ||
111 | assert.strictEqual(relatedInformation.length, 1); | ||
112 | const [related] = relatedInformation; | ||
113 | assert.strictEqual(related.message, 'defined here'); | ||
114 | |||
115 | // There are no actions available | ||
116 | assert.strictEqual(codeActions.length, 0); | ||
117 | }); | ||
118 | |||
119 | it('should map a Clippy copy pass by ref warning', () => { | ||
120 | const { diagnostic, codeActions } = mapFixtureToVsCode( | ||
121 | 'clippy/trivially_copy_pass_by_ref' | ||
122 | ); | ||
123 | |||
124 | assert.strictEqual( | ||
125 | diagnostic.severity, | ||
126 | vscode.DiagnosticSeverity.Warning | ||
127 | ); | ||
128 | assert.strictEqual( | ||
129 | diagnostic.message, | ||
130 | [ | ||
131 | 'this argument is passed by reference, but would be more efficient if passed by value', | ||
132 | '#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]', | ||
133 | 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref' | ||
134 | ].join('\n') | ||
135 | ); | ||
136 | assert.strictEqual( | ||
137 | diagnostic.code, | ||
138 | 'clippy::trivially_copy_pass_by_ref' | ||
139 | ); | ||
140 | assert.strictEqual(diagnostic.tags, undefined); | ||
141 | |||
142 | // One related information for the lint definition | ||
143 | const relatedInformation = diagnostic.relatedInformation; | ||
144 | if (!relatedInformation) { | ||
145 | return assert.fail('Related information unexpectedly undefined'); | ||
146 | } | ||
147 | assert.strictEqual(relatedInformation.length, 1); | ||
148 | const [related] = relatedInformation; | ||
149 | assert.strictEqual(related.message, 'lint level defined here'); | ||
150 | |||
151 | // One code action available to pass by value | ||
152 | assert.strictEqual(codeActions.length, 1); | ||
153 | const [codeAction] = codeActions; | ||
154 | assert.strictEqual( | ||
155 | codeAction.title, | ||
156 | 'consider passing by value instead: `self`' | ||
157 | ); | ||
158 | // Clippy does not mark this as machine applicable | ||
159 | assert.strictEqual(codeAction.isPreferred, false); | ||
160 | }); | ||
161 | }); | ||