Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/test/browser/repl.test.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
7
import assert from 'assert';
8
import { TreeVisibility } from '../../../../../base/browser/ui/tree/tree.js';
9
import { timeout } from '../../../../../base/common/async.js';
10
import severity from '../../../../../base/common/severity.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
12
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
13
import { RawDebugSession } from '../../browser/rawDebugSession.js';
14
import { ReplFilter } from '../../browser/replFilter.js';
15
import { DebugModel, StackFrame, Thread } from '../../common/debugModel.js';
16
import { RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult, ReplGroup, ReplModel, ReplOutputElement, ReplVariableElement } from '../../common/replModel.js';
17
import { createTestSession } from './callStack.test.js';
18
import { createMockDebugModel } from './mockDebugModel.js';
19
import { MockDebugAdapter, MockRawSession } from '../common/mockDebug.js';
20
21
suite('Debug - REPL', () => {
22
let model: DebugModel;
23
let rawSession: MockRawSession;
24
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
25
const configurationService = new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } });
26
27
setup(() => {
28
model = createMockDebugModel(disposables);
29
rawSession = new MockRawSession();
30
});
31
32
test('repl output', () => {
33
const session = disposables.add(createTestSession(model));
34
const repl = new ReplModel(configurationService);
35
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Error });
36
repl.appendToRepl(session, { output: 'second line ', sev: severity.Error });
37
repl.appendToRepl(session, { output: 'third line ', sev: severity.Error });
38
repl.appendToRepl(session, { output: 'fourth line', sev: severity.Error });
39
40
let elements = <ReplOutputElement[]>repl.getReplElements();
41
assert.strictEqual(elements.length, 2);
42
assert.strictEqual(elements[0].value, 'first line\n');
43
assert.strictEqual(elements[0].severity, severity.Error);
44
assert.strictEqual(elements[1].value, 'second line third line fourth line');
45
assert.strictEqual(elements[1].severity, severity.Error);
46
47
repl.appendToRepl(session, { output: '1', sev: severity.Warning });
48
elements = <ReplOutputElement[]>repl.getReplElements();
49
assert.strictEqual(elements.length, 3);
50
assert.strictEqual(elements[2].value, '1');
51
assert.strictEqual(elements[2].severity, severity.Warning);
52
53
const keyValueObject = { 'key1': 2, 'key2': 'value' };
54
repl.appendToRepl(session, { output: '', expression: new RawObjectReplElement('fakeid', 'fake', keyValueObject), sev: severity.Info });
55
const element = <ReplVariableElement>repl.getReplElements()[3];
56
assert.strictEqual(element.expression.value, 'Object');
57
assert.deepStrictEqual((element.expression as RawObjectReplElement).valueObj, keyValueObject);
58
59
repl.removeReplExpressions();
60
assert.strictEqual(repl.getReplElements().length, 0);
61
62
repl.appendToRepl(session, { output: '1\n', sev: severity.Info });
63
repl.appendToRepl(session, { output: '2', sev: severity.Info });
64
repl.appendToRepl(session, { output: '3\n4', sev: severity.Info });
65
repl.appendToRepl(session, { output: '5\n', sev: severity.Info });
66
repl.appendToRepl(session, { output: '6', sev: severity.Info });
67
elements = <ReplOutputElement[]>repl.getReplElements();
68
assert.deepStrictEqual(elements.map(e => e.toString()), ['1\n', '23\n', '45\n', '6']);
69
70
repl.removeReplExpressions();
71
});
72
73
test('repl output count', () => {
74
const session = disposables.add(createTestSession(model));
75
const repl = new ReplModel(configurationService);
76
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info });
77
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info });
78
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info });
79
repl.appendToRepl(session, { output: 'second line\n', sev: severity.Info });
80
repl.appendToRepl(session, { output: 'second line\n', sev: severity.Info });
81
repl.appendToRepl(session, { output: 'third line\n', sev: severity.Info });
82
const elements = <ReplOutputElement[]>repl.getReplElements();
83
assert.deepStrictEqual(elements.map(e => ({ value: e.value, count: e.count })), [
84
{ value: 'first line\n', count: 3 },
85
{ value: 'second line\n', count: 2 },
86
{ value: 'third line\n', count: 1 }
87
]);
88
});
89
90
test('repl merging', () => {
91
// 'mergeWithParent' should be ignored when there is no parent.
92
const parent = disposables.add(createTestSession(model, 'parent', { repl: 'mergeWithParent' }));
93
const child1 = disposables.add(createTestSession(model, 'child1', { parentSession: parent, repl: 'separate' }));
94
const child2 = disposables.add(createTestSession(model, 'child2', { parentSession: parent, repl: 'mergeWithParent' }));
95
const grandChild = disposables.add(createTestSession(model, 'grandChild', { parentSession: child2, repl: 'mergeWithParent' }));
96
const child3 = disposables.add(createTestSession(model, 'child3', { parentSession: parent }));
97
98
let parentChanges = 0;
99
disposables.add(parent.onDidChangeReplElements(() => ++parentChanges));
100
101
parent.appendToRepl({ output: '1\n', sev: severity.Info });
102
assert.strictEqual(parentChanges, 1);
103
assert.strictEqual(parent.getReplElements().length, 1);
104
assert.strictEqual(child1.getReplElements().length, 0);
105
assert.strictEqual(child2.getReplElements().length, 1);
106
assert.strictEqual(grandChild.getReplElements().length, 1);
107
assert.strictEqual(child3.getReplElements().length, 0);
108
109
grandChild.appendToRepl({ output: '2\n', sev: severity.Info });
110
assert.strictEqual(parentChanges, 2);
111
assert.strictEqual(parent.getReplElements().length, 2);
112
assert.strictEqual(child1.getReplElements().length, 0);
113
assert.strictEqual(child2.getReplElements().length, 2);
114
assert.strictEqual(grandChild.getReplElements().length, 2);
115
assert.strictEqual(child3.getReplElements().length, 0);
116
117
child3.appendToRepl({ output: '3\n', sev: severity.Info });
118
assert.strictEqual(parentChanges, 2);
119
assert.strictEqual(parent.getReplElements().length, 2);
120
assert.strictEqual(child1.getReplElements().length, 0);
121
assert.strictEqual(child2.getReplElements().length, 2);
122
assert.strictEqual(grandChild.getReplElements().length, 2);
123
assert.strictEqual(child3.getReplElements().length, 1);
124
125
child1.appendToRepl({ output: '4\n', sev: severity.Info });
126
assert.strictEqual(parentChanges, 2);
127
assert.strictEqual(parent.getReplElements().length, 2);
128
assert.strictEqual(child1.getReplElements().length, 1);
129
assert.strictEqual(child2.getReplElements().length, 2);
130
assert.strictEqual(grandChild.getReplElements().length, 2);
131
assert.strictEqual(child3.getReplElements().length, 1);
132
});
133
134
test('repl expressions', () => {
135
const session = disposables.add(createTestSession(model));
136
assert.strictEqual(session.getReplElements().length, 0);
137
model.addSession(session);
138
139
// eslint-disable-next-line local/code-no-any-casts
140
session['raw'] = <any>rawSession;
141
const thread = new Thread(session, 'mockthread', 1);
142
// eslint-disable-next-line local/code-no-any-casts
143
const stackFrame = new StackFrame(thread, 1, <any>undefined, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1, true);
144
const replModel = new ReplModel(configurationService);
145
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
146
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
147
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
148
149
assert.strictEqual(replModel.getReplElements().length, 3);
150
replModel.getReplElements().forEach(re => {
151
assert.strictEqual((<ReplEvaluationInput>re).value, 'myVariable');
152
});
153
154
replModel.removeReplExpressions();
155
assert.strictEqual(replModel.getReplElements().length, 0);
156
});
157
158
test('repl ordering', async () => {
159
const session = disposables.add(createTestSession(model));
160
model.addSession(session);
161
162
const adapter = new MockDebugAdapter();
163
const raw = disposables.add(new RawDebugSession(adapter, undefined!, '', '', undefined!, undefined!, undefined!, undefined!,));
164
session.initializeForTest(raw);
165
166
await session.addReplExpression(undefined, 'before.1');
167
assert.strictEqual(session.getReplElements().length, 3);
168
assert.strictEqual((<ReplEvaluationInput>session.getReplElements()[0]).value, 'before.1');
169
assert.strictEqual((<ReplOutputElement>session.getReplElements()[1]).value, 'before.1');
170
assert.strictEqual((<ReplEvaluationResult>session.getReplElements()[2]).value, '=before.1');
171
172
await session.addReplExpression(undefined, 'after.2');
173
await timeout(0);
174
assert.strictEqual(session.getReplElements().length, 6);
175
assert.strictEqual((<ReplEvaluationInput>session.getReplElements()[3]).value, 'after.2');
176
assert.strictEqual((<ReplEvaluationResult>session.getReplElements()[4]).value, '=after.2');
177
assert.strictEqual((<ReplOutputElement>session.getReplElements()[5]).value, 'after.2');
178
});
179
180
test('repl groups', async () => {
181
const session = disposables.add(createTestSession(model));
182
const repl = new ReplModel(configurationService);
183
184
repl.appendToRepl(session, { output: 'first global line', sev: severity.Info });
185
repl.startGroup(session, 'group_1', true);
186
repl.appendToRepl(session, { output: 'first line in group', sev: severity.Info });
187
repl.appendToRepl(session, { output: 'second line in group', sev: severity.Info });
188
const elements = repl.getReplElements();
189
assert.strictEqual(elements.length, 2);
190
const group = elements[1] as ReplGroup;
191
assert.strictEqual(group.name, 'group_1');
192
assert.strictEqual(group.autoExpand, true);
193
assert.strictEqual(group.hasChildren, true);
194
assert.strictEqual(group.hasEnded, false);
195
196
repl.startGroup(session, 'group_2', false);
197
repl.appendToRepl(session, { output: 'first line in subgroup', sev: severity.Info });
198
repl.appendToRepl(session, { output: 'second line in subgroup', sev: severity.Info });
199
const children = group.getChildren();
200
assert.strictEqual(children.length, 3);
201
assert.strictEqual((<ReplOutputElement>children[0]).value, 'first line in group');
202
assert.strictEqual((<ReplOutputElement>children[1]).value, 'second line in group');
203
assert.strictEqual((<ReplGroup>children[2]).name, 'group_2');
204
assert.strictEqual((<ReplGroup>children[2]).hasEnded, false);
205
assert.strictEqual((<ReplGroup>children[2]).getChildren().length, 2);
206
repl.endGroup();
207
assert.strictEqual((<ReplGroup>children[2]).hasEnded, true);
208
repl.appendToRepl(session, { output: 'third line in group', sev: severity.Info });
209
assert.strictEqual(group.getChildren().length, 4);
210
assert.strictEqual(group.hasEnded, false);
211
repl.endGroup();
212
assert.strictEqual(group.hasEnded, true);
213
repl.appendToRepl(session, { output: 'second global line', sev: severity.Info });
214
assert.strictEqual(repl.getReplElements().length, 3);
215
assert.strictEqual((<ReplOutputElement>repl.getReplElements()[2]).value, 'second global line');
216
});
217
218
test('repl identical line collapsing - character by character', () => {
219
const session = disposables.add(createTestSession(model));
220
const repl = new ReplModel(configurationService);
221
222
// Test case 1: Character-by-character output should NOT be collapsed
223
// These should print "111\n", not "(3)1"
224
repl.appendToRepl(session, { output: '1', sev: severity.Info });
225
repl.appendToRepl(session, { output: '1', sev: severity.Info });
226
repl.appendToRepl(session, { output: '1', sev: severity.Info });
227
repl.appendToRepl(session, { output: '\n', sev: severity.Info });
228
229
let elements = <ReplOutputElement[]>repl.getReplElements();
230
// Should be one element with "111\n" value, not collapsed
231
assert.strictEqual(elements.length, 1);
232
assert.strictEqual(elements[0].value, '111\n');
233
assert.strictEqual(elements[0].count, 1);
234
235
repl.removeReplExpressions();
236
237
// Test case 2: Character-by-character with mixed output
238
repl.appendToRepl(session, { output: '5', sev: severity.Info });
239
repl.appendToRepl(session, { output: '5', sev: severity.Info });
240
repl.appendToRepl(session, { output: '\n', sev: severity.Info });
241
242
elements = <ReplOutputElement[]>repl.getReplElements();
243
// Should be one element with "55\n" value, not "(2)5"
244
assert.strictEqual(elements.length, 1);
245
assert.strictEqual(elements[0].value, '55\n');
246
assert.strictEqual(elements[0].count, 1);
247
});
248
249
test('repl identical line collapsing - single event multiple lines', () => {
250
const session = disposables.add(createTestSession(model));
251
const repl = new ReplModel(configurationService);
252
253
// Test case: Single event with multiple identical lines should be collapsed
254
// This should be collapsed into "(2)hello"
255
repl.appendToRepl(session, { output: 'hello\nhello\n', sev: severity.Info });
256
257
const elements = <ReplOutputElement[]>repl.getReplElements();
258
// Should be one collapsed element with count 2
259
assert.strictEqual(elements.length, 1);
260
assert.strictEqual(elements[0].value, 'hello\n');
261
assert.strictEqual(elements[0].count, 2);
262
});
263
264
test('repl identical line collapsing - mixed scenarios', () => {
265
const session = disposables.add(createTestSession(model));
266
const repl = new ReplModel(configurationService);
267
268
// Test case: Mix of single events and multi-line events
269
repl.appendToRepl(session, { output: 'test\n', sev: severity.Info });
270
repl.appendToRepl(session, { output: 'test\ntest\n', sev: severity.Info });
271
272
const elements = <ReplOutputElement[]>repl.getReplElements();
273
// Should be one collapsed element with count 3
274
assert.strictEqual(elements.length, 1);
275
assert.strictEqual(elements[0].value, 'test\n');
276
assert.strictEqual(elements[0].count, 3);
277
});
278
279
test('repl filter', async () => {
280
const session = disposables.add(createTestSession(model));
281
const repl = new ReplModel(configurationService);
282
const replFilter = new ReplFilter();
283
284
const getFilteredElements = (): ReplOutputElement[] => {
285
const elements = repl.getReplElements();
286
return elements.filter((e): e is ReplOutputElement => {
287
const filterResult = replFilter.filter(e, TreeVisibility.Visible);
288
return filterResult === true || filterResult === TreeVisibility.Visible;
289
});
290
};
291
292
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info });
293
repl.appendToRepl(session, { output: 'second line\n', sev: severity.Info });
294
repl.appendToRepl(session, { output: 'third line\n', sev: severity.Info });
295
repl.appendToRepl(session, { output: 'fourth line\n', sev: severity.Info });
296
297
replFilter.filterQuery = 'first';
298
const r1 = getFilteredElements();
299
assert.strictEqual(r1.length, 1);
300
assert.strictEqual(r1[0].value, 'first line\n');
301
302
replFilter.filterQuery = '!first';
303
const r2 = getFilteredElements();
304
assert.strictEqual(r1.length, 1);
305
assert.strictEqual(r2[0].value, 'second line\n');
306
assert.strictEqual(r2[1].value, 'third line\n');
307
assert.strictEqual(r2[2].value, 'fourth line\n');
308
309
replFilter.filterQuery = 'first, line';
310
const r3 = getFilteredElements();
311
assert.strictEqual(r3.length, 4);
312
assert.strictEqual(r3[0].value, 'first line\n');
313
assert.strictEqual(r3[1].value, 'second line\n');
314
assert.strictEqual(r3[2].value, 'third line\n');
315
assert.strictEqual(r3[3].value, 'fourth line\n');
316
317
replFilter.filterQuery = 'line, !second';
318
const r4 = getFilteredElements();
319
assert.strictEqual(r4.length, 3);
320
assert.strictEqual(r4[0].value, 'first line\n');
321
assert.strictEqual(r4[1].value, 'third line\n');
322
assert.strictEqual(r4[2].value, 'fourth line\n');
323
324
replFilter.filterQuery = '!second, line';
325
const r4_same = getFilteredElements();
326
assert.strictEqual(r4.length, r4_same.length);
327
328
replFilter.filterQuery = '!line';
329
const r5 = getFilteredElements();
330
assert.strictEqual(r5.length, 0);
331
332
replFilter.filterQuery = 'smth';
333
const r6 = getFilteredElements();
334
assert.strictEqual(r6.length, 0);
335
});
336
});
337
338