tools/tool-testing/output-log.js
// Maintains a line-by-line merged log of multiple output channels
// (eg, stdout and stderr).
import TestFailure from './test-failure.js';
const hasOwn = Object.prototype.hasOwnProperty;
export default class OutputLog {
constructor(run) {
// each entry is an object withgit p keys 'channel', 'text', and if it is
// the last entry and there was no newline terminator, 'bare'
this.lines = [];
// map from a channel name to an object representing a partially
// read line of text on that channel. That object has keys 'text'
// (text read), 'offset' (cursor position, equal to text.length
// unless a '\r' has been read).
this.buffers = {};
// a Run, exclusively for inclusion in exceptions
this.run = run;
}
write(channel, text) {
if (!hasOwn.call(this.buffers, 'channel')) {
this.buffers[channel] = { text: '', offset: 0 };
}
const b = this.buffers[channel];
while (text.length) {
const m = text.match(/^[^\n\r]+/);
if (m) {
// A run of non-control characters.
b.text = b.text.substr(0, b.offset) +
m[0] + b.text.substr(b.offset + m[0].length);
b.offset += m[0].length;
text = text.substr(m[0].length);
continue;
}
if (text[0] === '\r') {
b.offset = 0;
text = text.substr(1);
continue;
}
if (text[0] === '\n') {
this.lines.push({ channel, text: b.text });
b.text = '';
b.offset = 0;
text = text.substr(1);
continue;
}
throw new Error("conditions should have been exhaustive?");
}
}
end() {
Object.keys(this.buffers).forEach((channel) => {
if (this.buffers[channel].text.length) {
this.lines.push({
channel,
text: this.buffers[channel].text,
bare: true,
});
this.buffers[channel] = { text: '', offset: 0};
}
});
}
forbid(pattern, channel) {
const failure = new TestFailure('forbidden-string-present', {
run: this.run,
});
this.lines.forEach((line) => {
if (channel && channel !== line.channel) {
return;
}
const match = (pattern instanceof RegExp) ?
(line.text.match(pattern)) : (line.text.indexOf(pattern) !== -1);
if (match) {
throw failure;
}
});
}
get() {
return this.lines;
}
}
import { markThrowingMethods } from "./test-utils.js";
markThrowingMethods(OutputLog.prototype);