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
3296 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
session['raw'] = <any>rawSession;
140
const thread = new Thread(session, 'mockthread', 1);
141
const stackFrame = new StackFrame(thread, 1, <any>undefined, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1, true);
142
const replModel = new ReplModel(configurationService);
143
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
144
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
145
replModel.addReplExpression(session, stackFrame, 'myVariable').then();
146
147
assert.strictEqual(replModel.getReplElements().length, 3);
148
replModel.getReplElements().forEach(re => {
149
assert.strictEqual((<ReplEvaluationInput>re).value, 'myVariable');
150
});
151
152
replModel.removeReplExpressions();
153
assert.strictEqual(replModel.getReplElements().length, 0);
154
});
155
156
test('repl ordering', async () => {
157
const session = disposables.add(createTestSession(model));
158
model.addSession(session);
159
160
const adapter = new MockDebugAdapter();
161
const raw = disposables.add(new RawDebugSession(adapter, undefined!, '', '', undefined!, undefined!, undefined!, undefined!,));
162
session.initializeForTest(raw);
163
164
await session.addReplExpression(undefined, 'before.1');
165
assert.strictEqual(session.getReplElements().length, 3);
166
assert.strictEqual((<ReplEvaluationInput>session.getReplElements()[0]).value, 'before.1');
167
assert.strictEqual((<ReplOutputElement>session.getReplElements()[1]).value, 'before.1');
168
assert.strictEqual((<ReplEvaluationResult>session.getReplElements()[2]).value, '=before.1');
169
170
await session.addReplExpression(undefined, 'after.2');
171
await timeout(0);
172
assert.strictEqual(session.getReplElements().length, 6);
173
assert.strictEqual((<ReplEvaluationInput>session.getReplElements()[3]).value, 'after.2');
174
assert.strictEqual((<ReplEvaluationResult>session.getReplElements()[4]).value, '=after.2');
175
assert.strictEqual((<ReplOutputElement>session.getReplElements()[5]).value, 'after.2');
176
});
177
178
test('repl groups', async () => {
179
const session = disposables.add(createTestSession(model));
180
const repl = new ReplModel(configurationService);
181
182
repl.appendToRepl(session, { output: 'first global line', sev: severity.Info });
183
repl.startGroup(session, 'group_1', true);
184
repl.appendToRepl(session, { output: 'first line in group', sev: severity.Info });
185
repl.appendToRepl(session, { output: 'second line in group', sev: severity.Info });
186
const elements = repl.getReplElements();
187
assert.strictEqual(elements.length, 2);
188
const group = elements[1] as ReplGroup;
189
assert.strictEqual(group.name, 'group_1');
190
assert.strictEqual(group.autoExpand, true);
191
assert.strictEqual(group.hasChildren, true);
192
assert.strictEqual(group.hasEnded, false);
193
194
repl.startGroup(session, 'group_2', false);
195
repl.appendToRepl(session, { output: 'first line in subgroup', sev: severity.Info });
196
repl.appendToRepl(session, { output: 'second line in subgroup', sev: severity.Info });
197
const children = group.getChildren();
198
assert.strictEqual(children.length, 3);
199
assert.strictEqual((<ReplOutputElement>children[0]).value, 'first line in group');
200
assert.strictEqual((<ReplOutputElement>children[1]).value, 'second line in group');
201
assert.strictEqual((<ReplGroup>children[2]).name, 'group_2');
202
assert.strictEqual((<ReplGroup>children[2]).hasEnded, false);
203
assert.strictEqual((<ReplGroup>children[2]).getChildren().length, 2);
204
repl.endGroup();
205
assert.strictEqual((<ReplGroup>children[2]).hasEnded, true);
206
repl.appendToRepl(session, { output: 'third line in group', sev: severity.Info });
207
assert.strictEqual(group.getChildren().length, 4);
208
assert.strictEqual(group.hasEnded, false);
209
repl.endGroup();
210
assert.strictEqual(group.hasEnded, true);
211
repl.appendToRepl(session, { output: 'second global line', sev: severity.Info });
212
assert.strictEqual(repl.getReplElements().length, 3);
213
assert.strictEqual((<ReplOutputElement>repl.getReplElements()[2]).value, 'second global line');
214
});
215
216
test('repl identical line collapsing - character by character', () => {
217
const session = disposables.add(createTestSession(model));
218
const repl = new ReplModel(configurationService);
219
220
// Test case 1: Character-by-character output should NOT be collapsed
221
// These should print "111\n", not "(3)1"
222
repl.appendToRepl(session, { output: '1', sev: severity.Info });
223
repl.appendToRepl(session, { output: '1', sev: severity.Info });
224
repl.appendToRepl(session, { output: '1', sev: severity.Info });
225
repl.appendToRepl(session, { output: '\n', sev: severity.Info });
226
227
let elements = <ReplOutputElement[]>repl.getReplElements();
228
// Should be one element with "111\n" value, not collapsed
229
assert.strictEqual(elements.length, 1);
230
assert.strictEqual(elements[0].value, '111\n');
231
assert.strictEqual(elements[0].count, 1);
232
233
repl.removeReplExpressions();
234
235
// Test case 2: Character-by-character with mixed output
236
repl.appendToRepl(session, { output: '5', sev: severity.Info });
237
repl.appendToRepl(session, { output: '5', sev: severity.Info });
238
repl.appendToRepl(session, { output: '\n', sev: severity.Info });
239
240
elements = <ReplOutputElement[]>repl.getReplElements();
241
// Should be one element with "55\n" value, not "(2)5"
242
assert.strictEqual(elements.length, 1);
243
assert.strictEqual(elements[0].value, '55\n');
244
assert.strictEqual(elements[0].count, 1);
245
});
246
247
test('repl identical line collapsing - single event multiple lines', () => {
248
const session = disposables.add(createTestSession(model));
249
const repl = new ReplModel(configurationService);
250
251
// Test case: Single event with multiple identical lines should be collapsed
252
// This should be collapsed into "(2)hello"
253
repl.appendToRepl(session, { output: 'hello\nhello\n', sev: severity.Info });
254
255
const elements = <ReplOutputElement[]>repl.getReplElements();
256
// Should be one collapsed element with count 2
257
assert.strictEqual(elements.length, 1);
258
assert.strictEqual(elements[0].value, 'hello\n');
259
assert.strictEqual(elements[0].count, 2);
260
});
261
262
test('repl identical line collapsing - mixed scenarios', () => {
263
const session = disposables.add(createTestSession(model));
264
const repl = new ReplModel(configurationService);
265
266
// Test case: Mix of single events and multi-line events
267
repl.appendToRepl(session, { output: 'test\n', sev: severity.Info });
268
repl.appendToRepl(session, { output: 'test\ntest\n', sev: severity.Info });
269
270
const elements = <ReplOutputElement[]>repl.getReplElements();
271
// Should be one collapsed element with count 3
272
assert.strictEqual(elements.length, 1);
273
assert.strictEqual(elements[0].value, 'test\n');
274
assert.strictEqual(elements[0].count, 3);
275
});
276
277
test('repl filter', async () => {
278
const session = disposables.add(createTestSession(model));
279
const repl = new ReplModel(configurationService);
280
const replFilter = new ReplFilter();
281
282
const getFilteredElements = (): ReplOutputElement[] => {
283
const elements = repl.getReplElements();
284
return elements.filter((e): e is ReplOutputElement => {
285
const filterResult = replFilter.filter(e, TreeVisibility.Visible);
286
return filterResult === true || filterResult === TreeVisibility.Visible;
287
});
288
};
289
290
repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info });
291
repl.appendToRepl(session, { output: 'second line\n', sev: severity.Info });
292
repl.appendToRepl(session, { output: 'third line\n', sev: severity.Info });
293
repl.appendToRepl(session, { output: 'fourth line\n', sev: severity.Info });
294
295
replFilter.filterQuery = 'first';
296
const r1 = getFilteredElements();
297
assert.strictEqual(r1.length, 1);
298
assert.strictEqual(r1[0].value, 'first line\n');
299
300
replFilter.filterQuery = '!first';
301
const r2 = getFilteredElements();
302
assert.strictEqual(r1.length, 1);
303
assert.strictEqual(r2[0].value, 'second line\n');
304
assert.strictEqual(r2[1].value, 'third line\n');
305
assert.strictEqual(r2[2].value, 'fourth line\n');
306
307
replFilter.filterQuery = 'first, line';
308
const r3 = getFilteredElements();
309
assert.strictEqual(r3.length, 4);
310
assert.strictEqual(r3[0].value, 'first line\n');
311
assert.strictEqual(r3[1].value, 'second line\n');
312
assert.strictEqual(r3[2].value, 'third line\n');
313
assert.strictEqual(r3[3].value, 'fourth line\n');
314
315
replFilter.filterQuery = 'line, !second';
316
const r4 = getFilteredElements();
317
assert.strictEqual(r4.length, 3);
318
assert.strictEqual(r4[0].value, 'first line\n');
319
assert.strictEqual(r4[1].value, 'third line\n');
320
assert.strictEqual(r4[2].value, 'fourth line\n');
321
322
replFilter.filterQuery = '!second, line';
323
const r4_same = getFilteredElements();
324
assert.strictEqual(r4.length, r4_same.length);
325
326
replFilter.filterQuery = '!line';
327
const r5 = getFilteredElements();
328
assert.strictEqual(r5.length, 0);
329
330
replFilter.filterQuery = 'smth';
331
const r6 = getFilteredElements();
332
assert.strictEqual(r6.length, 0);
333
});
334
});
335
336