mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
The single file which represented the bulk of the `meteor self-test` functionality had got a bit heavy and it stood to benefit from some dissemination. I embarked on this change originally when looking into replacing PhantomJS with Chrome Headless (and a new `ChromeClient` class) within self-test. Unfortunately, I didn't have time to take this the last step of actually implementing Chrome, but this should hopefully facilitate that change in the future by providing what I believe to be better compartmentalization of this logic. I apologize for the (likely) difficulty of reviewing this commit. Due to heavy intertwining of existing code it was hard to arrange these changes in an easy-to-review manner. I believe a reviewer will find that it's mainly copy and pasting into different files and careful adjusting of those files (new) module dependencies.
89 lines
2.3 KiB
JavaScript
89 lines
2.3 KiB
JavaScript
// 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) {
|
|
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 new TestFailure('forbidden-string-present', { run: this.run });
|
|
}
|
|
});
|
|
}
|
|
|
|
get() {
|
|
return this.lines;
|
|
}
|
|
}
|