Skip to content

Commit 7b9a184

Browse files
committed
Summarize the max severity at the top of logs, star all max-severity logs
1 parent 0e06340 commit 7b9a184

File tree

4 files changed

+119
-63
lines changed

4 files changed

+119
-63
lines changed

src/common/internal/logging/log_message.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { extractImportantStackTrace } from '../stack.js';
44
export class LogMessageWithStack extends Error {
55
readonly extra: unknown;
66

7+
private showStar: boolean = true;
78
private stackHiddenMessage: string | undefined = undefined;
89

910
constructor(name: string, ex: Error | ErrorWithExtra) {
@@ -16,19 +17,20 @@ export class LogMessageWithStack extends Error {
1617
}
1718
}
1819

19-
/** Set a flag so the stack is not printed in toJSON(). */
20-
setStackHidden(stackHiddenMessage: string) {
21-
this.stackHiddenMessage ??= stackHiddenMessage;
20+
/** Set a flag so the stack is not printed in toJSON(), and important messages are starred. */
21+
setPrintOptions(showStar: boolean, stackHiddenMessage: string) {
22+
this.showStar = showStar;
23+
this.stackHiddenMessage = stackHiddenMessage;
2224
}
2325

2426
toJSON(): string {
25-
let m = this.name;
27+
let m = (this.showStar ? '☆ ' : ' ') + this.name;
2628
if (this.message) m += ': ' + this.message;
2729
if (this.stack) {
2830
if (this.stackHiddenMessage === undefined) {
2931
m += '\n' + extractImportantStackTrace(this);
3032
} else if (this.stackHiddenMessage) {
31-
m += `\n at (elided: ${this.stackHiddenMessage})`;
33+
m += `\n at (elided: ${this.stackHiddenMessage})`;
3234
}
3335
}
3436
return m;

src/common/internal/logging/test_case_recorder.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ enum LogSeverity {
1818
const kMaxLogStacks = 2;
1919
const kMinSeverityForStack = LogSeverity.Warn;
2020

21-
function logSeverityToString(status: LogSeverity): Status {
21+
function logSeverityToString(severity: LogSeverity): string {
22+
return ['NotRun', 'Skip', 'Pass', 'Warn', 'ExpectFailed', 'ValidationFailed', 'ThrewException'][
23+
severity
24+
];
25+
}
26+
27+
function logSeverityToStatus(status: LogSeverity): Status {
2228
switch (status) {
2329
case LogSeverity.NotRun:
2430
return 'notrun';
@@ -42,11 +48,10 @@ export class TestCaseRecorder {
4248
private finalCaseStatus = LogSeverity.NotRun;
4349
private hideStacksBelowSeverity = kMinSeverityForStack;
4450
private startTime = -1;
51+
private preambleLog?: LogMessageWithStack;
4552
private logs: LogMessageWithStack[] = [];
4653
private logLinesAtCurrentSeverity = 0;
4754
private debugging = false;
48-
/** Used to dedup log messages which have identical stacks. */
49-
private messagesForPreviouslySeenStacks = new Map<string, LogMessageWithStack>();
5055

5156
constructor(result: LiveTestCaseResult, debugging: boolean) {
5257
this.result = result;
@@ -72,7 +77,7 @@ export class TestCaseRecorder {
7277
}
7378

7479
// Convert numeric enum back to string (but expose 'exception' as 'fail')
75-
this.result.status = logSeverityToString(this.finalCaseStatus);
80+
this.result.status = logSeverityToStatus(this.finalCaseStatus);
7681

7782
this.result.logs = this.logs;
7883
}
@@ -146,6 +151,18 @@ export class TestCaseRecorder {
146151
this.logImpl(LogSeverity.ThrewException, 'EXCEPTION', ex);
147152
}
148153

154+
private updatePreamble() {
155+
if (this.preambleLog === undefined) {
156+
const preambleError = new Error();
157+
preambleError.stack = undefined;
158+
this.preambleLog = new LogMessageWithStack('SUMMARY', preambleError);
159+
this.logs.unshift(this.preambleLog);
160+
}
161+
this.preambleLog.message = `highest severity is ☆ ${logSeverityToString(
162+
this.hideStacksBelowSeverity
163+
)}`;
164+
}
165+
149166
private logImpl(level: LogSeverity, name: string, baseException: unknown): void {
150167
assert(baseException instanceof Error, 'test threw a non-Error object');
151168
globalTestConfig.testHeartbeatCallback();
@@ -158,25 +175,26 @@ export class TestCaseRecorder {
158175
this.finalCaseStatus = Math.max(this.finalCaseStatus, level);
159176
}
160177

161-
// setFirstLineOnly for all logs except `kMaxLogStacks` stacks at the highest severity
178+
// Hide stack for all logs except `kMaxLogStacks` stacks at the highest severity
162179
if (level > this.hideStacksBelowSeverity) {
163180
this.logLinesAtCurrentSeverity = 0;
164181
this.hideStacksBelowSeverity = level;
182+
this.updatePreamble();
165183

166-
// Go back and setFirstLineOnly for everything of a lower log level
184+
// Go back and hide stack for everything of a lower log level
167185
for (const log of this.logs) {
168-
log.setStackHidden('below max severity');
186+
log.setPrintOptions(false, 'below max severity');
169187
}
170188
}
171189
if (level === this.hideStacksBelowSeverity) {
172190
this.logLinesAtCurrentSeverity++;
191+
if (this.logLinesAtCurrentSeverity > kMaxLogStacks) {
192+
logMessage.setPrintOptions(true, `only ${kMaxLogStacks} shown`);
193+
}
173194
} else if (level < kMinSeverityForStack) {
174-
logMessage.setStackHidden('');
195+
logMessage.setPrintOptions(false, '');
175196
} else if (level < this.hideStacksBelowSeverity) {
176-
logMessage.setStackHidden('below max severity');
177-
}
178-
if (this.logLinesAtCurrentSeverity > kMaxLogStacks) {
179-
logMessage.setStackHidden(`only ${kMaxLogStacks} shown`);
197+
logMessage.setPrintOptions(false, 'below max severity');
180198
}
181199

182200
this.logs.push(logMessage);

src/demo/a/b/c.spec.ts

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -32,49 +32,3 @@ g.test('deep_case_tree')
3232
.combine('z', [1, 2])
3333
)
3434
.fn(() => {});
35-
36-
g.test('statuses,debug').fn(t => {
37-
t.debug('debug');
38-
});
39-
40-
g.test('statuses,skip').fn(t => {
41-
t.skip('skip');
42-
});
43-
44-
g.test('statuses,warn').fn(t => {
45-
t.warn('warn');
46-
});
47-
48-
g.test('statuses,fail').fn(t => {
49-
t.fail('fail');
50-
});
51-
52-
g.test('statuses,throw').fn(() => {
53-
unreachable('unreachable');
54-
});
55-
56-
g.test('multiple_same_stack').fn(t => {
57-
for (let i = 0; i < 3; ++i) {
58-
t.fail(
59-
i === 2
60-
? 'this should appear after deduplicated line'
61-
: 'this should be "seen 2 times with identical stack"'
62-
);
63-
}
64-
});
65-
66-
g.test('multiple_same_level').fn(t => {
67-
t.fail('this should print a stack');
68-
t.fail('this should print a stack');
69-
t.fail('this should not print a stack');
70-
});
71-
72-
g.test('lower_levels_hidden,before').fn(t => {
73-
t.warn('warn - this should not print a stack');
74-
t.fail('fail');
75-
});
76-
77-
g.test('lower_levels_hidden,after').fn(t => {
78-
t.fail('fail');
79-
t.warn('warn - this should not print a stack');
80-
});

src/demo/logs.spec.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
export const description = 'Demos of how log levels show up in log outputs';
2+
3+
import { makeTestGroup } from '../common/framework/test_group.js';
4+
import { unreachable } from '../common/util/util.js';
5+
import { UnitTest } from '../unittests/unit_test.js';
6+
7+
export const g = makeTestGroup(UnitTest);
8+
9+
g.test('statuses,debug').fn(t => {
10+
t.debug('debug');
11+
});
12+
13+
g.test('statuses,skip').fn(t => {
14+
t.skip('skip');
15+
});
16+
17+
g.test('statuses,warn').fn(t => {
18+
t.warn('warn');
19+
});
20+
21+
g.test('statuses,fail').fn(t => {
22+
t.fail('fail');
23+
});
24+
25+
g.test('statuses,throw').fn(() => {
26+
unreachable('unreachable');
27+
});
28+
29+
g.test('multiple_same_level').fn(t => {
30+
t.fail('this should print a stack');
31+
t.fail('this should print a stack');
32+
t.fail('this should not print a stack');
33+
});
34+
35+
g.test('multiple_lower_level').fn(t => {
36+
t.fail('this should print a stack');
37+
t.fail('this should print a stack');
38+
t.fail('this should not print a stack');
39+
t.warn('this should not print a stack');
40+
t.warn('this should not print a stack');
41+
t.warn('this should not print a stack');
42+
});
43+
44+
g.test('lower_levels_hidden,before').fn(t => {
45+
t.warn('warn - this should not print a stack');
46+
t.fail('fail');
47+
});
48+
49+
g.test('lower_levels_hidden,after').fn(t => {
50+
t.fail('fail');
51+
t.warn('warn - this should not print a stack');
52+
});
53+
54+
g.test('exception_over_validation').fn(t => {
55+
t.rec.validationFailed(new Error('first, but lower priority - stack should be elided'));
56+
t.rec.threw(new Error('second, but higher priority - stack should be shown'));
57+
t.rec.validationFailed(new Error('third, lower priority - stack should be elided'));
58+
});
59+
60+
g.test('validation_over_expectation').fn(t => {
61+
t.rec.expectationFailed(new Error('first, but lower priority - stack should be elided'));
62+
t.rec.validationFailed(new Error('second, but higher priority - stack should be shown'));
63+
t.rec.expectationFailed(new Error('third, lower priority - stack should be elided'));
64+
});
65+
66+
g.test('expectation_over_warn').fn(t => {
67+
t.rec.warn(new Error('first, but lower priority - stack should be elided'));
68+
t.rec.expectationFailed(new Error('second, but higher priority - stack should be shown'));
69+
t.rec.warn(new Error('third, lower priority - stack should be elided'));
70+
});
71+
72+
g.test('warn_over_skip').fn(t => {
73+
t.rec.skipped(new Error('stacks are never shown for this level'));
74+
t.rec.warn(new Error('second, but higher priority - stack should be shown'));
75+
t.rec.skipped(new Error('stacks are never shown for this level'));
76+
});
77+
78+
g.test('skip_over_info').fn(t => {
79+
t.rec.info(new Error('stacks are never shown for this level'));
80+
t.rec.skipped(new Error('stacks are never shown for this level'));
81+
t.rec.info(new Error('stacks are never shown for this level'));
82+
});

0 commit comments

Comments
 (0)