api-content-tracing-spec.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { expect } from 'chai';
  2. import { app, contentTracing, TraceConfig, TraceCategoriesAndOptions } from 'electron/main';
  3. import * as fs from 'fs';
  4. import * as path from 'path';
  5. import { ifdescribe, delay } from './spec-helpers';
  6. // FIXME: The tests are skipped on arm/arm64.
  7. ifdescribe(!(['arm', 'arm64'].includes(process.arch)))('contentTracing', () => {
  8. const record = async (options: TraceConfig | TraceCategoriesAndOptions, outputFilePath: string | undefined, recordTimeInMilliseconds = 1e1) => {
  9. await app.whenReady();
  10. await contentTracing.startRecording(options);
  11. await delay(recordTimeInMilliseconds);
  12. const resultFilePath = await contentTracing.stopRecording(outputFilePath);
  13. return resultFilePath;
  14. };
  15. const outputFilePath = path.join(app.getPath('temp'), 'trace.json');
  16. beforeEach(() => {
  17. if (fs.existsSync(outputFilePath)) {
  18. fs.unlinkSync(outputFilePath);
  19. }
  20. });
  21. describe('startRecording', function () {
  22. this.timeout(5e3);
  23. const getFileSizeInKiloBytes = (filePath: string) => {
  24. const stats = fs.statSync(filePath);
  25. const fileSizeInBytes = stats.size;
  26. const fileSizeInKiloBytes = fileSizeInBytes / 1024;
  27. return fileSizeInKiloBytes;
  28. };
  29. it('accepts an empty config', async () => {
  30. const config = {};
  31. await record(config, outputFilePath);
  32. expect(fs.existsSync(outputFilePath)).to.be.true('output exists');
  33. const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath);
  34. expect(fileSizeInKiloBytes).to.be.above(0,
  35. `the trace output file is empty, check "${outputFilePath}"`);
  36. });
  37. it('accepts a trace config', async () => {
  38. // (alexeykuzmin): All categories are excluded on purpose,
  39. // so only metadata gets into the output file.
  40. const config = {
  41. excluded_categories: ['*']
  42. };
  43. await record(config, outputFilePath);
  44. expect(fs.existsSync(outputFilePath)).to.be.true('output exists');
  45. // If the `excluded_categories` param above is not respected
  46. // the file size will be above 50KB.
  47. const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath);
  48. const expectedMaximumFileSize = 10; // Depends on a platform.
  49. expect(fileSizeInKiloBytes).to.be.above(0,
  50. `the trace output file is empty, check "${outputFilePath}"`);
  51. expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
  52. `the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
  53. check "${outputFilePath}"`);
  54. });
  55. it('accepts "categoryFilter" and "traceOptions" as a config', async () => {
  56. // (alexeykuzmin): All categories are excluded on purpose,
  57. // so only metadata gets into the output file.
  58. const config = {
  59. categoryFilter: '__ThisIsANonexistentCategory__',
  60. traceOptions: ''
  61. };
  62. await record(config, outputFilePath);
  63. expect(fs.existsSync(outputFilePath)).to.be.true('output exists');
  64. // If the `categoryFilter` param above is not respected
  65. // the file size will be above 50KB.
  66. const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath);
  67. const expectedMaximumFileSize = 10; // Depends on a platform.
  68. expect(fileSizeInKiloBytes).to.be.above(0,
  69. `the trace output file is empty, check "${outputFilePath}"`);
  70. expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
  71. `the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
  72. check "${outputFilePath}"`);
  73. });
  74. });
  75. describe('stopRecording', function () {
  76. this.timeout(5e3);
  77. it('does not crash on empty string', async () => {
  78. const options = {
  79. categoryFilter: '*',
  80. traceOptions: 'record-until-full,enable-sampling'
  81. };
  82. await contentTracing.startRecording(options);
  83. const path = await contentTracing.stopRecording('');
  84. expect(path).to.be.a('string').that.is.not.empty('result path');
  85. expect(fs.statSync(path).isFile()).to.be.true('output exists');
  86. });
  87. it('calls its callback with a result file path', async () => {
  88. const resultFilePath = await record(/* options */ {}, outputFilePath);
  89. expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath);
  90. });
  91. it('creates a temporary file when an empty string is passed', async function () {
  92. const resultFilePath = await record(/* options */ {}, /* outputFilePath */ '');
  93. expect(resultFilePath).to.be.a('string').that.is.not.empty('result path');
  94. });
  95. it('creates a temporary file when no path is passed', async function () {
  96. const resultFilePath = await record(/* options */ {}, /* outputFilePath */ undefined);
  97. expect(resultFilePath).to.be.a('string').that.is.not.empty('result path');
  98. });
  99. it('rejects if no trace is happening', async () => {
  100. await expect(contentTracing.stopRecording()).to.be.rejected();
  101. });
  102. });
  103. describe('captured events', () => {
  104. it('include V8 samples from the main process', async function () {
  105. // This test is flaky on macOS CI.
  106. this.retries(3);
  107. await contentTracing.startRecording({
  108. categoryFilter: 'disabled-by-default-v8.cpu_profiler',
  109. traceOptions: 'record-until-full'
  110. });
  111. {
  112. const start = +new Date();
  113. let n = 0;
  114. const f = () => {};
  115. while (+new Date() - start < 200 || n < 500) {
  116. await delay(0);
  117. f();
  118. n++;
  119. }
  120. }
  121. const path = await contentTracing.stopRecording();
  122. const data = fs.readFileSync(path, 'utf8');
  123. const parsed = JSON.parse(data);
  124. expect(parsed.traceEvents.some((x: any) => x.cat === 'disabled-by-default-v8.cpu_profiler' && x.name === 'ProfileChunk')).to.be.true();
  125. });
  126. });
  127. });