-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnodejs_agent_simple.js
164 lines (148 loc) · 4.64 KB
/
nodejs_agent_simple.js
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Coverage server for simple NodeJS applications.
// Simply require it at the top of the main JavaScript file.
const net = require('net');
const runtimeCoverage = require('runtime-coverage');
const {
exit
} = require('process');
const DEBUG_OUTPUT = false
const COVERAGE_PORT = 3001;
const LCOV_HEADER = new Uint8Array([0xc1, 0xc0]);
const FORMAT_VERSION = new Uint8Array([0x10, 0x07]);
const BlockType = {
Header: 0x01,
SessionInfo: 0x10,
CoverageInfo: 0x11,
CmdOk: 0x20,
CmdDump: 0x40,
}
let coverage_started = false;
function log_debug(msg) {
if (DEBUG_OUTPUT) {
console.log("coverage agent: " + msg)
}
}
function concatUInt8Arrays(a1, a2) {
// sum of individual array lengths
let mergedArray = new Uint8Array(a1.length + a2.length);
mergedArray.set(a1);
mergedArray.set(a2, a1.length);
return mergedArray;
}
class Block {
constructor(block_type, block_data) {
this.block_type = block_type;
this.block_data = block_data;
}
to_buffer() {
let buf
if (this.block_type == BlockType.CoverageInfo) {
buf = Buffer.alloc(1 + 4 + this.block_data.length)
buf.writeUInt32LE(this.block_data.length, 1);
buf.fill(this.block_data, 5);
} else if (this.block_type == BlockType.Header) {
buf = Buffer.alloc(1 + LCOV_HEADER.length);
buf.fill(this.block_data, 1);
}
buf[0] = this.block_type;
return buf;
}
}
const server = net.createServer();
server.on('connection', function (socket) {
log_debug('Coverage connection established.');
socket.on('data', async function (buf) {
// buf.subarray(0, 5) = REQUEST_HEADER, check to aid in detecting concurrency bugs.
let header_bytes = buf.subarray(0, 5)
if (Buffer.compare(header_bytes, Buffer.from([0x01, 0xc0, 0xc0, 0x10, 0x07])) != 0) {
log_debug("HEADER BYTES NOT LCOV:")
log_debug(header_bytes)
exit()
}
// buf[5] = command, we only send BLOCK_CMD_DUMP, so this is ignored.
// buf[6] = boolean that indicates whether or not to retrieve coverage (ignore).
// Read RESET PARAMETER
let reset_byte = buf[7];
if (reset_byte == 0) {
log_debug("Warning: coverage agent resets coverage after each coverage request. Reset bit is ignored.");
}
if (!coverage_started) {
log_debug("*** starting coverage, should only happen once ***");
coverage_started = true
await start_coverage();
} else {
let cov_data
try {
cov_data = await get_coverage_delta();
} catch (error) {
log_debug("Error getting coverage:")
log_debug(error)
}
if (cov_data != null) {
let header_data = concatUInt8Arrays(LCOV_HEADER, FORMAT_VERSION);
let header_block = new Block(BlockType.Header, header_data);
let header_block_buf = header_block.to_buffer();
socket.write(header_block_buf);
let coverage_block = new Block(BlockType.CoverageInfo, cov_data)
let cov_block_buf = coverage_block.to_buffer();
socket.write(cov_block_buf);
} else {
log_debug("*** Coverage data is null! ***");
}
}
socket.write(Buffer.from([BlockType.CmdOk]));
});
// When the client requests to end the TCP connection with the server, the server
// ends the connection.
socket.on('end', function () {
log_debug('Closing connection with the client');
});
socket.on('error', function (err) {
log_debug(`Error: ${err}`);
});
});
server.listen(COVERAGE_PORT, '127.0.0.1', function () {
log_debug(`Coverage agent listening on port ${COVERAGE_PORT}`)
});
// Start block-level (=precise) coverage
// This should only be called once, after which execution counters
// are automatically reset on every get_coverage_delta call.
async function start_coverage() {
log_debug("start_coverage")
try {
await runtimeCoverage.startCoverage();
} catch (error) {
log_debug("error starting coverage")
log_debug.log(error)
}
}
// Return LCOV-coverage data since start_coverage or get_coverage_delta was last called.
async function get_coverage_delta() {
const options = {
all: true,
forceReload: false,
return: true,
reporters: ['lcovonly', 'text'],
};
let coverage_promise = runtimeCoverage.getCoverage(options)
let coverage
try {
coverage = await coverage_promise
await runtimeCoverage.startCoverage()
} catch (error) {
log_debug("Problem getting coverage:")
log_debug(error.message);
await start_coverage();
return "coverage not started";
}
if (coverage === "coverage not started") {
return null;
}
log_debug("Coverage is:");
log_debug(coverage["text"]);
return coverage["lcovonly"];
}
module.exports = {
start_coverage,
get_coverage_delta
}