summaryrefslogtreecommitdiff
path: root/lib/internal/process/coverage.js
blob: 95235c8ac913a7e4fb4cc95af3ea9ca8a7c786b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
'use strict';
let coverageConnection = null;
let coverageDirectory;

function writeCoverage() {
  if (!coverageConnection && coverageDirectory) {
    return;
  }

  const { join } = require('path');
  const { mkdirSync, writeFileSync } = require('fs');
  const { threadId } = require('internal/worker');

  const filename = `coverage-${process.pid}-${Date.now()}-${threadId}.json`;
  try {
    mkdirSync(coverageDirectory, { recursive: true });
  } catch (err) {
    if (err.code !== 'EEXIST') {
      console.error(err);
      return;
    }
  }

  const target = join(coverageDirectory, filename);
  try {
    disableAllAsyncHooks();
    let msg;
    coverageConnection._coverageCallback = function(_msg) {
      msg = _msg;
    };
    coverageConnection.dispatch(JSON.stringify({
      id: 3,
      method: 'Profiler.takePreciseCoverage'
    }));
    const coverageInfo = JSON.parse(msg).result;
    writeFileSync(target, JSON.stringify(coverageInfo));
  } catch (err) {
    console.error(err);
  } finally {
    coverageConnection.disconnect();
    coverageConnection = null;
  }
}

function disableAllAsyncHooks() {
  const { getHookArrays } = require('internal/async_hooks');
  const [hooks_array] = getHookArrays();
  hooks_array.forEach((hook) => { hook.disable(); });
}

exports.writeCoverage = writeCoverage;

function setup() {
  const { hasInspector } = internalBinding('config');
  if (!hasInspector) {
    process._rawDebug('inspector not enabled');
    return;
  }

  const { Connection } = internalBinding('inspector');
  coverageConnection = new Connection((res) => {
    if (coverageConnection._coverageCallback) {
      coverageConnection._coverageCallback(res);
    }
  });
  coverageConnection.dispatch(JSON.stringify({
    id: 1,
    method: 'Profiler.enable'
  }));
  coverageConnection.dispatch(JSON.stringify({
    id: 2,
    method: 'Profiler.startPreciseCoverage',
    params: {
      callCount: true,
      detailed: true
    }
  }));

  try {
    const { cwd } = internalBinding('process_methods');
    const { resolve } = require('path');
    coverageDirectory = process.env.NODE_V8_COVERAGE =
      resolve(cwd(), process.env.NODE_V8_COVERAGE);
  } catch (err) {
    process._rawDebug(err.toString());
  }
}

exports.setup = setup;

function setupExitHooks() {
  const reallyReallyExit = process.reallyExit;
  process.reallyExit = function(code) {
    writeCoverage();
    reallyReallyExit(code);
  };

  process.on('exit', writeCoverage);
}

exports.setupExitHooks = setupExitHooks;