Path: blob/main/src/vs/workbench/contrib/output/test/browser/outputChannelModel.test.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as assert from 'assert';6import { parseLogEntryAt } from '../../common/outputChannelModel.js';7import { TextModel } from '../../../../../editor/common/model/textModel.js';8import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';9import { LogLevel } from '../../../../../platform/log/common/log.js';10import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';11import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';1213suite('Logs Parsing', () => {1415const disposables = ensureNoDisposablesAreLeakedInTestSuite();1617let instantiationService: TestInstantiationService;1819setup(() => {20instantiationService = disposables.add(workbenchInstantiationService({}, disposables));21});2223test('should parse log entry with all components', () => {24const text = '2023-10-15 14:30:45.123 [info] [Git] Initializing repository';25const model = createModel(text);26const entry = parseLogEntryAt(model, 1);2728assert.strictEqual(entry?.timestamp, new Date('2023-10-15 14:30:45.123').getTime());29assert.strictEqual(entry?.logLevel, LogLevel.Info);30assert.strictEqual(entry?.category, 'Git');31assert.strictEqual(model.getValueInRange(entry?.range), text);32});3334test('should parse multi-line log entry', () => {35const text = [36'2023-10-15 14:30:45.123 [error] [Extension] Failed with error:',37'Error: Could not load extension',38' at Object.load (/path/to/file:10:5)'39].join('\n');40const model = createModel(text);41const entry = parseLogEntryAt(model, 1);4243assert.strictEqual(entry?.timestamp, new Date('2023-10-15 14:30:45.123').getTime());44assert.strictEqual(entry?.logLevel, LogLevel.Error);45assert.strictEqual(entry?.category, 'Extension');46assert.strictEqual(model.getValueInRange(entry?.range), text);47});4849test('should parse log entry without category', () => {50const text = '2023-10-15 14:30:45.123 [warning] System is running low on memory';51const model = createModel(text);52const entry = parseLogEntryAt(model, 1);5354assert.strictEqual(entry?.timestamp, new Date('2023-10-15 14:30:45.123').getTime());55assert.strictEqual(entry?.logLevel, LogLevel.Warning);56assert.strictEqual(entry?.category, undefined);57assert.strictEqual(model.getValueInRange(entry?.range), text);58});5960test('should return null for invalid log entry', () => {61const model = createModel('Not a valid log entry');62const entry = parseLogEntryAt(model, 1);6364assert.strictEqual(entry, null);65});6667test('should parse all supported log levels', () => {68const levels = {69info: LogLevel.Info,70trace: LogLevel.Trace,71debug: LogLevel.Debug,72warning: LogLevel.Warning,73error: LogLevel.Error74};7576for (const [levelText, expectedLevel] of Object.entries(levels)) {77const model = createModel(`2023-10-15 14:30:45.123 [${levelText}] Test message`);78const entry = parseLogEntryAt(model, 1);79assert.strictEqual(entry?.logLevel, expectedLevel, `Failed for log level: ${levelText}`);80}81});8283test('should parse timestamp correctly', () => {84const timestamps = [85'2023-01-01 00:00:00.000',86'2023-12-31 23:59:59.999',87'2023-06-15 12:30:45.500'88];8990for (const timestamp of timestamps) {91const model = createModel(`${timestamp} [info] Test message`);92const entry = parseLogEntryAt(model, 1);93assert.strictEqual(entry?.timestamp, new Date(timestamp).getTime(), `Failed for timestamp: ${timestamp}`);94}95});9697test('should handle last line of file', () => {98const model = createModel([99'2023-10-15 14:30:45.123 [info] First message',100'2023-10-15 14:30:45.124 [info] Last message',101''102].join('\n'));103104let actual = parseLogEntryAt(model, 1);105assert.strictEqual(actual?.timestamp, new Date('2023-10-15 14:30:45.123').getTime());106assert.strictEqual(actual?.logLevel, LogLevel.Info);107assert.strictEqual(actual?.category, undefined);108assert.strictEqual(model.getValueInRange(actual?.range), '2023-10-15 14:30:45.123 [info] First message');109110actual = parseLogEntryAt(model, 2);111assert.strictEqual(actual?.timestamp, new Date('2023-10-15 14:30:45.124').getTime());112assert.strictEqual(actual?.logLevel, LogLevel.Info);113assert.strictEqual(actual?.category, undefined);114assert.strictEqual(model.getValueInRange(actual?.range), '2023-10-15 14:30:45.124 [info] Last message');115116actual = parseLogEntryAt(model, 3);117assert.strictEqual(actual, null);118});119120test('should parse multi-line log entry with empty lines', () => {121const text = [122'2025-01-27 09:53:00.450 [info] Found with version <20.18.1>',123'Now using node v20.18.1 (npm v10.8.2)',124'',125'> husky - npm run -s precommit',126'> husky - node v20.18.1',127'',128'Reading git index versions...'129].join('\n');130const model = createModel(text);131const entry = parseLogEntryAt(model, 1);132133assert.strictEqual(entry?.timestamp, new Date('2025-01-27 09:53:00.450').getTime());134assert.strictEqual(entry?.logLevel, LogLevel.Info);135assert.strictEqual(entry?.category, undefined);136assert.strictEqual(model.getValueInRange(entry?.range), text);137138});139140function createModel(content: string): TextModel {141return disposables.add(instantiationService.createInstance(TextModel, content, 'log', TextModel.DEFAULT_CREATION_OPTIONS, null));142}143});144145146