@iamsergio/qttest-utils 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,4 +4,14 @@ A nodejs module for listing Qt Test executables and their individual test slots
4
4
 
5
5
  To be used by vscode extensions that implement the `Testing API`, but can also be used standalone for whatever reason ;).
6
6
 
7
- For example usage `src/example.ts`.
7
+
8
+ ## Example
9
+
10
+ ```
11
+ $ cd test/qt_test
12
+ $ cmake --preset=dev
13
+ $ cmake --build build-dev/
14
+ $ cd ../..
15
+ $ tsc
16
+ $ node out/example.js test/qt_test/build-dev
17
+ ```
package/a ADDED
@@ -0,0 +1,8 @@
1
+ const pattern = /at:\s+(.+?)::(.+?)\s+\((.+?):(\d+)\)/gm;
2
+ const matches = Array.from(data.matchAll(pattern));
3
+ const failingTests = matches.map(match => ({
4
+ name: match[2],
5
+ filePath: match[3],
6
+ lineNumber: parseInt(match[4]),
7
+ }));
8
+ resolve(failingTests);
package/foo ADDED
@@ -0,0 +1,15 @@
1
+ TAP version 13
2
+ # MyTest
3
+ ok 1 - initTestCase()
4
+ not ok 2 - testF()
5
+ ---
6
+ # failed
7
+ at: MyTest::testF() (/data/sources/vscode-test-extension/qttest-utils/test/qt_test/test2.cpp:13)
8
+ file: /data/sources/vscode-test-extension/qttest-utils/test/qt_test/test2.cpp
9
+ line: 13
10
+ ...
11
+ ok 3 - cleanupTestCase()
12
+ 1..3
13
+ # tests 3
14
+ # pass 2
15
+ # fail 1
package/out/example.js CHANGED
@@ -30,16 +30,46 @@ function example() {
30
30
  process.exit(1);
31
31
  }
32
32
  let qt = new qttest_1.QtTests();
33
+ qt.setLogFunction((message) => {
34
+ console.log(message);
35
+ });
33
36
  // Gather all tests that would be executed by CTest:
34
37
  yield qt.discoverViaCMake(buildDirPath);
35
38
  // Filter-out the ones that don't link to QtTest (doctests and such)
36
39
  yield qt.removeNonLinking();
37
40
  // Example of filtering out by regexp:
38
41
  qt.removeMatching(/(tst_view|tst_window)/);
39
- // Example of filtering out by regexp (inverted):
40
- qt.maintainMatching(/(tst_docks|tst_qtwidgets|tst_multisplitter)/);
42
+ // Uncomment to see example of filtering out by regexp (inverted):
43
+ // qt.maintainMatching(/(tst_docks|tst_qtwidgets|tst_multisplitter)/);
41
44
  qt.dumpExecutablePaths();
42
- qt.dumpTestSlots();
45
+ yield qt.dumpTestSlots();
46
+ console.log("\nRunning tests...");
47
+ for (var executable of qt.qtTestExecutables) {
48
+ yield executable.runTest();
49
+ if (executable.lastExitCode === 0)
50
+ console.log(" PASS: " + executable.filename);
51
+ else
52
+ console.log(" FAIL: " + executable.filename + "; code=" + executable.lastExitCode);
53
+ for (let slot of executable.slots) {
54
+ if (slot.lastTestFailure) {
55
+ console.log(" failed slot=" + slot.name + "; path=" + slot.lastTestFailure.filePath + "; line=" + slot.lastTestFailure.lineNumber);
56
+ }
57
+ }
58
+ }
59
+ // Also run individual slots, just for example purposes:
60
+ console.log("\nRunning single tests...");
61
+ let slot = qt.qtTestExecutables[1].slots[0];
62
+ yield slot.runTest();
63
+ if (slot.lastTestFailure)
64
+ console.log(" FAIL:" + JSON.stringify(slot.lastTestFailure));
65
+ else
66
+ console.log(" PASS:");
67
+ let slot2 = qt.qtTestExecutables[1].slots[2];
68
+ yield slot2.runTest();
69
+ if (slot2.lastTestFailure)
70
+ console.log(" FAIL:" + JSON.stringify(slot2.lastTestFailure));
71
+ else
72
+ console.log(" PASS");
43
73
  });
44
74
  }
45
75
  example();
package/out/qttest.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ type LoggerFunction = (arg: string) => void;
1
2
  /**
2
3
  * Represents a single QtTest executable.
3
4
  * Supports listing the individual test slots
@@ -7,6 +8,7 @@ export declare class QtTest {
7
8
  readonly buildDirPath: string;
8
9
  vscodeTestItem: any | undefined;
9
10
  slots: QtTestSlot[] | null;
11
+ lastExitCode: number;
10
12
  constructor(filename: string, buildDirPath: string);
11
13
  get id(): string;
12
14
  get label(): string;
@@ -23,12 +25,17 @@ export declare class QtTest {
23
25
  */
24
26
  linksToQtTestLib(): Promise<boolean> | undefined;
25
27
  isQtTestViaHelp(): Promise<boolean | undefined>;
26
- runTest(slotName?: string, cwd?: string): Promise<boolean>;
28
+ slotByName(name: string): QtTestSlot | undefined;
29
+ runTest(slot?: QtTestSlot, cwd?: string): Promise<boolean>;
30
+ tapOutputFileName(slot?: QtTestSlot): string;
31
+ txtOutputFileName(slot?: QtTestSlot): string;
27
32
  command(): {
28
33
  label: string;
29
34
  executablePath: string;
30
35
  args: string[];
31
36
  };
37
+ clearSubTestStates(): void;
38
+ updateSubTestStates(cwdDir: string, slot?: QtTestSlot): Promise<void>;
32
39
  }
33
40
  /**
34
41
  * Represents a single Qt test slot
@@ -37,6 +44,7 @@ export declare class QtTestSlot {
37
44
  name: string;
38
45
  parentQTest: QtTest;
39
46
  vscodeTestItem: any | undefined;
47
+ lastTestFailure: TestFailure | undefined;
40
48
  constructor(name: string, parent: QtTest);
41
49
  get id(): string;
42
50
  get absoluteFilePath(): string;
@@ -54,9 +62,16 @@ export declare class QtTests {
54
62
  qtTestExecutables: QtTest[];
55
63
  discoverViaCMake(buildDirPath: string): Promise<void>;
56
64
  removeNonLinking(): Promise<void>;
65
+ setLogFunction(func: LoggerFunction): void;
57
66
  removeByRunningHelp(): Promise<void>;
58
67
  removeMatching(regex: RegExp): void;
59
68
  maintainMatching(regex: RegExp): void;
60
69
  dumpExecutablePaths(): void;
61
70
  dumpTestSlots(): Promise<void>;
62
71
  }
72
+ export interface TestFailure {
73
+ name: string;
74
+ filePath: string;
75
+ lineNumber: number;
76
+ }
77
+ export {};
package/out/qttest.js CHANGED
@@ -43,13 +43,22 @@ const child_process_1 = require("child_process");
43
43
  const path_1 = __importDefault(require("path"));
44
44
  const fs = __importStar(require("fs"));
45
45
  const cmake_1 = require("./cmake");
46
+ var gLogFunction;
47
+ function logMessage(message) {
48
+ if (gLogFunction) {
49
+ gLogFunction(message);
50
+ }
51
+ }
46
52
  /**
47
53
  * Represents a single QtTest executable.
48
54
  * Supports listing the individual test slots
49
55
  */
50
56
  class QtTest {
51
57
  constructor(filename, buildDirPath) {
58
+ /// The list of individual runnable test slots
52
59
  this.slots = null;
60
+ /// Set after running
61
+ this.lastExitCode = 0;
53
62
  this.filename = filename;
54
63
  this.buildDirPath = buildDirPath;
55
64
  }
@@ -159,34 +168,111 @@ class QtTest {
159
168
  });
160
169
  });
161
170
  }
171
+ slotByName(name) {
172
+ if (!this.slots)
173
+ return undefined;
174
+ for (let slot of this.slots) {
175
+ if (slot.name == name)
176
+ return slot;
177
+ }
178
+ return undefined;
179
+ }
162
180
  /// Runs this test
163
- runTest(slotName, cwd = "") {
181
+ runTest(slot, cwd = "") {
164
182
  return __awaiter(this, void 0, void 0, function* () {
165
183
  let args = [];
166
- if (slotName) {
184
+ if (slot) {
167
185
  // Runs a single Qt test instead
168
- args = args.concat(slotName);
186
+ args = args.concat(slot.name);
187
+ }
188
+ else {
189
+ this.clearSubTestStates();
169
190
  }
191
+ // log to file
192
+ args = args.concat("-o").concat(this.tapOutputFileName(slot) + ",tap");
193
+ args = args.concat("-o").concat(this.txtOutputFileName(slot) + ",txt");
170
194
  return yield new Promise((resolve, reject) => {
171
- let opts = cwd.length > 0 ? { cwd: cwd } : { cwd: this.buildDirPath };
172
- const child = (0, child_process_1.spawn)(this.filename, args, opts);
173
- child.stdout.on("data", (chunk) => {
174
- // chunk.toString()
175
- });
176
- child.on("exit", (code) => {
195
+ let cwdDir = cwd.length > 0 ? cwd : this.buildDirPath;
196
+ logMessage("Running " + this.filename + " " + args.join(" ") + " with cwd=" + cwdDir);
197
+ const child = (0, child_process_1.spawn)(this.filename, args, { cwd: cwdDir });
198
+ child.on("exit", (code) => __awaiter(this, void 0, void 0, function* () {
199
+ /// We can code even be null ?
200
+ if (code == undefined)
201
+ code = -1;
202
+ if (!slot) {
203
+ this.lastExitCode = code;
204
+ }
205
+ /// When running a QtTest executable, let's check which sub-tests failed
206
+ /// (So VSCode can show some error icon for each fail)
207
+ yield this.updateSubTestStates(cwdDir, slot);
177
208
  if (code === 0) {
178
209
  resolve(true);
179
210
  }
180
211
  else {
181
212
  resolve(false);
182
213
  }
183
- });
214
+ }));
184
215
  });
185
216
  });
186
217
  }
218
+ /// Using .tap so we don't have to use a separate XML library
219
+ /// .tap is plain text and a single regexp can catch the failing tests and line number
220
+ tapOutputFileName(slot) {
221
+ let slotName = slot ? ("_" + slot.name) : "";
222
+ return this.label + slotName + ".tap";
223
+ }
224
+ txtOutputFileName(slot) {
225
+ let slotName = slot ? ("_" + slot.name) : "";
226
+ return this.label + slotName + ".txt";
227
+ }
187
228
  command() {
188
229
  return { label: this.label, executablePath: this.filename, args: [] };
189
230
  }
231
+ clearSubTestStates() {
232
+ if (this.slots) {
233
+ for (let slot of this.slots) {
234
+ slot.lastTestFailure = undefined;
235
+ }
236
+ }
237
+ }
238
+ updateSubTestStates(cwdDir, slot) {
239
+ return __awaiter(this, void 0, void 0, function* () {
240
+ let tapFileName = cwdDir + "/" + this.tapOutputFileName(slot);
241
+ var failures = yield new Promise((resolve, reject) => {
242
+ fs.readFile(tapFileName, "utf8", (error, data) => {
243
+ if (error) {
244
+ logMessage("ERROR: Failed to read log file");
245
+ reject(error);
246
+ }
247
+ else {
248
+ // A fail line is something like:
249
+ // at: MyTest::testF() (/some/path/qttest-utils/test/qt_test/test2.cpp:13)
250
+ const pattern = /at:\s+(.+?)::(.+?)\(\)\s+\((.+?):(\d+)\)/gm;
251
+ const matches = Array.from(data.matchAll(pattern));
252
+ const failedResults = matches.map(match => ({
253
+ name: match[2],
254
+ filePath: match[3],
255
+ lineNumber: parseInt(match[4]),
256
+ }));
257
+ resolve(failedResults);
258
+ }
259
+ });
260
+ });
261
+ for (let failure of failures) {
262
+ if (slot && slot.name != failure.name) {
263
+ // We executed a single slot, ignore anything else
264
+ continue;
265
+ }
266
+ let failedSlot = this.slotByName(failure.name);
267
+ if (failedSlot) {
268
+ failedSlot.lastTestFailure = failure;
269
+ }
270
+ else {
271
+ logMessage("ERROR: Failed to find slot with name " + failure.name);
272
+ }
273
+ }
274
+ });
275
+ }
190
276
  }
191
277
  exports.QtTest = QtTest;
192
278
  /**
@@ -205,7 +291,7 @@ class QtTestSlot {
205
291
  }
206
292
  runTest() {
207
293
  return __awaiter(this, void 0, void 0, function* () {
208
- return this.parentQTest.runTest(this.name);
294
+ return this.parentQTest.runTest(this);
209
295
  });
210
296
  }
211
297
  command() {
@@ -231,7 +317,7 @@ class QtTests {
231
317
  }
232
318
  }
233
319
  else {
234
- console.error("Failed to retrieve ctests!");
320
+ logMessage("ERROR: Failed to retrieve ctests!");
235
321
  }
236
322
  });
237
323
  }
@@ -255,6 +341,9 @@ class QtTests {
255
341
  }
256
342
  });
257
343
  }
344
+ setLogFunction(func) {
345
+ gLogFunction = func;
346
+ }
258
347
  removeByRunningHelp() {
259
348
  return __awaiter(this, void 0, void 0, function* () {
260
349
  this.qtTestExecutables = this.qtTestExecutables.filter((ex) => __awaiter(this, void 0, void 0, function* () { return yield ex.isQtTestViaHelp(); }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iamsergio/qttest-utils",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "API for listing QtTest executables from a build directory and which individual test slots each executable contains. Useful for a Text Explorer VSCode extension.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/example.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import { CMakeTests, CMakeTest } from "./cmake";
6
6
  import { QtTest, QtTests } from "./qttest";
7
7
  import fs from 'fs';
8
+ import { exec } from "child_process";
8
9
 
9
10
  async function example() {
10
11
  const args = process.argv.slice(2)
@@ -22,6 +23,10 @@ async function example() {
22
23
 
23
24
  let qt = new QtTests();
24
25
 
26
+ qt.setLogFunction((message: string) => {
27
+ console.log(message);
28
+ });
29
+
25
30
  // Gather all tests that would be executed by CTest:
26
31
  await qt.discoverViaCMake(buildDirPath);
27
32
 
@@ -31,11 +36,43 @@ async function example() {
31
36
  // Example of filtering out by regexp:
32
37
  qt.removeMatching(/(tst_view|tst_window)/);
33
38
 
34
- // Example of filtering out by regexp (inverted):
35
- qt.maintainMatching(/(tst_docks|tst_qtwidgets|tst_multisplitter)/);
39
+ // Uncomment to see example of filtering out by regexp (inverted):
40
+ // qt.maintainMatching(/(tst_docks|tst_qtwidgets|tst_multisplitter)/);
36
41
 
37
42
  qt.dumpExecutablePaths();
38
- qt.dumpTestSlots();
43
+ await qt.dumpTestSlots();
44
+
45
+ console.log("\nRunning tests...");
46
+ for (var executable of qt.qtTestExecutables) {
47
+ await executable.runTest();
48
+ if (executable.lastExitCode === 0)
49
+ console.log(" PASS: " + executable.filename);
50
+ else
51
+ console.log(" FAIL: " + executable.filename + "; code=" + executable.lastExitCode);
52
+ for (let slot of executable.slots!) {
53
+ if (slot.lastTestFailure) {
54
+ console.log(" failed slot=" + slot.name + "; path=" + slot.lastTestFailure.filePath + "; line=" + slot.lastTestFailure.lineNumber);
55
+ }
56
+ }
57
+ }
58
+
59
+ // Also run individual slots, just for example purposes:
60
+
61
+ console.log("\nRunning single tests...");
62
+ let slot = qt.qtTestExecutables[1].slots![0];
63
+ await slot.runTest();
64
+ if (slot.lastTestFailure)
65
+ console.log(" FAIL:" + JSON.stringify(slot.lastTestFailure));
66
+ else
67
+ console.log(" PASS:");
68
+
69
+ let slot2 = qt.qtTestExecutables[1].slots![2];
70
+ await slot2.runTest();
71
+ if (slot2.lastTestFailure)
72
+ console.log(" FAIL:" + JSON.stringify(slot2.lastTestFailure));
73
+ else
74
+ console.log(" PASS");
75
+
39
76
  }
40
77
 
41
78
  example();
package/src/qttest.ts CHANGED
@@ -7,6 +7,16 @@ import path from "path";
7
7
  import * as fs from 'fs';
8
8
  import { CMakeTests } from "./cmake";
9
9
 
10
+
11
+ type LoggerFunction = (arg: string) => void;
12
+ var gLogFunction: LoggerFunction | undefined;
13
+
14
+ function logMessage(message: string) {
15
+ if (gLogFunction) {
16
+ gLogFunction(message);
17
+ }
18
+ }
19
+
10
20
  /**
11
21
  * Represents a single QtTest executable.
12
22
  * Supports listing the individual test slots
@@ -18,8 +28,12 @@ export class QtTest {
18
28
  /// Allows vscode extensions to associate with a test item
19
29
  vscodeTestItem: any | undefined;
20
30
 
31
+ /// The list of individual runnable test slots
21
32
  slots: QtTestSlot[] | null = null;
22
33
 
34
+ /// Set after running
35
+ lastExitCode: number = 0;
36
+
23
37
  constructor(filename: string, buildDirPath: string) {
24
38
  this.filename = filename;
25
39
  this.buildDirPath = buildDirPath;
@@ -138,22 +152,50 @@ export class QtTest {
138
152
  });
139
153
  }
140
154
 
155
+ public slotByName(name: string): QtTestSlot | undefined {
156
+ if (!this.slots)
157
+ return undefined;
158
+
159
+ for (let slot of this.slots) {
160
+ if (slot.name == name)
161
+ return slot;
162
+ }
163
+
164
+ return undefined;
165
+ }
166
+
141
167
  /// Runs this test
142
- public async runTest(slotName?: string, cwd: string = ""): Promise<boolean> {
168
+ public async runTest(slot?: QtTestSlot, cwd: string = ""): Promise<boolean> {
143
169
  let args: string[] = [];
144
- if (slotName) {
170
+ if (slot) {
145
171
  // Runs a single Qt test instead
146
- args = args.concat(slotName);
172
+ args = args.concat(slot.name);
173
+ } else {
174
+ this.clearSubTestStates();
147
175
  }
148
176
 
177
+ // log to file
178
+ args = args.concat("-o").concat(this.tapOutputFileName(slot) + ",tap");
179
+ args = args.concat("-o").concat(this.txtOutputFileName(slot) + ",txt");
180
+
149
181
  return await new Promise((resolve, reject) => {
150
- let opts = cwd.length > 0 ? { cwd: cwd } : { cwd: this.buildDirPath };
151
- const child = spawn(this.filename, args, opts);
152
- child.stdout.on("data", (chunk) => {
153
- // chunk.toString()
154
- });
182
+ let cwdDir = cwd.length > 0 ? cwd : this.buildDirPath;
183
+ logMessage("Running " + this.filename + " " + args.join(" ") + " with cwd=" + cwdDir);
184
+ const child = spawn(this.filename, args, { cwd: cwdDir });
185
+
186
+ child.on("exit", async (code) => {
187
+
188
+ /// We can code even be null ?
189
+ if (code == undefined) code = -1;
190
+
191
+ if (!slot) {
192
+ this.lastExitCode = code;
193
+ }
194
+
195
+ /// When running a QtTest executable, let's check which sub-tests failed
196
+ /// (So VSCode can show some error icon for each fail)
197
+ await this.updateSubTestStates(cwdDir, slot);
155
198
 
156
- child.on("exit", (code) => {
157
199
  if (code === 0) {
158
200
  resolve(true);
159
201
  } else {
@@ -163,9 +205,71 @@ export class QtTest {
163
205
  });
164
206
  }
165
207
 
208
+ /// Using .tap so we don't have to use a separate XML library
209
+ /// .tap is plain text and a single regexp can catch the failing tests and line number
210
+ public tapOutputFileName(slot?: QtTestSlot): string {
211
+ let slotName = slot ? ("_" + slot.name) : "";
212
+ return this.label + slotName + ".tap";
213
+ }
214
+
215
+ public txtOutputFileName(slot?: QtTestSlot): string {
216
+ let slotName = slot ? ("_" + slot.name) : "";
217
+ return this.label + slotName + ".txt";
218
+ }
219
+
166
220
  public command(): { label: string, executablePath: string, args: string[] } {
167
221
  return { label: this.label, executablePath: this.filename, args: [] };
168
222
  }
223
+
224
+
225
+ public clearSubTestStates() {
226
+ if (this.slots) {
227
+ for (let slot of this.slots) {
228
+ slot.lastTestFailure = undefined;
229
+ }
230
+ }
231
+ }
232
+
233
+ public async updateSubTestStates(cwdDir: string, slot?: QtTestSlot) {
234
+
235
+ let tapFileName: string = cwdDir + "/" + this.tapOutputFileName(slot);
236
+
237
+ var failures = await new Promise<TestFailure[]>((resolve, reject) => {
238
+ fs.readFile(tapFileName, "utf8", (error, data) => {
239
+ if (error) {
240
+ logMessage("ERROR: Failed to read log file");
241
+ reject(error);
242
+ } else {
243
+ // A fail line is something like:
244
+ // at: MyTest::testF() (/some/path/qttest-utils/test/qt_test/test2.cpp:13)
245
+
246
+ const pattern = /at:\s+(.+?)::(.+?)\(\)\s+\((.+?):(\d+)\)/gm;
247
+ const matches = Array.from(data.matchAll(pattern));
248
+ const failedResults = matches.map(match => ({
249
+ name: match[2],
250
+ filePath: match[3],
251
+ lineNumber: parseInt(match[4]),
252
+ }));
253
+
254
+ resolve(failedResults);
255
+ }
256
+ });
257
+ });
258
+
259
+ for (let failure of failures) {
260
+ if (slot && slot.name != failure.name) {
261
+ // We executed a single slot, ignore anything else
262
+ continue;
263
+ }
264
+
265
+ let failedSlot = this.slotByName(failure.name);
266
+ if (failedSlot) {
267
+ failedSlot.lastTestFailure = failure;
268
+ } else {
269
+ logMessage("ERROR: Failed to find slot with name " + failure.name);
270
+ }
271
+ }
272
+ }
169
273
  }
170
274
 
171
275
  /**
@@ -180,6 +284,9 @@ export class QtTestSlot {
180
284
  /// Allows vscode extensions to associate with a test item
181
285
  vscodeTestItem: any | undefined;
182
286
 
287
+ /// Set after running
288
+ lastTestFailure: TestFailure | undefined;
289
+
183
290
  constructor(name: string, parent: QtTest) {
184
291
  this.name = name;
185
292
  this.parentQTest = parent;
@@ -194,7 +301,7 @@ export class QtTestSlot {
194
301
  }
195
302
 
196
303
  public async runTest(): Promise<boolean> {
197
- return this.parentQTest.runTest(this.name);
304
+ return this.parentQTest.runTest(this);
198
305
  }
199
306
 
200
307
  public command(): { label: string, executablePath: string, args: string[] } {
@@ -217,7 +324,7 @@ export class QtTests {
217
324
  this.qtTestExecutables.push(qtest);
218
325
  }
219
326
  } else {
220
- console.error("Failed to retrieve ctests!");
327
+ logMessage("ERROR: Failed to retrieve ctests!");
221
328
  }
222
329
  }
223
330
 
@@ -239,6 +346,10 @@ export class QtTests {
239
346
  }
240
347
  }
241
348
 
349
+ public setLogFunction(func: LoggerFunction) {
350
+ gLogFunction = func;
351
+ }
352
+
242
353
  public async removeByRunningHelp() {
243
354
  this.qtTestExecutables = this.qtTestExecutables.filter(async (ex) => await ex.isQtTestViaHelp());
244
355
  }
@@ -273,3 +384,10 @@ export class QtTests {
273
384
  }
274
385
  }
275
386
  }
387
+
388
+ /// Represents a failure location
389
+ export interface TestFailure {
390
+ name: string;
391
+ filePath: string;
392
+ lineNumber: number;
393
+ }
@@ -0,0 +1,22 @@
1
+ # SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2
+ # Author: Sergio Martins <sergio.martins@kdab.com>
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ cmake_minimum_required(VERSION 3.12)
6
+ project(qttest)
7
+
8
+ find_package(Qt5Test 5.15 REQUIRED)
9
+ set(CMAKE_AUTOMOC ON)
10
+
11
+ add_executable(test1 test1.cpp)
12
+ add_executable(test2 test2.cpp)
13
+ add_executable(test3 test3.cpp)
14
+
15
+ target_link_libraries(test1 Qt5::Test)
16
+ target_link_libraries(test2 Qt5::Test)
17
+ target_link_libraries(test3 Qt5::Test)
18
+
19
+ enable_testing()
20
+ add_test(NAME test1 COMMAND test1)
21
+ add_test(NAME test2 COMMAND test2)
22
+ add_test(NAME test3 COMMAND test3)
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 2,
3
+ "configurePresets": [
4
+ {
5
+ "name": "dev",
6
+ "displayName": "dev",
7
+ "generator": "Ninja",
8
+ "binaryDir": "${sourceDir}/build-dev",
9
+ "cacheVariables": {
10
+ "CMAKE_BUILD_TYPE": "Debug",
11
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
12
+ }
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,18 @@
1
+ // SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group
2
+ // company <info@kdab.com> Author: Sergio Martins <sergio.martins@kdab.com>
3
+ // SPDX-License-Identifier: MIT
4
+
5
+ #include <QObject>
6
+ #include <QtTest>
7
+
8
+ class MyTest : public QObject {
9
+ Q_OBJECT
10
+ private Q_SLOTS:
11
+ void testA() {}
12
+ void testB() {}
13
+ void testC() {}
14
+ };
15
+
16
+ QTEST_MAIN(MyTest);
17
+
18
+ #include <test1.moc>
@@ -0,0 +1,18 @@
1
+ // SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group
2
+ // company <info@kdab.com> Author: Sergio Martins <sergio.martins@kdab.com>
3
+ // SPDX-License-Identifier: MIT
4
+
5
+ #include <QObject>
6
+ #include <QtTest>
7
+
8
+ class MyTest : public QObject {
9
+ Q_OBJECT
10
+ private Q_SLOTS:
11
+ void testD() {}
12
+ void testE() {}
13
+ void testF() { QFAIL("failed"); }
14
+ };
15
+
16
+ QTEST_MAIN(MyTest);
17
+
18
+ #include <test2.moc>
@@ -0,0 +1,18 @@
1
+ // SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group
2
+ // company <info@kdab.com> Author: Sergio Martins <sergio.martins@kdab.com>
3
+ // SPDX-License-Identifier: MIT
4
+
5
+ #include <QObject>
6
+ #include <QtTest>
7
+
8
+ class MyTest : public QObject {
9
+ Q_OBJECT
10
+ private Q_SLOTS:
11
+ void testG() {}
12
+ void testH() {}
13
+ void testI() {}
14
+ };
15
+
16
+ QTEST_MAIN(MyTest);
17
+
18
+ #include <test3.moc>
package/test1.log ADDED
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <TestCase name="MyTest">
3
+ <Environment>
4
+ <QtVersion>5.15.2</QtVersion>
5
+ <QtBuild>Qt 5.15.2 (x86_64&#x002D;little_endian&#x002D;lp64 shared (dynamic) release build; by GCC 5.3.1 20160406 (Red Hat 5.3.1&#x002D;6))</QtBuild>
6
+ <QTestVersion>5.15.2</QTestVersion>
7
+ </Environment>
8
+ <TestFunction name="initTestCase">
9
+ <Incident type="pass" file="" line="0" />
10
+ <Duration msecs="0.312962"/>
11
+ </TestFunction>
12
+ <TestFunction name="testD">
13
+ <Incident type="pass" file="" line="0" />
14
+ <Duration msecs="0.010127"/>
15
+ </TestFunction>
16
+ <TestFunction name="testE">
17
+ <Incident type="pass" file="" line="0" />
18
+ <Duration msecs="0.005239"/>
19
+ </TestFunction>
20
+ <TestFunction name="testF">
21
+ <Incident type="fail" file="/data/sources/vscode&#x002D;test&#x002D;extension/qttest&#x002D;utils/test/qt_test/test2.cpp" line="13">
22
+ <Description><![CDATA[failed]]></Description>
23
+ </Incident>
24
+ <Duration msecs="0.018438"/>
25
+ </TestFunction>
26
+ <TestFunction name="cleanupTestCase">
27
+ <Incident type="pass" file="" line="0" />
28
+ <Duration msecs="0.00426"/>
29
+ </TestFunction>
30
+ <Duration msecs="0.432951"/>
31
+ </TestCase>
package/test1.log1 ADDED
@@ -0,0 +1,17 @@
1
+ TAP version 13
2
+ # MyTest
3
+ ok 1 - initTestCase()
4
+ ok 2 - testD()
5
+ ok 3 - testE()
6
+ not ok 4 - testF()
7
+ ---
8
+ # failed
9
+ at: MyTest::testF() (/data/sources/vscode-test-extension/qttest-utils/test/qt_test/test2.cpp:13)
10
+ file: /data/sources/vscode-test-extension/qttest-utils/test/qt_test/test2.cpp
11
+ line: 13
12
+ ...
13
+ ok 5 - cleanupTestCase()
14
+ 1..5
15
+ # tests 5
16
+ # pass 4
17
+ # fail 1