@iamsergio/qttest-utils 2.2.2 → 2.5.0

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/out/qttest.js CHANGED
@@ -18,52 +18,58 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
18
18
  }) : function(o, v) {
19
19
  o["default"] = v;
20
20
  });
21
- var __importStar = (this && this.__importStar) || function (mod) {
22
- if (mod && mod.__esModule) return mod;
23
- var result = {};
24
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
25
- __setModuleDefault(result, mod);
26
- return result;
27
- };
28
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
- return new (P || (P = Promise))(function (resolve, reject) {
31
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
32
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
33
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
34
- step((generator = generator.apply(thisArg, _arguments || [])).next());
35
- });
36
- };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
37
38
  var __importDefault = (this && this.__importDefault) || function (mod) {
38
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
39
40
  };
40
41
  Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.QtTests = exports.QtTestSlot = exports.QtTest = exports.logMessage = void 0;
42
+ exports.QtTests = exports.QtTestSlot = exports.QtTest = void 0;
43
+ exports.logMessage = logMessage;
42
44
  const child_process_1 = require("child_process");
43
45
  const path_1 = __importDefault(require("path"));
44
46
  const fs = __importStar(require("fs"));
45
47
  const cmake_1 = require("./cmake");
48
+ const tap_parser_1 = require("tap-parser");
46
49
  var gLogFunction;
47
50
  function logMessage(message) {
48
51
  if (gLogFunction) {
49
52
  gLogFunction(message);
50
53
  }
51
54
  }
52
- exports.logMessage = logMessage;
53
55
  /**
54
56
  * Represents a single QtTest executable.
55
57
  * Supports listing the individual test slots
56
58
  */
57
59
  class QtTest {
60
+ filename;
61
+ buildDirPath;
62
+ /// If true, will print more verbose output
63
+ verbose = false;
64
+ /// Allows vscode extensions to associate with a test item
65
+ vscodeTestItem;
66
+ /// The list of individual runnable test slots
67
+ slots = null;
68
+ /// Set after running
69
+ lastExitCode = 0;
70
+ /// Allows the caller to receive the output of the test process
71
+ outputFunc = undefined;
58
72
  constructor(filename, buildDirPath) {
59
- /// If true, will print more verbose output
60
- this.verbose = false;
61
- /// The list of individual runnable test slots
62
- this.slots = null;
63
- /// Set after running
64
- this.lastExitCode = 0;
65
- /// Allows the caller to receive the output of the test process
66
- this.outputFunc = undefined;
67
73
  this.filename = filename;
68
74
  this.buildDirPath = buildDirPath;
69
75
  }
@@ -92,48 +98,46 @@ class QtTest {
92
98
  /**
93
99
  * Calls "./yourqttest -functions" and stores the results in the slots property.
94
100
  */
95
- parseAvailableSlots() {
96
- return __awaiter(this, void 0, void 0, function* () {
97
- let slotNames = [];
98
- let output = "";
99
- let err = "";
100
- yield new Promise((resolve, reject) => {
101
- if (!fs.existsSync(this.filename)) {
102
- reject(new Error("qttest: File doesn't exit: " + this.filename));
103
- return;
104
- }
105
- const child = (0, child_process_1.spawn)(this.filename, ["-functions"], {
106
- cwd: this.buildDirPath,
107
- });
108
- child.stdout.on("data", (chunk) => {
109
- output += chunk.toString();
110
- });
111
- child.stderr.on("data", (chunk) => {
112
- err += chunk.toString();
113
- });
114
- child.on("exit", (code) => {
115
- if (code === 0) {
116
- slotNames = slotNames.concat(output.split("\n"));
117
- slotNames = slotNames.map((entry) => entry.trim().replace("()", ""));
118
- slotNames = slotNames.filter((entry) => entry.length > 0);
119
- if (slotNames.length > 0) {
120
- this.slots = [];
121
- for (var slotName of slotNames) {
122
- var slot = new QtTestSlot(slotName, this);
123
- this.slots.push(slot);
124
- }
101
+ async parseAvailableSlots() {
102
+ let slotNames = [];
103
+ let output = "";
104
+ let err = "";
105
+ await new Promise((resolve, reject) => {
106
+ if (!fs.existsSync(this.filename)) {
107
+ reject(new Error("qttest: File doesn't exit: " + this.filename));
108
+ return;
109
+ }
110
+ const child = (0, child_process_1.spawn)(this.filename, ["-functions"], {
111
+ cwd: this.buildDirPath,
112
+ });
113
+ child.stdout.on("data", (chunk) => {
114
+ output += chunk.toString();
115
+ });
116
+ child.stderr.on("data", (chunk) => {
117
+ err += chunk.toString();
118
+ });
119
+ child.on("exit", (code) => {
120
+ if (code === 0) {
121
+ slotNames = slotNames.concat(output.split("\n"));
122
+ slotNames = slotNames.map((entry) => entry.trim().replace("()", ""));
123
+ slotNames = slotNames.filter((entry) => entry.length > 0);
124
+ if (slotNames.length > 0) {
125
+ this.slots = [];
126
+ for (var slotName of slotNames) {
127
+ var slot = new QtTestSlot(slotName, this);
128
+ this.slots.push(slot);
125
129
  }
126
- resolve(slotNames);
127
- }
128
- else {
129
- reject(new Error("qttest: Failed to run -functions, stdout=" +
130
- output +
131
- "; stderr=" +
132
- err +
133
- "; code=" +
134
- code));
135
130
  }
136
- });
131
+ resolve(slotNames);
132
+ }
133
+ else {
134
+ reject(new Error("qttest: Failed to run -functions, stdout=" +
135
+ output +
136
+ "; stderr=" +
137
+ err +
138
+ "; code=" +
139
+ code));
140
+ }
137
141
  });
138
142
  });
139
143
  }
@@ -177,27 +181,25 @@ class QtTest {
177
181
  }
178
182
  /// Returns whether this test is a QtTest by running it with -help and checking if the help text looks familiar
179
183
  /// Note that if this is not a QtTest it might not run help and instead execute the test itself
180
- isQtTestViaHelp() {
181
- return __awaiter(this, void 0, void 0, function* () {
182
- return yield new Promise((resolve, reject) => {
183
- const child = (0, child_process_1.spawn)(this.filename, ["-help"]);
184
- let output = "";
185
- let result = false;
186
- child.stdout.on("data", (chunk) => {
187
- if (!result) {
188
- if (chunk.toString().includes("[testfunction[:testdata]]")) {
189
- result = true;
190
- }
191
- }
192
- });
193
- child.on("exit", (code) => {
194
- if (code === 0) {
195
- resolve(result);
196
- }
197
- else {
198
- resolve(false);
184
+ async isQtTestViaHelp() {
185
+ return await new Promise((resolve, reject) => {
186
+ const child = (0, child_process_1.spawn)(this.filename, ["-help"]);
187
+ let output = "";
188
+ let result = false;
189
+ child.stdout.on("data", (chunk) => {
190
+ if (!result) {
191
+ if (chunk.toString().includes("[testfunction[:testdata]]")) {
192
+ result = true;
199
193
  }
200
- });
194
+ }
195
+ });
196
+ child.on("exit", (code) => {
197
+ if (code === 0) {
198
+ resolve(result);
199
+ }
200
+ else {
201
+ resolve(false);
202
+ }
201
203
  });
202
204
  });
203
205
  }
@@ -211,64 +213,62 @@ class QtTest {
211
213
  return undefined;
212
214
  }
213
215
  /// Runs this test
214
- runTest(slot, cwd = "") {
215
- return __awaiter(this, void 0, void 0, function* () {
216
- let args = [];
217
- if (slot) {
218
- // Runs a single Qt test instead
219
- args = args.concat(slot.name);
220
- }
221
- else {
222
- this.clearSubTestStates();
216
+ async runTest(slot, cwd = "") {
217
+ let args = [];
218
+ if (slot) {
219
+ // Runs a single Qt test instead
220
+ args = args.concat(slot.name);
221
+ }
222
+ else {
223
+ this.clearSubTestStates();
224
+ }
225
+ // log to file and to stdout
226
+ args = args.concat("-o").concat(this.tapOutputFileName(slot) + ",tap");
227
+ args = args.concat("-o").concat(this.txtOutputFileName(slot) + ",txt");
228
+ args = args.concat("-o").concat("-,txt");
229
+ return await new Promise((resolve, reject) => {
230
+ let cwdDir = cwd.length > 0 ? cwd : this.buildDirPath;
231
+ logMessage("Running " +
232
+ this.filename +
233
+ " " +
234
+ args.join(" ") +
235
+ " with cwd=" +
236
+ cwdDir);
237
+ const child = (0, child_process_1.spawn)(this.filename, args, { cwd: cwdDir });
238
+ if (this.outputFunc) {
239
+ // Callers wants the process output:
240
+ child.stdout.on("data", (chunk) => {
241
+ if (this.outputFunc)
242
+ this.outputFunc(chunk.toString());
243
+ });
244
+ child.stderr.on("data", (chunk) => {
245
+ if (this.outputFunc)
246
+ this.outputFunc(chunk.toString());
247
+ });
223
248
  }
224
- // log to file and to stdout
225
- args = args.concat("-o").concat(this.tapOutputFileName(slot) + ",tap");
226
- args = args.concat("-o").concat(this.txtOutputFileName(slot) + ",txt");
227
- args = args.concat("-o").concat("-,txt");
228
- return yield new Promise((resolve, reject) => {
229
- let cwdDir = cwd.length > 0 ? cwd : this.buildDirPath;
230
- logMessage("Running " +
231
- this.filename +
232
- " " +
233
- args.join(" ") +
234
- " with cwd=" +
235
- cwdDir);
236
- const child = (0, child_process_1.spawn)(this.filename, args, { cwd: cwdDir });
237
- if (this.outputFunc) {
238
- // Callers wants the process output:
239
- child.stdout.on("data", (chunk) => {
240
- if (this.outputFunc)
241
- this.outputFunc(chunk.toString());
242
- });
243
- child.stderr.on("data", (chunk) => {
244
- if (this.outputFunc)
245
- this.outputFunc(chunk.toString());
246
- });
249
+ child.on("exit", async (code) => {
250
+ /// Can code even be null ?
251
+ if (code == undefined)
252
+ code = -1;
253
+ if (!slot) {
254
+ this.lastExitCode = code;
247
255
  }
248
- child.on("exit", (code) => __awaiter(this, void 0, void 0, function* () {
249
- /// Can code even be null ?
250
- if (code == undefined)
251
- code = -1;
252
- if (!slot) {
253
- this.lastExitCode = code;
254
- }
255
- if (this.slots && this.slots.length > 0) {
256
- /// When running a QtTest executable, let's check which sub-tests failed
257
- /// (So VSCode can show some error icon for each fail)
258
- try {
259
- yield this.updateSubTestStates(cwdDir, slot);
260
- }
261
- catch (e) {
262
- logMessage("Failed to update sub-test states: " + e);
263
- }
264
- }
265
- if (code === 0) {
266
- resolve(true);
256
+ if (this.slots && this.slots.length > 0) {
257
+ /// When running a QtTest executable, let's check which sub-tests failed
258
+ /// (So VSCode can show some error icon for each fail)
259
+ try {
260
+ await this.updateSubTestStates(cwdDir, slot);
267
261
  }
268
- else {
269
- resolve(false);
262
+ catch (e) {
263
+ logMessage("Failed to update sub-test states: " + e);
270
264
  }
271
- }));
265
+ }
266
+ if (code === 0) {
267
+ resolve(true);
268
+ }
269
+ else {
270
+ resolve(false);
271
+ }
272
272
  });
273
273
  });
274
274
  }
@@ -292,43 +292,74 @@ class QtTest {
292
292
  }
293
293
  }
294
294
  }
295
- updateSubTestStates(cwdDir, slot) {
296
- return __awaiter(this, void 0, void 0, function* () {
297
- let tapFileName = cwdDir + "/" + this.tapOutputFileName(slot);
298
- var failures = yield new Promise((resolve, reject) => {
299
- fs.readFile(tapFileName, "utf8", (error, data) => {
300
- if (error) {
301
- logMessage("ERROR: Failed to read log file");
302
- reject(error);
303
- }
304
- else {
305
- // A fail line is something like:
306
- // at: MyTest::testF() (/some/path/qttest-utils/test/qt_test/test2.cpp:13)
307
- const pattern = /at:\s+(.+?)::(.+?)\(\)\s+\((.+?):(\d+)\)/gm;
308
- const matches = Array.from(data.matchAll(pattern));
309
- const failedResults = matches.map((match) => ({
310
- name: match[2],
311
- filePath: match[3],
312
- lineNumber: parseInt(match[4]),
313
- }));
314
- resolve(failedResults);
315
- }
316
- });
317
- });
318
- for (let failure of failures) {
319
- if (slot && slot.name != failure.name) {
320
- // We executed a single slot, ignore anything else
321
- continue;
322
- }
323
- let failedSlot = this.slotByName(failure.name);
324
- if (failedSlot) {
325
- failedSlot.lastTestFailure = failure;
295
+ async updateSubTestStates(cwdDir, slot) {
296
+ let tapFileName = cwdDir + "/" + this.tapOutputFileName(slot);
297
+ var failures = await new Promise((resolve, reject) => {
298
+ fs.readFile(tapFileName, "utf8", (error, data) => {
299
+ if (error) {
300
+ logMessage("ERROR: Failed to read log file");
301
+ reject(error);
326
302
  }
327
303
  else {
328
- logMessage("ERROR: Failed to find slot with name " + failure.name);
304
+ let failedResults = [];
305
+ try {
306
+ const tap_events = tap_parser_1.Parser.parse(data);
307
+ for (let event of tap_events) {
308
+ try {
309
+ if (event.length < 2)
310
+ continue;
311
+ if (event.at(0) != "assert")
312
+ continue;
313
+ var obj = event.at(1);
314
+ let pass = obj["ok"] === true;
315
+ let xfail = !pass && obj["todo"] !== false;
316
+ if (xfail) {
317
+ // This is a QEXPECT_FAIL test, all good.
318
+ // QtTest outputs it as "todo"
319
+ continue;
320
+ }
321
+ // There's an QEXPECT_FAIL but test passed, not good.
322
+ let xpass = pass && obj["todo"].includes("returned TRUE unexpectedly");
323
+ if (!pass || xpass) {
324
+ // We found a failure
325
+ var name = obj["name"].replace(/\(.*\)/, "");
326
+ var filename = "";
327
+ var lineNumber = -1;
328
+ if (obj["diag"]) {
329
+ filename = obj["diag"]["file"];
330
+ lineNumber = obj["diag"]["line"];
331
+ }
332
+ else {
333
+ // A XPASS for example misses file:line info. Nothing we can do, it's a Qt bug arguably.
334
+ }
335
+ failedResults.push({
336
+ name: name,
337
+ filePath: filename,
338
+ lineNumber: lineNumber,
339
+ });
340
+ }
341
+ }
342
+ catch (e) { }
343
+ }
344
+ }
345
+ catch (e) { }
346
+ resolve(failedResults);
329
347
  }
330
- }
348
+ });
331
349
  });
350
+ for (let failure of failures) {
351
+ if (slot && slot.name != failure.name) {
352
+ // We executed a single slot, ignore anything else
353
+ continue;
354
+ }
355
+ let failedSlot = this.slotByName(failure.name);
356
+ if (failedSlot) {
357
+ failedSlot.lastTestFailure = failure;
358
+ }
359
+ else {
360
+ logMessage("ERROR: Failed to find slot with name " + failure.name);
361
+ }
362
+ }
332
363
  }
333
364
  }
334
365
  exports.QtTest = QtTest;
@@ -336,6 +367,13 @@ exports.QtTest = QtTest;
336
367
  * Represents a single Qt test slot
337
368
  */
338
369
  class QtTestSlot {
370
+ name;
371
+ // The QTest executable this slot belongs to
372
+ parentQTest;
373
+ /// Allows vscode extensions to associate with a test item
374
+ vscodeTestItem;
375
+ /// Set after running
376
+ lastTestFailure;
339
377
  constructor(name, parent) {
340
378
  this.name = name;
341
379
  this.parentQTest = parent;
@@ -346,10 +384,8 @@ class QtTestSlot {
346
384
  get absoluteFilePath() {
347
385
  return this.parentQTest.filename;
348
386
  }
349
- runTest() {
350
- return __awaiter(this, void 0, void 0, function* () {
351
- return this.parentQTest.runTest(this);
352
- });
387
+ async runTest() {
388
+ return this.parentQTest.runTest(this);
353
389
  }
354
390
  command() {
355
391
  return {
@@ -364,51 +400,43 @@ exports.QtTestSlot = QtTestSlot;
364
400
  * Represents the list of all QtTest executables in your project
365
401
  */
366
402
  class QtTests {
367
- constructor() {
368
- this.qtTestExecutables = [];
369
- }
370
- discoverViaCMake(buildDirPath) {
371
- return __awaiter(this, void 0, void 0, function* () {
372
- var cmake = new cmake_1.CMakeTests(buildDirPath);
373
- let ctests = yield cmake.tests();
374
- if (ctests) {
375
- for (let ctest of ctests) {
376
- let qtest = new QtTest(ctest.executablePath(), buildDirPath);
377
- this.qtTestExecutables.push(qtest);
378
- }
379
- }
380
- else {
381
- logMessage("ERROR: Failed to retrieve ctests!");
403
+ qtTestExecutables = [];
404
+ async discoverViaCMake(buildDirPath) {
405
+ var cmake = new cmake_1.CMakeTests(buildDirPath);
406
+ let ctests = await cmake.tests();
407
+ if (ctests) {
408
+ for (let ctest of ctests) {
409
+ let qtest = new QtTest(ctest.executablePath(), buildDirPath);
410
+ this.qtTestExecutables.push(qtest);
382
411
  }
383
- });
412
+ }
413
+ else {
414
+ logMessage("ERROR: Failed to retrieve ctests!");
415
+ }
384
416
  }
385
417
  /// Removes any executable (from the list) that doesn't link to libQtTest.so
386
418
  /// This heuristic tries to filter-out doctest and other non-Qt tests
387
419
  /// Only implemented for linux for now
388
- removeNonLinking() {
389
- return __awaiter(this, void 0, void 0, function* () {
390
- let isLinux = process.platform === "linux";
391
- if (!isLinux) {
392
- return;
393
- }
394
- let acceptedExecutables = [];
395
- for (let ex of this.qtTestExecutables) {
396
- let linksToQt = yield ex.linksToQtTestLib();
397
- // undefined or true is accepted
398
- if (linksToQt !== false) {
399
- acceptedExecutables.push(ex);
400
- }
401
- this.qtTestExecutables = acceptedExecutables;
420
+ async removeNonLinking() {
421
+ let isLinux = process.platform === "linux";
422
+ if (!isLinux) {
423
+ return;
424
+ }
425
+ let acceptedExecutables = [];
426
+ for (let ex of this.qtTestExecutables) {
427
+ let linksToQt = await ex.linksToQtTestLib();
428
+ // undefined or true is accepted
429
+ if (linksToQt !== false) {
430
+ acceptedExecutables.push(ex);
402
431
  }
403
- });
432
+ this.qtTestExecutables = acceptedExecutables;
433
+ }
404
434
  }
405
435
  setLogFunction(func) {
406
436
  gLogFunction = func;
407
437
  }
408
- removeByRunningHelp() {
409
- return __awaiter(this, void 0, void 0, function* () {
410
- this.qtTestExecutables = this.qtTestExecutables.filter((ex) => __awaiter(this, void 0, void 0, function* () { return yield ex.isQtTestViaHelp(); }));
411
- });
438
+ async removeByRunningHelp() {
439
+ this.qtTestExecutables = this.qtTestExecutables.filter(async (ex) => await ex.isQtTestViaHelp());
412
440
  }
413
441
  /// Removes any executable (from the list) that matches the specified regex
414
442
  removeMatching(regex) {
@@ -423,19 +451,17 @@ class QtTests {
423
451
  console.log(ex.filename);
424
452
  }
425
453
  }
426
- dumpTestSlots() {
427
- return __awaiter(this, void 0, void 0, function* () {
428
- for (var ex of this.qtTestExecutables) {
429
- if (!ex.slots)
430
- yield ex.parseAvailableSlots();
431
- console.log(ex.filename);
432
- if (ex.slots) {
433
- for (let slot of ex.slots) {
434
- console.log(" - " + slot.name);
435
- }
454
+ async dumpTestSlots() {
455
+ for (var ex of this.qtTestExecutables) {
456
+ if (!ex.slots)
457
+ await ex.parseAvailableSlots();
458
+ console.log(ex.filename);
459
+ if (ex.slots) {
460
+ for (let slot of ex.slots) {
461
+ console.log(" - " + slot.name);
436
462
  }
437
463
  }
438
- });
464
+ }
439
465
  }
440
466
  /// Returns all executables that contain a Qt test slot with the specified name
441
467
  executablesContainingSlot(slotName) {