Skip to content

Commit 4b376ed

Browse files
committed
Add more structured output for tests
The diff and the errors were always available, but they were not being sent to the output. Additionally, make sure to send output to both the test explorer log and the codeql test log.
1 parent ea3db03 commit 4b376ed

5 files changed

Lines changed: 131 additions & 5 deletions

File tree

extensions/ql-vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- Clear the problems view of all CodeQL query results when a database is removed.
1515
- Add a `View DIL` command on query history items. This opens a text editor containing the Datalog Intermediary Language representation of the compiled query.
1616
- Alter structure of the _Test Explorer_ tree. It now follows the structure of the filesystem instead of the qlpacks.
17+
- Ensure output of CodeQL test runs includes compilation error messages and test failure messages.
1718

1819
## 1.3.3 - 16 September 2020
1920

extensions/ql-vscode/src/cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DistributionProvider } from './distribution';
1414
import { assertNever } from './helpers-pure';
1515
import { QueryMetadata, SortDirection } from './interface-types';
1616
import { Logger, ProgressReporter } from './logging';
17+
import { CompilationMessage } from './messages';
1718

1819
/**
1920
* The version of the SARIF format that we are using.
@@ -87,10 +88,11 @@ export interface TestRunOptions {
8788
export interface TestCompleted {
8889
test: string;
8990
pass: boolean;
90-
messages: string[];
91+
messages: CompilationMessage[];
9192
compilationMs: number;
9293
evaluationMs: number;
9394
expected: string;
95+
diff: string[] | undefined;
9496
}
9597

9698
/**

extensions/ql-vscode/src/test-adapter.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,26 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
221221
cancellationToken: cancellationToken,
222222
logger: testLogger
223223
})) {
224+
const state = event.pass
225+
? 'passed'
226+
: event.messages?.length
227+
? 'errored'
228+
: 'failed';
229+
let message: string | undefined;
230+
if (event.diff?.length) {
231+
message = ['', `${state}: ${event.test}`, ...event.diff, ''].join('\n');
232+
testLogger.log(message);
233+
}
234+
(event.diff || []).join('\n');
224235
this._testStates.fire({
225236
type: 'test',
226-
state: event.pass ? 'passed' : 'failed',
227-
test: event.test
237+
state,
238+
test: event.test,
239+
message,
240+
decorations: event.messages?.map(msg => ({
241+
line: msg.position.line,
242+
message: msg.message
243+
}))
228244
});
229245
}
230246
}

extensions/ql-vscode/src/vscode-tests/no-workspace/databases.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ describe('databases', () => {
2424
databaseManager = new DatabaseManager(
2525
{
2626
workspaceState: {
27-
update: updateSpy
28-
}
27+
update: updateSpy,
28+
get: sinon.stub()
29+
},
2930
} as unknown as ExtensionContext,
3031
{} as QueryServerConfig,
3132
{} as Logger,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import 'vscode-test';
2+
import 'mocha';
3+
import * as sinon from 'sinon';
4+
import { Uri, WorkspaceFolder } from 'vscode';
5+
import { expect } from 'chai';
6+
7+
import { QLTestAdapter } from '../../test-adapter';
8+
import { CodeQLCliServer } from '../../cli';
9+
10+
describe('test-adapter', () => {
11+
let adapter: QLTestAdapter;
12+
let runTestsSpy: sinon.SinonStub;
13+
let resolveTestsSpy: sinon.SinonStub;
14+
let resolveQlpacksSpy: sinon.SinonStub;
15+
let sandox: sinon.SinonSandbox;
16+
17+
beforeEach(() => {
18+
sandox = sinon.createSandbox();
19+
mockRunTests();
20+
resolveQlpacksSpy = sandox.stub().resolves({});
21+
resolveTestsSpy = sandox.stub().resolves([]);
22+
adapter = new QLTestAdapter({
23+
name: 'ABC',
24+
uri: Uri.parse('file:/ab/c')
25+
} as WorkspaceFolder, {
26+
runTests: runTestsSpy,
27+
resolveQlpacks: resolveQlpacksSpy,
28+
resolveTests: resolveTestsSpy
29+
} as unknown as CodeQLCliServer);
30+
});
31+
32+
afterEach(() => {
33+
sandox.restore();
34+
});
35+
36+
it('should run some tests', async () => {
37+
38+
const listenerSpy = sandox.spy();
39+
adapter.testStates(listenerSpy);
40+
const testsPath = Uri.parse('file:/ab/c').fsPath;
41+
const dPath = Uri.parse('file:/ab/c/d.ql').fsPath;
42+
const gPath = Uri.parse('file:/ab/c/e/f/g.ql').fsPath;
43+
const hPath = Uri.parse('file:/ab/c/e/f/h.ql').fsPath;
44+
45+
await adapter.run([testsPath]);
46+
47+
expect(listenerSpy.getCall(0).args).to.deep.eq([
48+
{ type: 'started', tests: [testsPath] }
49+
]);
50+
expect(listenerSpy.getCall(1).args).to.deep.eq([{
51+
type: 'test',
52+
state: 'passed',
53+
test: dPath,
54+
message: undefined,
55+
decorations: []
56+
}]);
57+
expect(listenerSpy.getCall(2).args).to.deep.eq([{
58+
type: 'test',
59+
state: 'errored',
60+
test: gPath,
61+
message: `\nerrored: ${gPath}\npqr\nxyz\n`,
62+
decorations: [
63+
{ line: 1, message: 'abc' }
64+
]
65+
}]);
66+
expect(listenerSpy.getCall(3).args).to.deep.eq([{
67+
type: 'test',
68+
state: 'failed',
69+
test: hPath,
70+
message: `\nfailed: ${hPath}\njkh\ntuv\n`,
71+
decorations: []
72+
}]);
73+
expect(listenerSpy.getCall(4).args).to.deep.eq([{ type: 'finished' }]);
74+
expect(listenerSpy).to.have.callCount(5);
75+
});
76+
77+
function mockRunTests() {
78+
// runTests is an async generator function. This is not directly supported in sinon
79+
// However, we can pretend the same thing by just returning an async array.
80+
runTestsSpy = sandox.stub();
81+
runTestsSpy.returns(
82+
(async function*() {
83+
yield Promise.resolve({
84+
test: Uri.parse('file:/ab/c/d.ql').fsPath,
85+
pass: true,
86+
messages: []
87+
});
88+
yield Promise.resolve({
89+
test: Uri.parse('file:/ab/c/e/f/g.ql').fsPath,
90+
pass: false,
91+
diff: ['pqr', 'xyz'],
92+
// a compile error
93+
messages: [
94+
{ position: { line: 1 }, message: 'abc' }
95+
]
96+
});
97+
yield Promise.resolve({
98+
test: Uri.parse('file:/ab/c/e/f/h.ql').fsPath,
99+
pass: false,
100+
diff: ['jkh', 'tuv'],
101+
messages: []
102+
});
103+
})()
104+
);
105+
}
106+
});

0 commit comments

Comments
 (0)