@jsenv/test 3.0.9 → 3.0.11

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.
@@ -1,10 +1,10 @@
1
- import os, { cpus, totalmem, freemem, availableParallelism } from "node:os";
1
+ import os, { cpus, release, totalmem, availableParallelism, freemem } from "node:os";
2
+ import process$1, { cpuUsage, memoryUsage } from "node:process";
2
3
  import { readdir, chmod, stat, lstat, promises, readFile as readFile$1, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, chmodSync, statSync, lstatSync, unlinkSync, readdirSync, rmdirSync, readFileSync, existsSync } from "node:fs";
3
4
  import { takeCoverage } from "node:v8";
4
5
  import stripAnsi from "strip-ansi";
5
6
  import { URL_META, createException } from "./js/exception.js";
6
7
  import stringWidth from "string-width";
7
- import process$1, { memoryUsage } from "node:process";
8
8
  import tty from "node:tty";
9
9
  import { pathToFileURL, fileURLToPath } from "node:url";
10
10
  import { dirname } from "node:path";
@@ -1456,7 +1456,7 @@ const humanizeFileSize = (numberOfBytes, { decimals, short } = {}) => {
1456
1456
  return inspectBytes(numberOfBytes, { decimals, short });
1457
1457
  };
1458
1458
 
1459
- const humanizeMemoryUsage = (metricValue, { decimals, short } = {}) => {
1459
+ const humanizeMemory = (metricValue, { decimals, short } = {}) => {
1460
1460
  return inspectBytes(metricValue, { decimals, fixedDecimals: true, short });
1461
1461
  };
1462
1462
 
@@ -3292,9 +3292,10 @@ process.platform === "linux";
3292
3292
  // https://gist.github.com/GaetanoPiazzolla/c40e1ebb9f709d091208e89baf9f4e00
3293
3293
 
3294
3294
 
3295
- const startMeasuringCpuUsage = () => {
3295
+ const startMeasuringTotalCpuUsage = () => {
3296
3296
  let previousCpuArray = cpus();
3297
3297
  let previousMs = Date.now();
3298
+ let previousCpuUsage = cpuUsage();
3298
3299
 
3299
3300
  const overall = {
3300
3301
  inactive: 100,
@@ -3302,6 +3303,12 @@ const startMeasuringCpuUsage = () => {
3302
3303
  system: 0,
3303
3304
  user: 0,
3304
3305
  };
3306
+
3307
+ const thisProcess = {
3308
+ active: 0,
3309
+ system: 0,
3310
+ user: 0,
3311
+ };
3305
3312
  const details = previousCpuArray.map(() => {
3306
3313
  return {
3307
3314
  inactive: 100,
@@ -3358,6 +3365,19 @@ const startMeasuringCpuUsage = () => {
3358
3365
  overallUsageSample,
3359
3366
  });
3360
3367
 
3368
+ const processCpuUsage = cpuUsage();
3369
+ const thisProcessSystemMs = Math.round(
3370
+ (processCpuUsage.system - previousCpuUsage.system) / 1000,
3371
+ );
3372
+ const thisProcessUserMs = Math.round(
3373
+ (processCpuUsage.user - previousCpuUsage.user) / 1000,
3374
+ );
3375
+ previousCpuUsage = processCpuUsage;
3376
+ const thisProcessActiveMs = thisProcessSystemMs + thisProcessUserMs;
3377
+ thisProcess.active = thisProcessActiveMs / overallMsEllapsed;
3378
+ thisProcess.system = thisProcessSystemMs / overallMsEllapsed;
3379
+ thisProcess.user = thisProcessUserMs / overallMsEllapsed;
3380
+
3361
3381
  if (samples.length === 10) {
3362
3382
  let index = 0;
3363
3383
  for (const detail of details) {
@@ -3407,6 +3427,7 @@ const startMeasuringCpuUsage = () => {
3407
3427
 
3408
3428
  return {
3409
3429
  overall,
3430
+ thisProcess,
3410
3431
  details,
3411
3432
  stop: () => {
3412
3433
  clearInterval(interval);
@@ -4946,7 +4967,9 @@ const replaceUrls = (source, replace) => {
4946
4967
  const reporterList = ({
4947
4968
  animated = true,
4948
4969
  mockFluctuatingValues, // used for snapshot testing logs
4949
- showMemoryUsage = true,
4970
+ platformInfo,
4971
+ memoryUsage: memoryUsage$1,
4972
+ cpuUsage,
4950
4973
  spy = () => {
4951
4974
  return {
4952
4975
  write: (log) => {
@@ -4967,7 +4990,9 @@ const reporterList = ({
4967
4990
  process.exitCode !== 1;
4968
4991
 
4969
4992
  const logOptions = {
4970
- showMemoryUsage,
4993
+ platformInfo,
4994
+ memoryUsage: memoryUsage$1,
4995
+ cpuUsage,
4971
4996
  mockFluctuatingValues,
4972
4997
  group: false,
4973
4998
  intermediateSummary: !animatedLogEnabled,
@@ -5028,7 +5053,7 @@ const reporterList = ({
5028
5053
  });
5029
5054
  infos.push(ANSI.color(duration, ANSI.GREY));
5030
5055
  const memoryHeapUsed = memoryUsage().heapUsed;
5031
- const memoryHeapUsedFormatted = humanizeMemoryUsage(memoryHeapUsed, {
5056
+ const memoryHeapUsedFormatted = humanizeMemory(memoryHeapUsed, {
5032
5057
  short: true,
5033
5058
  decimals: 0,
5034
5059
  });
@@ -5079,8 +5104,10 @@ const reporterList = ({
5079
5104
  reporters.push(
5080
5105
  reporterList({
5081
5106
  animated: false,
5107
+ platformInfo,
5108
+ memoryUsage: memoryUsage$1,
5109
+ cpuUsage,
5082
5110
  mockFluctuatingValues, // used for snapshot testing logs
5083
- showMemoryUsage,
5084
5111
  spy: () => {
5085
5112
  let rawOutput = "";
5086
5113
 
@@ -5100,58 +5127,59 @@ const reporterList = ({
5100
5127
  };
5101
5128
 
5102
5129
  const renderIntro = (testPlanResult, logOptions) => {
5103
- const { counters } = testPlanResult;
5104
- const planified = counters.planified;
5105
- const { groups } = testPlanResult;
5106
- const groupNames = Object.keys(groups);
5107
-
5108
- if (planified === 0) {
5109
- return `${renderBigSection({
5110
- title: "nothing to execute",
5111
- content: "",
5112
- })}\n\n`;
5113
- }
5114
- if (planified === 1) {
5115
- const groupName = groupNames[0];
5116
- const groupInfo = groups[groupName];
5117
- return `${renderBigSection({
5118
- title: "1 execution to run",
5119
- content: `${groupName} (${getGroupRenderedName(groupInfo, logOptions)})`,
5120
- })}\n\n`;
5121
- }
5122
- if (groupNames.length === 1) {
5123
- const groupName = groupNames[0];
5124
- const groupInfo = groups[groupName];
5125
- return `${renderBigSection({
5126
- title: `${planified} executions to run`,
5127
- content: `${groupName} (${getGroupRenderedName(groupInfo, logOptions)})`,
5128
- })}\n\n`;
5129
- }
5130
-
5131
- let introLines = [];
5132
- for (const groupName of groupNames) {
5133
- const groupInfo = groups[groupName];
5134
- introLines.push(
5135
- `${groupInfo.count} with ${groupName} (${getGroupRenderedName(
5136
- groupInfo,
5137
- logOptions,
5138
- )})`,
5139
- );
5130
+ const lines = [];
5131
+ if (logOptions.platformInfo) {
5132
+ {
5133
+ let osLine = `os: `;
5134
+ if (logOptions.mockFluctuatingValues) {
5135
+ osLine += `os@<mock>`;
5136
+ } else {
5137
+ osLine += `${testPlanResult.os.name}@${testPlanResult.os.version}`;
5138
+ }
5139
+ osLine += renderDetails({
5140
+ cpu: logOptions.mockFluctuatingValues
5141
+ ? "<mock>"
5142
+ : testPlanResult.os.availableCpu,
5143
+ memory: logOptions.mockFluctuatingValues
5144
+ ? "<mock>GB"
5145
+ : humanizeMemory(testPlanResult.os.availableMemory, {
5146
+ short: true,
5147
+ decimals: 0,
5148
+ }),
5149
+ });
5150
+ lines.push(osLine);
5151
+ }
5152
+ {
5153
+ const process = logOptions.mockFluctuatingValues
5154
+ ? `node@mock`
5155
+ : `${testPlanResult.process.name}@${testPlanResult.process.version}`;
5156
+ let processLine = `process: ${process}`;
5157
+ lines.push(processLine);
5158
+ }
5159
+ }
5160
+
5161
+ const directory = logOptions.mockFluctuatingValues
5162
+ ? "/mock/"
5163
+ : urlToFileSystemPath(testPlanResult.rootDirectoryUrl);
5164
+ const numberOfFiles = Object.keys(testPlanResult.results).length;
5165
+ let fileFoundLine = "";
5166
+ if (numberOfFiles === 0) {
5167
+ fileFoundLine += `no file matching "testPlan" in ${directory}:
5168
+ ${testPlanResult.patterns.join("\n")}`;
5169
+ } else if (numberOfFiles === 1) {
5170
+ fileFoundLine += `1 file matching "testPlan" in ${directory}`;
5171
+ } else {
5172
+ fileFoundLine += `${numberOfFiles} files matching "testPlan" in ${directory}`;
5140
5173
  }
5174
+
5175
+ lines.push(fileFoundLine);
5176
+
5141
5177
  return `${renderBigSection({
5142
- title: `${planified} executions to run`,
5143
- content: introLines.join("\n"),
5178
+ title: "execution start",
5179
+ content: lines.join("\n"),
5144
5180
  })}\n\n`;
5145
5181
  };
5146
5182
 
5147
- const getGroupRenderedName = (groupInfo, logOptions) => {
5148
- let { runtimeName, runtimeVersion } = groupInfo;
5149
- if (logOptions.mockFluctuatingValues && groupInfo.runtimeType === "node") {
5150
- runtimeVersion = "<mock>";
5151
- }
5152
- return `${runtimeName}@${runtimeVersion}`;
5153
- };
5154
-
5155
5183
  const renderExecutionLog = (execution, logOptions) => {
5156
5184
  let log = "";
5157
5185
  // label
@@ -5206,10 +5234,10 @@ const renderExecutionLabel = (execution, logOptions) => {
5206
5234
  : humanizeDuration(duration, { short: true });
5207
5235
  infos.push(ANSI.color(durationFormatted, ANSI.GREY));
5208
5236
  }
5209
- if (logOptions.showMemoryUsage && typeof memoryUsage === "number") {
5237
+ if (logOptions.memoryUsage && typeof memoryUsage === "number") {
5210
5238
  const memoryUsageFormatted = logOptions.mockFluctuatingValues
5211
5239
  ? `<mock>MB`
5212
- : humanizeMemoryUsage(memoryUsage, { short: true });
5240
+ : humanizeMemory(memoryUsage, { short: true });
5213
5241
  infos.push(ANSI.color(memoryUsageFormatted, ANSI.GREY));
5214
5242
  }
5215
5243
  if (infos.length) {
@@ -5471,55 +5499,88 @@ const renderErrors = (execution, logOptions) => {
5471
5499
  };
5472
5500
 
5473
5501
  const renderOutro = (testPlanResult, logOptions = {}) => {
5474
- let finalSummary = "";
5502
+ const lines = [];
5475
5503
  const { counters } = testPlanResult;
5476
5504
  const { planified } = counters;
5477
5505
 
5478
- if (planified === 1) {
5479
- finalSummary += `1 execution: `;
5506
+ let executionLine = "";
5507
+ if (planified === 0) {
5508
+ executionLine += `no execution`;
5509
+ } else if (planified === 1) {
5510
+ executionLine += `1 execution: `;
5480
5511
  } else {
5481
- finalSummary += `${planified} executions: `;
5512
+ executionLine += `${planified} executions: `;
5482
5513
  }
5483
- finalSummary += renderStatusRepartition(counters);
5514
+ executionLine += renderStatusRepartition(counters);
5515
+ lines.push(executionLine);
5484
5516
 
5485
- let durationLog = ``;
5517
+ let durationLine = `duration: `;
5486
5518
  const { timings } = testPlanResult;
5487
5519
  if (logOptions.mockFluctuatingValues) {
5488
- durationLog += "<mock>s";
5520
+ durationLine += "<mock>s";
5489
5521
  } else {
5490
- durationLog += humanizeDuration(timings.end, { short: true });
5522
+ durationLine += humanizeDuration(timings.end, { short: true });
5491
5523
  const namedTimings = {
5492
- setup: timings.executionStart,
5493
- execution: timings.executionEnd,
5494
- teardown: timings.teardownEnd - timings.executionEnd,
5524
+ setup: humanizeTiming(timings.executionStart),
5525
+ execution: humanizeTiming(timings.executionEnd),
5526
+ teardown: humanizeTiming(timings.teardownEnd - timings.executionEnd),
5495
5527
  ...(testPlanResult.coverage
5496
- ? { coverage: timings.coverageTeardownEnd - timings.teardownEnd }
5528
+ ? {
5529
+ coverage: humanizeTiming(
5530
+ timings.coverageTeardownEnd - timings.teardownEnd,
5531
+ ),
5532
+ }
5497
5533
  : {}),
5498
5534
  };
5499
- const timingDetails = [];
5500
- for (const key of Object.keys(namedTimings)) {
5501
- const value = namedTimings[key];
5502
- const timingDuration = humanizeDuration(value, { short: true });
5503
- const timingString = `${ANSI.color(`${key}:`, ANSI.GREY)} ${ANSI.color(
5504
- timingDuration,
5505
- ANSI.GREY,
5506
- )}`;
5507
- timingDetails.push(ANSI.color(timingString, ANSI.GREY));
5508
- }
5509
- if (timingDetails.length) {
5510
- durationLog += ` ${ANSI.color("(", ANSI.GREY)}${timingDetails.join(
5511
- ANSI.color(", ", ANSI.GREY),
5512
- )}${ANSI.color(")", ANSI.GREY)}`;
5513
- }
5535
+ durationLine += renderDetails(namedTimings);
5536
+ }
5537
+ lines.push(durationLine);
5538
+
5539
+ if (logOptions.cpuUsage) {
5540
+ const processCpuUsage = testPlanResult.cpuUsage.process;
5541
+ let cpuUsageLine = "cpu: ";
5542
+ cpuUsageLine += `${humanizeProcessCpuUsage(processCpuUsage.end)}`;
5543
+ cpuUsageLine += renderDetails({
5544
+ med: humanizeProcessCpuUsage(processCpuUsage.median),
5545
+ min: humanizeProcessCpuUsage(processCpuUsage.min),
5546
+ max: humanizeProcessCpuUsage(processCpuUsage.max),
5547
+ });
5548
+ lines.push(cpuUsageLine);
5549
+ }
5550
+ if (logOptions.memoryUsage) {
5551
+ const processMemoryUsage = testPlanResult.memoryUsage.process;
5552
+ let memoryUsageLine = "memory: ";
5553
+ memoryUsageLine += `${humanizeProcessMemoryUsage(processMemoryUsage.end)}`;
5554
+ debugger;
5555
+ memoryUsageLine += renderDetails({
5556
+ med: humanizeProcessMemoryUsage(processMemoryUsage.median),
5557
+ min: humanizeProcessMemoryUsage(processMemoryUsage.min),
5558
+ max: humanizeProcessMemoryUsage(processMemoryUsage.max),
5559
+ });
5560
+ lines.push(memoryUsageLine);
5514
5561
  }
5515
- finalSummary += `\nduration: ${durationLog}`;
5516
5562
 
5517
- return `\n${renderBigSection({ title: "summary", content: finalSummary })}\n`;
5563
+ return `\n${renderBigSection({ title: "summary", content: lines.join("\n") })}\n`;
5564
+ };
5565
+
5566
+ const humanizeTiming = (value) => {
5567
+ return humanizeDuration(value, { short: true });
5568
+ };
5569
+
5570
+ const humanizeProcessCpuUsage = (ratio) => {
5571
+ const percentageAsNumber = ratio * 100;
5572
+ const percentageAsNumberRounded = Math.round(percentageAsNumber);
5573
+ const percentage = `${percentageAsNumberRounded}%`;
5574
+ return percentage;
5575
+ };
5576
+
5577
+ const humanizeProcessMemoryUsage = (value) => {
5578
+ return humanizeMemory(value, { short: true, decimals: 0 });
5518
5579
  };
5519
5580
 
5520
5581
  const renderStatusRepartition = (counters, { showProgression } = {}) => {
5521
5582
  if (counters.planified === 0) {
5522
- return `nothing to run`;
5583
+ return ``;
5523
5584
  }
5524
5585
  if (counters.aborted === counters.planified) {
5525
5586
  return `all ${ANSI.color(`aborted`, COLOR_ABORTED)}`;
@@ -5606,6 +5667,29 @@ const renderSection = ({
5606
5667
  return section;
5607
5668
  };
5608
5669
 
5670
+ const renderDetails = (data) => {
5671
+ const details = [];
5672
+ for (const key of Object.keys(data)) {
5673
+ const value = data[key];
5674
+ let valueString = "";
5675
+ valueString += ANSI.color(`${key}:`, ANSI.GREY);
5676
+ const useNonGreyAnsiColor =
5677
+ typeof value === "string" && value.includes("\x1b");
5678
+ valueString += " ";
5679
+ valueString += useNonGreyAnsiColor ? value : ANSI.color(value, ANSI.GREY);
5680
+ details.push(valueString);
5681
+ }
5682
+ if (details.length === 0) {
5683
+ return "";
5684
+ }
5685
+
5686
+ let string = "";
5687
+ string += ` ${ANSI.color("(", ANSI.GREY)}`;
5688
+ string += details.join(ANSI.color(", ", ANSI.GREY));
5689
+ string += ANSI.color(")", ANSI.GREY);
5690
+ return string;
5691
+ };
5692
+
5609
5693
  /*
5610
5694
  *
5611
5695
  */
@@ -5627,6 +5711,9 @@ const logsDefault = {
5627
5711
  level: "info",
5628
5712
  type: "list",
5629
5713
  animated: true,
5714
+ platformInfo: false,
5715
+ memoryUsage: false,
5716
+ cpuUsage: false,
5630
5717
  fileUrl: undefined,
5631
5718
  };
5632
5719
  const githubCheckDefault = {
@@ -5671,8 +5758,8 @@ const coverageDefault = {
5671
5758
  };
5672
5759
  const parallelDefault = {
5673
5760
  max: "80%", // percentage resolved against the available cpus
5674
- maxCpu: "100%",
5675
- maxMemory: "100%",
5761
+ maxCpu: "80%",
5762
+ maxMemory: "50%",
5676
5763
  };
5677
5764
 
5678
5765
  const executeTestPlan = async ({
@@ -5723,12 +5810,63 @@ const executeTestPlan = async ({
5723
5810
  });
5724
5811
  }
5725
5812
 
5726
- const timingOrigin = Date.now();
5813
+ const cpuUsage = startMeasuringTotalCpuUsage();
5814
+ operation.addEndCallback(cpuUsage.stop);
5815
+ const processCpuUsageMonitoring = startMonitoringMetric(() => {
5816
+ return cpuUsage.thisProcess.active;
5817
+ });
5818
+ const osCpuUsageMonitoring = startMonitoringMetric(() => {
5819
+ return cpuUsage.overall.active;
5820
+ });
5821
+ const processMemoryUsageMonitoring = startMonitoringMetric(() => {
5822
+ return memoryUsage().rss;
5823
+ });
5824
+ const osMemoryUsageMonitoring = startMonitoringMetric(() => {
5825
+ const total = totalmem();
5826
+ const free = freemem();
5827
+ return total - free;
5828
+ });
5829
+
5830
+ const timingsOrigin = Date.now();
5727
5831
  const takeTiming = () => {
5728
- return Date.now() - timingOrigin;
5832
+ return Date.now() - timingsOrigin;
5729
5833
  };
5730
5834
  const testPlanResult = {
5835
+ os: {
5836
+ name:
5837
+ process.platform === "darwin"
5838
+ ? "mac"
5839
+ : process.platform === "win32" || process.platform === "win64"
5840
+ ? "windows"
5841
+ : process.platform === "linux"
5842
+ ? "linux"
5843
+ : "other",
5844
+ version: release(),
5845
+ availableCpu: countAvailableCpus(),
5846
+ availableMemory: totalmem(),
5847
+ },
5848
+ process: {
5849
+ name: "node",
5850
+ version: process.version.slice(1),
5851
+ },
5852
+ memoryUsage: {
5853
+ os: osMemoryUsageMonitoring.info,
5854
+ process: processMemoryUsageMonitoring.info,
5855
+ },
5856
+ cpuUsage: {
5857
+ os: osCpuUsageMonitoring.info,
5858
+ process: processCpuUsageMonitoring.info,
5859
+ },
5860
+ timings: {
5861
+ origin: timingsOrigin,
5862
+ executionStart: null,
5863
+ executionEnd: null,
5864
+ teardownEnd: null,
5865
+ coverageTeardownEnd: null,
5866
+ end: null,
5867
+ },
5731
5868
  rootDirectoryUrl: String(rootDirectoryUrl),
5869
+ patterns: Object.keys(testPlan),
5732
5870
  groups: {},
5733
5871
  counters: {
5734
5872
  planified: 0,
@@ -5745,14 +5883,6 @@ const executeTestPlan = async ({
5745
5883
  },
5746
5884
  aborted: false,
5747
5885
  failed: false,
5748
- timings: {
5749
- origin: timingOrigin,
5750
- executionStart: null,
5751
- executionEnd: null,
5752
- teardownEnd: null,
5753
- coverageTeardownEnd: null,
5754
- end: null,
5755
- },
5756
5886
  coverage: null,
5757
5887
  results: {},
5758
5888
  };
@@ -5780,9 +5910,6 @@ const executeTestPlan = async ({
5780
5910
  let finalizeCoverage;
5781
5911
 
5782
5912
  try {
5783
- const cpuUsage = startMeasuringCpuUsage();
5784
- operation.addEndCallback(cpuUsage.stop);
5785
-
5786
5913
  let logger;
5787
5914
  const runtimeInfo = {
5788
5915
  someNeedsServer: false,
@@ -5813,6 +5940,10 @@ const executeTestPlan = async ({
5813
5940
 
5814
5941
  if (logs.type === "list" && logger.levels.info) {
5815
5942
  const listReporterOptions = {
5943
+ mockFluctuatingValues: logs.mockFluctuatingValues,
5944
+ platformInfo: logs.platformInfo,
5945
+ memoryUsage: logs.memoryUsage,
5946
+ cpuUsage: logs.cpuUsage,
5816
5947
  animated: logger.levels.debug ? false : logs.animated,
5817
5948
  fileUrl:
5818
5949
  logs.fileUrl === undefined
@@ -5871,8 +6002,8 @@ const executeTestPlan = async ({
5871
6002
  const max = parallel.max;
5872
6003
  if (typeof max === "string") {
5873
6004
  const maxAsRatio = assertPercentageAndConvertToRatio(max);
5874
- const availableCpus = countAvailableCpus();
5875
- parallel.max = Math.round(maxAsRatio * availableCpus) || 1;
6005
+ parallel.max =
6006
+ Math.round(maxAsRatio * testPlanResult.os.availableCpu) || 1;
5876
6007
  } else if (typeof max === "number") {
5877
6008
  if (max < 1) {
5878
6009
  parallel.max = 1;
@@ -5886,8 +6017,9 @@ const executeTestPlan = async ({
5886
6017
  const maxMemory = parallel.maxMemory;
5887
6018
  if (typeof maxMemory === "string") {
5888
6019
  const maxMemoryAsRatio = assertPercentageAndConvertToRatio(maxMemory);
5889
- const totalMemory = totalmem();
5890
- parallel.maxMemory = Math.round(maxMemoryAsRatio * totalMemory);
6020
+ parallel.maxMemory = Math.round(
6021
+ maxMemoryAsRatio * testPlanResult.os.availableMemory,
6022
+ );
5891
6023
  } else if (typeof maxMemory !== "number") {
5892
6024
  throw new TypeError(
5893
6025
  `parallel.maxMemory must be a number or a percentage, got ${maxMemory}`,
@@ -6425,18 +6557,22 @@ To fix this warning:
6425
6557
  // if nothing is executing these limitations don't apply
6426
6558
  executionExecutingSet.size > 0
6427
6559
  ) {
6428
- const freeMemory = freemem();
6429
- const totalMemory = totalmem();
6430
- const usedMemory = totalMemory - freeMemory;
6431
- if (usedMemory > parallel.maxMemory) {
6560
+ if (processMemoryUsageMonitoring.measure() > parallel.maxMemory) {
6432
6561
  // retry after Xms in case memory usage decreases
6433
- const promise = operation.wait(200);
6562
+ const promise = (async () => {
6563
+ await operation.wait(200);
6564
+ await startAsMuchAsPossible();
6565
+ })();
6434
6566
  promises.push(promise);
6435
6567
  break;
6436
6568
  }
6437
- if (cpuUsage.overall.active > parallel.maxCpu) {
6569
+
6570
+ if (processCpuUsageMonitoring.measure() > parallel.maxCpu) {
6438
6571
  // retry after Xms in case cpu usage decreases
6439
- const promise = operation.wait(200);
6572
+ const promise = (async () => {
6573
+ await operation.wait(200);
6574
+ await startAsMuchAsPossible();
6575
+ })();
6440
6576
  promises.push(promise);
6441
6577
  break;
6442
6578
  }
@@ -6460,7 +6596,6 @@ To fix this warning:
6460
6596
  if (promises.length) {
6461
6597
  await Promise.all(promises);
6462
6598
  promises.length = 0;
6463
- await startAsMuchAsPossible();
6464
6599
  }
6465
6600
  };
6466
6601
 
@@ -6539,6 +6674,11 @@ To fix this warning:
6539
6674
  timings.coverageTeardownEnd = takeTiming();
6540
6675
  timings.end = takeTiming();
6541
6676
 
6677
+ osMemoryUsageMonitoring.end();
6678
+ processMemoryUsageMonitoring.end();
6679
+ osCpuUsageMonitoring.end();
6680
+ processCpuUsageMonitoring.end();
6681
+
6542
6682
  afterEachCallbackSet.clear();
6543
6683
  afterEachInOrderCallbackSet.clear();
6544
6684
  for (const afterAllCallback of afterAllCallbackSet) {
@@ -6551,6 +6691,51 @@ To fix this warning:
6551
6691
  return testPlanResult;
6552
6692
  };
6553
6693
 
6694
+ const startMonitoringMetric = (measure) => {
6695
+ const metrics = [];
6696
+ const takeMeasure = () => {
6697
+ const value = measure();
6698
+ metrics.push(value);
6699
+ return value;
6700
+ };
6701
+
6702
+ const info = {
6703
+ start: takeMeasure(),
6704
+ min: null,
6705
+ max: null,
6706
+ median: null,
6707
+ end: null,
6708
+ };
6709
+ return {
6710
+ info,
6711
+ measure: takeMeasure,
6712
+ end: () => {
6713
+ info.end = takeMeasure();
6714
+ metrics.sort((a, b) => a - b);
6715
+ info.min = metrics[0];
6716
+ info.max = metrics[metrics.length - 1];
6717
+ info.median = medianFromSortedArray(metrics);
6718
+ metrics.length = 0;
6719
+ },
6720
+ };
6721
+ };
6722
+
6723
+ const medianFromSortedArray = (array) => {
6724
+ const length = array.length;
6725
+ const isOdd = length % 2 === 1;
6726
+ if (isOdd) {
6727
+ const medianNumberIndex = (length - 1) / 2;
6728
+ const medianNumber = array[medianNumberIndex];
6729
+ return medianNumber;
6730
+ }
6731
+ const rightMiddleNumberIndex = length / 2;
6732
+ const leftMiddleNumberIndex = rightMiddleNumberIndex - 1;
6733
+ const leftMiddleNumber = array[leftMiddleNumberIndex];
6734
+ const rightMiddleNumber = array[rightMiddleNumberIndex];
6735
+ const medianNumber = (leftMiddleNumber + rightMiddleNumber) / 2;
6736
+ return medianNumber;
6737
+ };
6738
+
6554
6739
  const countAvailableCpus = () => {
6555
6740
  if (typeof availableParallelism === "function") {
6556
6741
  return availableParallelism();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/test",
3
- "version": "3.0.9",
3
+ "version": "3.0.11",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -2,7 +2,14 @@
2
2
  *
3
3
  */
4
4
 
5
- import { cpus, availableParallelism, freemem, totalmem } from "node:os";
5
+ import {
6
+ release,
7
+ cpus,
8
+ availableParallelism,
9
+ totalmem,
10
+ freemem,
11
+ } from "node:os";
12
+ import { memoryUsage } from "node:process";
6
13
  import { existsSync } from "node:fs";
7
14
  import { takeCoverage } from "node:v8";
8
15
  import stripAnsi from "strip-ansi";
@@ -19,7 +26,7 @@ import {
19
26
  readGitHubWorkflowEnv,
20
27
  } from "@jsenv/github-check-run";
21
28
 
22
- import { startMeasuringCpuUsage } from "../helpers/cpu_usage.js";
29
+ import { startMeasuringTotalCpuUsage } from "../helpers/cpu_usage.js";
23
30
  import { createCallOrderer } from "../helpers/call_orderer.js";
24
31
  import { generateCoverage } from "../coverage/generate_coverage.js";
25
32
  import { assertAndNormalizeWebServer } from "./web_server_param.js";
@@ -43,6 +50,9 @@ const logsDefault = {
43
50
  level: "info",
44
51
  type: "list",
45
52
  animated: true,
53
+ platformInfo: false,
54
+ memoryUsage: false,
55
+ cpuUsage: false,
46
56
  fileUrl: undefined,
47
57
  };
48
58
  const githubCheckDefault = {
@@ -87,8 +97,8 @@ const coverageDefault = {
87
97
  };
88
98
  const parallelDefault = {
89
99
  max: "80%", // percentage resolved against the available cpus
90
- maxCpu: "100%",
91
- maxMemory: "100%",
100
+ maxCpu: "80%",
101
+ maxMemory: "50%",
92
102
  };
93
103
 
94
104
  export const executeTestPlan = async ({
@@ -139,12 +149,63 @@ export const executeTestPlan = async ({
139
149
  });
140
150
  }
141
151
 
142
- const timingOrigin = Date.now();
152
+ const cpuUsage = startMeasuringTotalCpuUsage();
153
+ operation.addEndCallback(cpuUsage.stop);
154
+ const processCpuUsageMonitoring = startMonitoringMetric(() => {
155
+ return cpuUsage.thisProcess.active;
156
+ });
157
+ const osCpuUsageMonitoring = startMonitoringMetric(() => {
158
+ return cpuUsage.overall.active;
159
+ });
160
+ const processMemoryUsageMonitoring = startMonitoringMetric(() => {
161
+ return memoryUsage().rss;
162
+ });
163
+ const osMemoryUsageMonitoring = startMonitoringMetric(() => {
164
+ const total = totalmem();
165
+ const free = freemem();
166
+ return total - free;
167
+ });
168
+
169
+ const timingsOrigin = Date.now();
143
170
  const takeTiming = () => {
144
- return Date.now() - timingOrigin;
171
+ return Date.now() - timingsOrigin;
145
172
  };
146
173
  const testPlanResult = {
174
+ os: {
175
+ name:
176
+ process.platform === "darwin"
177
+ ? "mac"
178
+ : process.platform === "win32" || process.platform === "win64"
179
+ ? "windows"
180
+ : process.platform === "linux"
181
+ ? "linux"
182
+ : "other",
183
+ version: release(),
184
+ availableCpu: countAvailableCpus(),
185
+ availableMemory: totalmem(),
186
+ },
187
+ process: {
188
+ name: "node",
189
+ version: process.version.slice(1),
190
+ },
191
+ memoryUsage: {
192
+ os: osMemoryUsageMonitoring.info,
193
+ process: processMemoryUsageMonitoring.info,
194
+ },
195
+ cpuUsage: {
196
+ os: osCpuUsageMonitoring.info,
197
+ process: processCpuUsageMonitoring.info,
198
+ },
199
+ timings: {
200
+ origin: timingsOrigin,
201
+ executionStart: null,
202
+ executionEnd: null,
203
+ teardownEnd: null,
204
+ coverageTeardownEnd: null,
205
+ end: null,
206
+ },
147
207
  rootDirectoryUrl: String(rootDirectoryUrl),
208
+ patterns: Object.keys(testPlan),
148
209
  groups: {},
149
210
  counters: {
150
211
  planified: 0,
@@ -161,14 +222,6 @@ export const executeTestPlan = async ({
161
222
  },
162
223
  aborted: false,
163
224
  failed: false,
164
- timings: {
165
- origin: timingOrigin,
166
- executionStart: null,
167
- executionEnd: null,
168
- teardownEnd: null,
169
- coverageTeardownEnd: null,
170
- end: null,
171
- },
172
225
  coverage: null,
173
226
  results: {},
174
227
  };
@@ -196,9 +249,6 @@ export const executeTestPlan = async ({
196
249
  let finalizeCoverage;
197
250
 
198
251
  try {
199
- const cpuUsage = startMeasuringCpuUsage();
200
- operation.addEndCallback(cpuUsage.stop);
201
-
202
252
  let logger;
203
253
  const runtimeInfo = {
204
254
  someNeedsServer: false,
@@ -229,6 +279,10 @@ export const executeTestPlan = async ({
229
279
 
230
280
  if (logs.type === "list" && logger.levels.info) {
231
281
  const listReporterOptions = {
282
+ mockFluctuatingValues: logs.mockFluctuatingValues,
283
+ platformInfo: logs.platformInfo,
284
+ memoryUsage: logs.memoryUsage,
285
+ cpuUsage: logs.cpuUsage,
232
286
  animated: logger.levels.debug ? false : logs.animated,
233
287
  fileUrl:
234
288
  logs.fileUrl === undefined
@@ -287,8 +341,8 @@ export const executeTestPlan = async ({
287
341
  const max = parallel.max;
288
342
  if (typeof max === "string") {
289
343
  const maxAsRatio = assertPercentageAndConvertToRatio(max);
290
- const availableCpus = countAvailableCpus();
291
- parallel.max = Math.round(maxAsRatio * availableCpus) || 1;
344
+ parallel.max =
345
+ Math.round(maxAsRatio * testPlanResult.os.availableCpu) || 1;
292
346
  } else if (typeof max === "number") {
293
347
  if (max < 1) {
294
348
  parallel.max = 1;
@@ -302,8 +356,9 @@ export const executeTestPlan = async ({
302
356
  const maxMemory = parallel.maxMemory;
303
357
  if (typeof maxMemory === "string") {
304
358
  const maxMemoryAsRatio = assertPercentageAndConvertToRatio(maxMemory);
305
- const totalMemory = totalmem();
306
- parallel.maxMemory = Math.round(maxMemoryAsRatio * totalMemory);
359
+ parallel.maxMemory = Math.round(
360
+ maxMemoryAsRatio * testPlanResult.os.availableMemory,
361
+ );
307
362
  } else if (typeof maxMemory !== "number") {
308
363
  throw new TypeError(
309
364
  `parallel.maxMemory must be a number or a percentage, got ${maxMemory}`,
@@ -841,18 +896,22 @@ To fix this warning:
841
896
  // if nothing is executing these limitations don't apply
842
897
  executionExecutingSet.size > 0
843
898
  ) {
844
- const freeMemory = freemem();
845
- const totalMemory = totalmem();
846
- const usedMemory = totalMemory - freeMemory;
847
- if (usedMemory > parallel.maxMemory) {
899
+ if (processMemoryUsageMonitoring.measure() > parallel.maxMemory) {
848
900
  // retry after Xms in case memory usage decreases
849
- const promise = operation.wait(200);
901
+ const promise = (async () => {
902
+ await operation.wait(200);
903
+ await startAsMuchAsPossible();
904
+ })();
850
905
  promises.push(promise);
851
906
  break;
852
907
  }
853
- if (cpuUsage.overall.active > parallel.maxCpu) {
908
+
909
+ if (processCpuUsageMonitoring.measure() > parallel.maxCpu) {
854
910
  // retry after Xms in case cpu usage decreases
855
- const promise = operation.wait(200);
911
+ const promise = (async () => {
912
+ await operation.wait(200);
913
+ await startAsMuchAsPossible();
914
+ })();
856
915
  promises.push(promise);
857
916
  break;
858
917
  }
@@ -876,7 +935,6 @@ To fix this warning:
876
935
  if (promises.length) {
877
936
  await Promise.all(promises);
878
937
  promises.length = 0;
879
- await startAsMuchAsPossible();
880
938
  }
881
939
  };
882
940
 
@@ -956,6 +1014,11 @@ To fix this warning:
956
1014
  timings.coverageTeardownEnd = takeTiming();
957
1015
  timings.end = takeTiming();
958
1016
 
1017
+ osMemoryUsageMonitoring.end();
1018
+ processMemoryUsageMonitoring.end();
1019
+ osCpuUsageMonitoring.end();
1020
+ processCpuUsageMonitoring.end();
1021
+
959
1022
  afterEachCallbackSet.clear();
960
1023
  afterEachInOrderCallbackSet.clear();
961
1024
  for (const afterAllCallback of afterAllCallbackSet) {
@@ -968,6 +1031,51 @@ To fix this warning:
968
1031
  return testPlanResult;
969
1032
  };
970
1033
 
1034
+ const startMonitoringMetric = (measure) => {
1035
+ const metrics = [];
1036
+ const takeMeasure = () => {
1037
+ const value = measure();
1038
+ metrics.push(value);
1039
+ return value;
1040
+ };
1041
+
1042
+ const info = {
1043
+ start: takeMeasure(),
1044
+ min: null,
1045
+ max: null,
1046
+ median: null,
1047
+ end: null,
1048
+ };
1049
+ return {
1050
+ info,
1051
+ measure: takeMeasure,
1052
+ end: () => {
1053
+ info.end = takeMeasure();
1054
+ metrics.sort((a, b) => a - b);
1055
+ info.min = metrics[0];
1056
+ info.max = metrics[metrics.length - 1];
1057
+ info.median = medianFromSortedArray(metrics);
1058
+ metrics.length = 0;
1059
+ },
1060
+ };
1061
+ };
1062
+
1063
+ const medianFromSortedArray = (array) => {
1064
+ const length = array.length;
1065
+ const isOdd = length % 2 === 1;
1066
+ if (isOdd) {
1067
+ const medianNumberIndex = (length - 1) / 2;
1068
+ const medianNumber = array[medianNumberIndex];
1069
+ return medianNumber;
1070
+ }
1071
+ const rightMiddleNumberIndex = length / 2;
1072
+ const leftMiddleNumberIndex = rightMiddleNumberIndex - 1;
1073
+ const leftMiddleNumber = array[leftMiddleNumberIndex];
1074
+ const rightMiddleNumber = array[rightMiddleNumberIndex];
1075
+ const medianNumber = (leftMiddleNumber + rightMiddleNumber) / 2;
1076
+ return medianNumber;
1077
+ };
1078
+
971
1079
  const countAvailableCpus = () => {
972
1080
  if (typeof availableParallelism === "function") {
973
1081
  return availableParallelism();
@@ -1,13 +1,14 @@
1
- import { memoryUsage } from "node:process";
1
+ import { memoryUsage as processMemoryUsage } from "node:process";
2
2
  import stripAnsi from "strip-ansi";
3
3
  import { writeFileSync } from "@jsenv/filesystem";
4
4
  import {
5
5
  createDynamicLog,
6
6
  humanizeDuration,
7
- humanizeMemoryUsage,
7
+ humanizeMemory,
8
8
  ANSI,
9
9
  UNICODE,
10
10
  } from "@jsenv/humanize";
11
+ import { urlToFileSystemPath } from "@jsenv/urls";
11
12
 
12
13
  import { formatErrorForTerminal } from "../format_error_for_terminal.js";
13
14
 
@@ -36,7 +37,9 @@ import { formatErrorForTerminal } from "../format_error_for_terminal.js";
36
37
  export const reporterList = ({
37
38
  animated = true,
38
39
  mockFluctuatingValues, // used for snapshot testing logs
39
- showMemoryUsage = true,
40
+ platformInfo,
41
+ memoryUsage,
42
+ cpuUsage,
40
43
  spy = () => {
41
44
  return {
42
45
  write: (log) => {
@@ -57,7 +60,9 @@ export const reporterList = ({
57
60
  process.exitCode !== 1;
58
61
 
59
62
  const logOptions = {
60
- showMemoryUsage,
63
+ platformInfo,
64
+ memoryUsage,
65
+ cpuUsage,
61
66
  mockFluctuatingValues,
62
67
  group: false,
63
68
  intermediateSummary: !animatedLogEnabled,
@@ -117,8 +122,8 @@ export const reporterList = ({
117
122
  rounded: false,
118
123
  });
119
124
  infos.push(ANSI.color(duration, ANSI.GREY));
120
- const memoryHeapUsed = memoryUsage().heapUsed;
121
- const memoryHeapUsedFormatted = humanizeMemoryUsage(memoryHeapUsed, {
125
+ const memoryHeapUsed = processMemoryUsage().heapUsed;
126
+ const memoryHeapUsedFormatted = humanizeMemory(memoryHeapUsed, {
122
127
  short: true,
123
128
  decimals: 0,
124
129
  });
@@ -169,8 +174,10 @@ export const reporterList = ({
169
174
  reporters.push(
170
175
  reporterList({
171
176
  animated: false,
177
+ platformInfo,
178
+ memoryUsage,
179
+ cpuUsage,
172
180
  mockFluctuatingValues, // used for snapshot testing logs
173
- showMemoryUsage,
174
181
  spy: () => {
175
182
  let rawOutput = "";
176
183
 
@@ -190,58 +197,59 @@ export const reporterList = ({
190
197
  };
191
198
 
192
199
  const renderIntro = (testPlanResult, logOptions) => {
193
- const { counters } = testPlanResult;
194
- const planified = counters.planified;
195
- const { groups } = testPlanResult;
196
- const groupNames = Object.keys(groups);
200
+ const lines = [];
201
+ if (logOptions.platformInfo) {
202
+ os_line: {
203
+ let osLine = `os: `;
204
+ if (logOptions.mockFluctuatingValues) {
205
+ osLine += `os@<mock>`;
206
+ } else {
207
+ osLine += `${testPlanResult.os.name}@${testPlanResult.os.version}`;
208
+ }
209
+ osLine += renderDetails({
210
+ cpu: logOptions.mockFluctuatingValues
211
+ ? "<mock>"
212
+ : testPlanResult.os.availableCpu,
213
+ memory: logOptions.mockFluctuatingValues
214
+ ? "<mock>GB"
215
+ : humanizeMemory(testPlanResult.os.availableMemory, {
216
+ short: true,
217
+ decimals: 0,
218
+ }),
219
+ });
220
+ lines.push(osLine);
221
+ }
222
+ process_line: {
223
+ const process = logOptions.mockFluctuatingValues
224
+ ? `node@mock`
225
+ : `${testPlanResult.process.name}@${testPlanResult.process.version}`;
226
+ let processLine = `process: ${process}`;
227
+ lines.push(processLine);
228
+ }
229
+ }
197
230
 
198
- if (planified === 0) {
199
- return `${renderBigSection({
200
- title: "nothing to execute",
201
- content: "",
202
- })}\n\n`;
203
- }
204
- if (planified === 1) {
205
- const groupName = groupNames[0];
206
- const groupInfo = groups[groupName];
207
- return `${renderBigSection({
208
- title: "1 execution to run",
209
- content: `${groupName} (${getGroupRenderedName(groupInfo, logOptions)})`,
210
- })}\n\n`;
211
- }
212
- if (groupNames.length === 1) {
213
- const groupName = groupNames[0];
214
- const groupInfo = groups[groupName];
215
- return `${renderBigSection({
216
- title: `${planified} executions to run`,
217
- content: `${groupName} (${getGroupRenderedName(groupInfo, logOptions)})`,
218
- })}\n\n`;
219
- }
220
-
221
- let introLines = [];
222
- for (const groupName of groupNames) {
223
- const groupInfo = groups[groupName];
224
- introLines.push(
225
- `${groupInfo.count} with ${groupName} (${getGroupRenderedName(
226
- groupInfo,
227
- logOptions,
228
- )})`,
229
- );
231
+ const directory = logOptions.mockFluctuatingValues
232
+ ? "/mock/"
233
+ : urlToFileSystemPath(testPlanResult.rootDirectoryUrl);
234
+ const numberOfFiles = Object.keys(testPlanResult.results).length;
235
+ let fileFoundLine = "";
236
+ if (numberOfFiles === 0) {
237
+ fileFoundLine += `no file matching "testPlan" in ${directory}:
238
+ ${testPlanResult.patterns.join("\n")}`;
239
+ } else if (numberOfFiles === 1) {
240
+ fileFoundLine += `1 file matching "testPlan" in ${directory}`;
241
+ } else {
242
+ fileFoundLine += `${numberOfFiles} files matching "testPlan" in ${directory}`;
230
243
  }
244
+
245
+ lines.push(fileFoundLine);
246
+
231
247
  return `${renderBigSection({
232
- title: `${planified} executions to run`,
233
- content: introLines.join("\n"),
248
+ title: "execution start",
249
+ content: lines.join("\n"),
234
250
  })}\n\n`;
235
251
  };
236
252
 
237
- const getGroupRenderedName = (groupInfo, logOptions) => {
238
- let { runtimeName, runtimeVersion } = groupInfo;
239
- if (logOptions.mockFluctuatingValues && groupInfo.runtimeType === "node") {
240
- runtimeVersion = "<mock>";
241
- }
242
- return `${runtimeName}@${runtimeVersion}`;
243
- };
244
-
245
253
  const renderExecutionLog = (execution, logOptions) => {
246
254
  let log = "";
247
255
  // label
@@ -296,10 +304,10 @@ const renderExecutionLabel = (execution, logOptions) => {
296
304
  : humanizeDuration(duration, { short: true });
297
305
  infos.push(ANSI.color(durationFormatted, ANSI.GREY));
298
306
  }
299
- if (logOptions.showMemoryUsage && typeof memoryUsage === "number") {
307
+ if (logOptions.memoryUsage && typeof memoryUsage === "number") {
300
308
  const memoryUsageFormatted = logOptions.mockFluctuatingValues
301
309
  ? `<mock>MB`
302
- : humanizeMemoryUsage(memoryUsage, { short: true });
310
+ : humanizeMemory(memoryUsage, { short: true });
303
311
  infos.push(ANSI.color(memoryUsageFormatted, ANSI.GREY));
304
312
  }
305
313
  if (infos.length) {
@@ -561,55 +569,88 @@ const renderErrors = (execution, logOptions) => {
561
569
  };
562
570
 
563
571
  export const renderOutro = (testPlanResult, logOptions = {}) => {
564
- let finalSummary = "";
572
+ const lines = [];
565
573
  const { counters } = testPlanResult;
566
574
  const { planified } = counters;
567
575
 
568
- if (planified === 1) {
569
- finalSummary += `1 execution: `;
576
+ let executionLine = "";
577
+ if (planified === 0) {
578
+ executionLine += `no execution`;
579
+ } else if (planified === 1) {
580
+ executionLine += `1 execution: `;
570
581
  } else {
571
- finalSummary += `${planified} executions: `;
582
+ executionLine += `${planified} executions: `;
572
583
  }
573
- finalSummary += renderStatusRepartition(counters);
584
+ executionLine += renderStatusRepartition(counters);
585
+ lines.push(executionLine);
574
586
 
575
- let durationLog = ``;
587
+ let durationLine = `duration: `;
576
588
  const { timings } = testPlanResult;
577
589
  if (logOptions.mockFluctuatingValues) {
578
- durationLog += "<mock>s";
590
+ durationLine += "<mock>s";
579
591
  } else {
580
- durationLog += humanizeDuration(timings.end, { short: true });
592
+ durationLine += humanizeDuration(timings.end, { short: true });
581
593
  const namedTimings = {
582
- setup: timings.executionStart,
583
- execution: timings.executionEnd,
584
- teardown: timings.teardownEnd - timings.executionEnd,
594
+ setup: humanizeTiming(timings.executionStart),
595
+ execution: humanizeTiming(timings.executionEnd),
596
+ teardown: humanizeTiming(timings.teardownEnd - timings.executionEnd),
585
597
  ...(testPlanResult.coverage
586
- ? { coverage: timings.coverageTeardownEnd - timings.teardownEnd }
598
+ ? {
599
+ coverage: humanizeTiming(
600
+ timings.coverageTeardownEnd - timings.teardownEnd,
601
+ ),
602
+ }
587
603
  : {}),
588
604
  };
589
- const timingDetails = [];
590
- for (const key of Object.keys(namedTimings)) {
591
- const value = namedTimings[key];
592
- const timingDuration = humanizeDuration(value, { short: true });
593
- const timingString = `${ANSI.color(`${key}:`, ANSI.GREY)} ${ANSI.color(
594
- timingDuration,
595
- ANSI.GREY,
596
- )}`;
597
- timingDetails.push(ANSI.color(timingString, ANSI.GREY));
598
- }
599
- if (timingDetails.length) {
600
- durationLog += ` ${ANSI.color("(", ANSI.GREY)}${timingDetails.join(
601
- ANSI.color(", ", ANSI.GREY),
602
- )}${ANSI.color(")", ANSI.GREY)}`;
603
- }
605
+ durationLine += renderDetails(namedTimings);
606
+ }
607
+ lines.push(durationLine);
608
+
609
+ if (logOptions.cpuUsage) {
610
+ const processCpuUsage = testPlanResult.cpuUsage.process;
611
+ let cpuUsageLine = "cpu: ";
612
+ cpuUsageLine += `${humanizeProcessCpuUsage(processCpuUsage.end)}`;
613
+ cpuUsageLine += renderDetails({
614
+ med: humanizeProcessCpuUsage(processCpuUsage.median),
615
+ min: humanizeProcessCpuUsage(processCpuUsage.min),
616
+ max: humanizeProcessCpuUsage(processCpuUsage.max),
617
+ });
618
+ lines.push(cpuUsageLine);
619
+ }
620
+ if (logOptions.memoryUsage) {
621
+ const processMemoryUsage = testPlanResult.memoryUsage.process;
622
+ let memoryUsageLine = "memory: ";
623
+ memoryUsageLine += `${humanizeProcessMemoryUsage(processMemoryUsage.end)}`;
624
+ debugger;
625
+ memoryUsageLine += renderDetails({
626
+ med: humanizeProcessMemoryUsage(processMemoryUsage.median),
627
+ min: humanizeProcessMemoryUsage(processMemoryUsage.min),
628
+ max: humanizeProcessMemoryUsage(processMemoryUsage.max),
629
+ });
630
+ lines.push(memoryUsageLine);
604
631
  }
605
- finalSummary += `\nduration: ${durationLog}`;
606
632
 
607
- return `\n${renderBigSection({ title: "summary", content: finalSummary })}\n`;
633
+ return `\n${renderBigSection({ title: "summary", content: lines.join("\n") })}\n`;
634
+ };
635
+
636
+ const humanizeTiming = (value) => {
637
+ return humanizeDuration(value, { short: true });
638
+ };
639
+
640
+ const humanizeProcessCpuUsage = (ratio) => {
641
+ const percentageAsNumber = ratio * 100;
642
+ const percentageAsNumberRounded = Math.round(percentageAsNumber);
643
+ const percentage = `${percentageAsNumberRounded}%`;
644
+ return percentage;
645
+ };
646
+
647
+ const humanizeProcessMemoryUsage = (value) => {
648
+ return humanizeMemory(value, { short: true, decimals: 0 });
608
649
  };
609
650
 
610
651
  const renderStatusRepartition = (counters, { showProgression } = {}) => {
611
652
  if (counters.planified === 0) {
612
- return `nothing to run`;
653
+ return ``;
613
654
  }
614
655
  if (counters.aborted === counters.planified) {
615
656
  return `all ${ANSI.color(`aborted`, COLOR_ABORTED)}`;
@@ -695,3 +736,26 @@ const renderSection = ({
695
736
  section += bottomDashes;
696
737
  return section;
697
738
  };
739
+
740
+ const renderDetails = (data) => {
741
+ const details = [];
742
+ for (const key of Object.keys(data)) {
743
+ const value = data[key];
744
+ let valueString = "";
745
+ valueString += ANSI.color(`${key}:`, ANSI.GREY);
746
+ const useNonGreyAnsiColor =
747
+ typeof value === "string" && value.includes("\x1b");
748
+ valueString += " ";
749
+ valueString += useNonGreyAnsiColor ? value : ANSI.color(value, ANSI.GREY);
750
+ details.push(valueString);
751
+ }
752
+ if (details.length === 0) {
753
+ return "";
754
+ }
755
+
756
+ let string = "";
757
+ string += ` ${ANSI.color("(", ANSI.GREY)}`;
758
+ string += details.join(ANSI.color(", ", ANSI.GREY));
759
+ string += ANSI.color(")", ANSI.GREY);
760
+ return string;
761
+ };
@@ -1,10 +1,12 @@
1
1
  // https://gist.github.com/GaetanoPiazzolla/c40e1ebb9f709d091208e89baf9f4e00
2
2
 
3
3
  import { cpus } from "node:os";
4
+ import { cpuUsage } from "node:process";
4
5
 
5
- export const startMeasuringCpuUsage = () => {
6
+ export const startMeasuringTotalCpuUsage = () => {
6
7
  let previousCpuArray = cpus();
7
8
  let previousMs = Date.now();
9
+ let previousCpuUsage = cpuUsage();
8
10
 
9
11
  const overall = {
10
12
  inactive: 100,
@@ -12,6 +14,12 @@ export const startMeasuringCpuUsage = () => {
12
14
  system: 0,
13
15
  user: 0,
14
16
  };
17
+
18
+ const thisProcess = {
19
+ active: 0,
20
+ system: 0,
21
+ user: 0,
22
+ };
15
23
  const details = previousCpuArray.map(() => {
16
24
  return {
17
25
  inactive: 100,
@@ -68,6 +76,19 @@ export const startMeasuringCpuUsage = () => {
68
76
  overallUsageSample,
69
77
  });
70
78
 
79
+ const processCpuUsage = cpuUsage();
80
+ const thisProcessSystemMs = Math.round(
81
+ (processCpuUsage.system - previousCpuUsage.system) / 1000,
82
+ );
83
+ const thisProcessUserMs = Math.round(
84
+ (processCpuUsage.user - previousCpuUsage.user) / 1000,
85
+ );
86
+ previousCpuUsage = processCpuUsage;
87
+ const thisProcessActiveMs = thisProcessSystemMs + thisProcessUserMs;
88
+ thisProcess.active = thisProcessActiveMs / overallMsEllapsed;
89
+ thisProcess.system = thisProcessSystemMs / overallMsEllapsed;
90
+ thisProcess.user = thisProcessUserMs / overallMsEllapsed;
91
+
71
92
  if (samples.length === 10) {
72
93
  let index = 0;
73
94
  for (const detail of details) {
@@ -117,6 +138,7 @@ export const startMeasuringCpuUsage = () => {
117
138
 
118
139
  return {
119
140
  overall,
141
+ thisProcess,
120
142
  details,
121
143
  stop: () => {
122
144
  clearInterval(interval);
@@ -140,3 +162,35 @@ export const ratioAsPercentage = (ratio) => {
140
162
  const percentage = `${percentageAsNumberRounded}%`;
141
163
  return percentage;
142
164
  };
165
+
166
+ export const startMeasuringCpuUsage = () => {
167
+ let previousHrtime = process.hrtime();
168
+ let previousCpuUsage = process.cpuUsage();
169
+
170
+ const info = {
171
+ used: 0,
172
+ };
173
+
174
+ const interval = setInterval(() => {
175
+ const hrtime = process.hrtime(previousHrtime);
176
+ previousHrtime = hrtime;
177
+ const cpuUsage = process.cpuUsage(previousCpuUsage);
178
+ previousCpuUsage = cpuUsage;
179
+
180
+ const ellapsedMs = hrtime[0] * 1000 + hrtime[1] / 1000000;
181
+ const userMs = Math.round(cpuUsage.user / 1000);
182
+ const systemMs = Math.round(cpuUsage.system / 1000);
183
+ const activeMs = userMs + systemMs;
184
+ const usedRatio = activeMs / ellapsedMs;
185
+ const used = Math.round(usedRatio * 100) / 100;
186
+ info.used = used;
187
+ }, 15);
188
+ interval.unref();
189
+
190
+ return {
191
+ info,
192
+ stop: () => {
193
+ clearInterval(interval);
194
+ },
195
+ };
196
+ };
@@ -1,7 +1,15 @@
1
- import { startMeasuringCpuUsage, formatUsage } from "./cpu_usage.js";
1
+ import {
2
+ startMeasuringCpuUsage,
3
+ startMeasuringTotalCpuUsage,
4
+ formatUsage,
5
+ } from "./cpu_usage.js";
2
6
 
3
7
  const cpuUsage = startMeasuringCpuUsage();
8
+ setInterval(() => {
9
+ console.log(cpuUsage.info.used);
10
+ }, 400);
4
11
 
12
+ const totalCpuUsage = startMeasuringTotalCpuUsage();
5
13
  setInterval(() => {
6
- console.log(formatUsage(cpuUsage.overall));
14
+ console.log(formatUsage(totalCpuUsage.overall));
7
15
  }, 400);