Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/simulation/notebooks.stest.ts
13388 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
import assert from 'assert';
7
import * as fs from 'fs';
8
import { EOL } from 'os';
9
import * as path from 'path';
10
import type { NotebookDocument } from 'vscode';
11
import { Intent } from '../../src/extension/common/constants';
12
import { IDiffService } from '../../src/platform/diff/common/diffService';
13
import { DiffServiceImpl } from '../../src/platform/diff/node/diffServiceImpl';
14
import { IAlternativeNotebookContentService } from '../../src/platform/notebook/common/alternativeContent';
15
import { AlternativeNotebookContentEditGenerator, IAlternativeNotebookContentEditGenerator } from '../../src/platform/notebook/common/alternativeContentEditGenerator';
16
import { AlternativeNotebookFormat } from '../../src/platform/notebook/common/alternativeContentFormat';
17
import { MockAlternativeNotebookContentService } from '../../src/platform/notebook/common/mockAlternativeContentService';
18
import { INotebookService, VariablesResult } from '../../src/platform/notebook/common/notebookService';
19
import { ITestingServicesAccessor } from '../../src/platform/test/node/services';
20
import { IFile, SimulationWorkspace } from '../../src/platform/test/node/simulationWorkspace';
21
import { SimulationAlternativeNotebookContentService, SimulationNotebookService } from '../../src/platform/test/node/simulationWorkspaceServices';
22
import { ExtHostNotebookDocumentData } from '../../src/util/common/test/shims/notebookDocument';
23
import { DisposableStore } from '../../src/util/vs/base/common/lifecycle';
24
import { ResourceMap } from '../../src/util/vs/base/common/map';
25
import { Schemas } from '../../src/util/vs/base/common/network';
26
import { URI } from '../../src/util/vs/base/common/uri';
27
import { SyncDescriptor } from '../../src/util/vs/platform/instantiation/common/descriptors';
28
import { ssuite, stest } from '../base/stest';
29
import { getDiagnostics } from './diagnosticProviders';
30
import { DiagnosticsProvider, ITestDiagnostic } from './diagnosticProviders/diagnosticsProvider';
31
import { canExecutePythonCodeWithoutErrors, isValidPythonFile } from './diagnosticProviders/python';
32
import { simulateInlineChat } from './inlineChatSimulator';
33
import { fromFixture, getFixturesDir } from './stestUtil';
34
import { DiagnosticProviderId, IOutcome, IScenario } from './types';
35
36
export function fromNotebookFixture(pathOrDirnameWithinFixturesDir: string, activeCell?: number /** when provided, code in other cells will be emptied */) {
37
const filePath: string = path.join(getFixturesDir(), pathOrDirnameWithinFixturesDir);
38
const baseDirname: string = path.dirname(filePath);
39
const fileName = path.relative(baseDirname, filePath);
40
const fileContents = fs.readFileSync(filePath).toString();
41
42
try {
43
const notebook = JSON.parse(fileContents);
44
const cells = notebook.cells as any[];
45
notebook.cells = cells.map((cell, index) => {
46
if (index !== activeCell) {
47
return {
48
...cell,
49
source: ['']
50
};
51
} else {
52
return cell;
53
}
54
});
55
56
return { kind: 'relativeFile' as const, fileName, fileContents: JSON.stringify(notebook, undefined, 2) };
57
} catch {
58
return { kind: 'relativeFile' as const, fileName, fileContents };
59
}
60
}
61
62
ssuite({ title: 'notebook', subtitle: 'edit', location: 'inline' }, () => {
63
64
stest({ description: 'variables', language: 'python' }, (testingServiceCollection) => {
65
return simulateInlineChat(testingServiceCollection, {
66
files: [fromFixture('notebook/variables.ipynb')],
67
queries: [
68
{
69
file: 'variables.ipynb',
70
activeCell: 0,
71
selection: [2, 0, 2, 0],
72
query: 'print seconds in a week',
73
expectedIntent: 'edit',
74
validate: async (outcome, workspace, accessor) => {
75
assert.strictEqual(outcome.type, 'inlineEdit');
76
assert.ok(/print\(seconds_in_a_week/.test(outcome.fileContents));
77
}
78
}
79
]
80
});
81
});
82
83
stest({ description: 'dataframe', language: 'python' }, (testingServiceCollection) => {
84
return simulateInlineChat(testingServiceCollection, {
85
files: [fromFixture('notebook/dataframe.ipynb')],
86
queries: [
87
{
88
file: 'dataframe.ipynb',
89
activeCell: 2,
90
selection: [0, 0, 0, 0],
91
query: 'add a new column called adjusted to the dataframe and set it to the value of the activity column minus 2',
92
expectedIntent: 'edit',
93
validate: async (outcome, workspace, accessor) => {
94
assert.strictEqual(outcome.type, 'inlineEdit');
95
assert.ok(outcome.fileContents.includes('my_dataframe[\'adjusted\']') || outcome.fileContents.includes('my_dataframe[\"adjusted\"]'));
96
assert.ok(!outcome.fileContents.includes('import'));
97
}
98
}
99
]
100
});
101
});
102
103
stest({ description: 'data cleansing', language: 'python' }, (testingServiceCollection) => {
104
return simulateInlineChat(testingServiceCollection, {
105
files: [fromFixture('notebook/datacleansing.ipynb')],
106
queries: [
107
{
108
file: 'datacleansing.ipynb',
109
activeCell: 2,
110
selection: [0, 0, 0, 0],
111
query: 'check for missing values',
112
expectedIntent: 'edit',
113
validate: async (outcome, workspace, accessor) => {
114
assert.strictEqual(outcome.type, 'inlineEdit');
115
assert.ok(!outcome.fileContents.includes('import'));
116
assert.ok(outcome.fileContents.includes('mydf'));
117
assert.ok(outcome.fileContents.includes('isnull') || outcome.fileContents.includes('dropna'));
118
}
119
}
120
]
121
});
122
});
123
124
stest({ description: 'plot', language: 'python' }, (testingServiceCollection) => {
125
return simulateInlineChat(testingServiceCollection, {
126
files: [fromFixture('notebook/plot.ipynb')],
127
queries: [
128
{
129
file: 'plot.ipynb',
130
activeCell: 1,
131
selection: [0, 0, 0, 0],
132
query: 'plot the data frame',
133
expectedIntent: 'edit',
134
validate: async (outcome, workspace, accessor) => {
135
assert.strictEqual(outcome.type, 'inlineEdit');
136
assert.ok(!outcome.fileContents.includes('import'));
137
assert.ok(outcome.fileContents.includes('df.plot') || outcome.fileContents.includes('px.bar'));
138
}
139
}
140
]
141
});
142
});
143
144
stest({ description: '/fix notebook exection ImportError', language: 'python' }, (testingServiceCollection) => {
145
return simulateInlineChat(testingServiceCollection, {
146
files: [fromFixture('notebook/errors.ipynb')],
147
queries: [
148
{
149
file: 'errors.ipynb',
150
activeCell: 0,
151
selection: [0, 0, 0, 0],
152
query: '/fix ModuleNotFoundError: No module named \'pandas\'',
153
expectedIntent: 'edit',
154
validate: async (outcome, workspace, accessor) => {
155
assert.strictEqual(outcome.type, 'inlineEdit');
156
assert.ok(!outcome.fileContents.includes('!pip'));
157
assert.ok(outcome.fileContents.includes('%pip install'));
158
assert.ok(outcome.fileContents.indexOf('pip') === outcome.fileContents.lastIndexOf('pip'));
159
}
160
}
161
]
162
});
163
});
164
165
stest({ description: 'edit notebook code should not duplicate the content', language: 'python' }, (testingServiceCollection) => {
166
return simulateInlineChat(testingServiceCollection, {
167
files: [fromFixture('notebook/edit.ipynb')],
168
queries: [
169
{
170
file: 'edit.ipynb',
171
activeCell: 0,
172
selection: [6, 0, 8, 0],
173
query: 'make the plot larger',
174
expectedIntent: 'edit',
175
validate: async (outcome, workspace, accessor) => {
176
assert.strictEqual(outcome.type, 'inlineEdit');
177
assert.ok(
178
outcome.fileContents.includes('plt.figure')
179
|| outcome.fileContents.includes('plt.gcf')
180
);
181
// check if 'plt.figure' only shows up once
182
const matches = outcome.fileContents.match(/(plt\.figure)|(plt\.gcf)/g);
183
assert.strictEqual(matches?.length, 1);
184
}
185
}
186
]
187
});
188
});
189
190
stest({ description: 'set index', language: 'python' }, (testingServiceCollection) => {
191
return simulateInlineChat(testingServiceCollection, {
192
files: [fromFixture('notebook/edit.ipynb')],
193
queries: [
194
{
195
file: 'edit.ipynb',
196
activeCell: 1,
197
selection: [13, 0, 13, 0],
198
query: 'Set the \'origin\' colum as the index of the dataframe',
199
expectedIntent: 'edit',
200
validate: async (outcome, workspace, accessor) => {
201
assert.strictEqual(outcome.type, 'inlineEdit');
202
assert.ok(outcome.fileContents.includes('.set_index'));
203
assert.strictEqual(outcome.fileContents.match(/set\_index/g)?.length, 1);
204
assert.strictEqual(outcome.fileContents.match(/DataFrame/g)?.length, 1);
205
}
206
}
207
]
208
});
209
});
210
211
stest({ description: 'group by', language: 'python' }, (testingServiceCollection) => {
212
return simulateInlineChat(testingServiceCollection, {
213
files: [fromFixture('notebook/edit.ipynb')],
214
queries: [
215
{
216
file: 'edit.ipynb',
217
activeCell: 2,
218
selection: [6, 0, 6, 0],
219
query: 'Group the entire dataframe by regiment and company',
220
expectedIntent: 'edit',
221
validate: async (outcome, workspace, accessor) => {
222
assert.strictEqual(outcome.type, 'inlineEdit');
223
assert.ok(outcome.fileContents.includes('regiment.groupby'));
224
assert.strictEqual(outcome.fileContents.match(/groupby/g)?.length, 1);
225
assert.strictEqual(outcome.fileContents.match(/DataFrame/g)?.length, 1);
226
}
227
}
228
]
229
});
230
});
231
232
// import matplotlib.pyplot as plt
233
234
// months = range(1, 13)
235
// nyc_temp_2000 = [20.0, 30.5, 80.1, 80.3, 56.5, 99.6]
236
// plt.plot(months, nyc_temp_2000)
237
// stest({ description: '/fix Matplotlib: x and y must have same first dimension', language: 'python' }, (testingServiceCollection) => {
238
// return runScenario(accessor, {
239
// files: [fromFixture('notebook/errors.ipynb')],
240
// queries: [
241
// {
242
// file: 'errors.ipynb',
243
// activeCell: 6,
244
// selection: [4, 0, 4, 0],
245
// query: '/fix ValueError: x and y must have same first dimension, but have shapes (12,) and (6,)',
246
// expectedIntent: 'edit',
247
// validate: async (outcome, workspace, accessor) => {
248
// assert.strictEqual(outcome.type, 'inlineEdit');
249
// assert.strictEqual(outcome.appliedEdits.length, 1);
250
// const edit = outcome.appliedEdits[0];
251
// assert.ok(edit.newText.includes('global'));
252
// }
253
// }
254
// ]
255
// });
256
// });
257
258
259
260
261
// NameError: name 'df' is not defined -> should suggest rerun cell
262
});
263
264
ssuite({ title: 'notebook', subtitle: 'generate', location: 'inline' }, () => {
265
266
stest({ description: 'edit markdown cell should support code example', language: 'markdown' }, (testingServiceCollection) => {
267
return simulateInlineChat(testingServiceCollection, {
268
files: [fromFixture('notebook/md.ipynb')],
269
queries: [
270
{
271
file: 'md.ipynb',
272
activeCell: 0,
273
selection: [0, 0, 0, 0],
274
query: 'describe fibonacci algorithm in markdown, along with code example',
275
expectedIntent: 'generate',
276
validate: async (outcome, workspace, accessor) => {
277
assert.strictEqual(outcome.type, 'inlineEdit');
278
assert.ok(outcome.fileContents.includes('```'));
279
const matches = outcome.fileContents.match(/\`\`\`/g);
280
assert.ok(matches && matches.length > 0 && matches.length % 2 === 0);
281
}
282
}
283
]
284
});
285
});
286
287
stest({ description: 'Which was the most-ordered item', language: 'python' }, (testingServiceCollection) => {
288
return simulateInlineChat(testingServiceCollection, {
289
files: [fromFixture('notebook/sales.ipynb')],
290
queries: [
291
{
292
file: 'sales.ipynb',
293
activeCell: 13,
294
selection: [0, 0, 0, 0],
295
query: 'Which was the most-ordered item? ', // How many items were orderd in total?
296
expectedIntent: 'generate',
297
validate: async (outcome, workspace, accessor) => {
298
assert.strictEqual(outcome.type, 'inlineEdit');
299
}
300
}
301
]
302
});
303
});
304
305
stest({ description: 'How many items were orderd in total?', language: 'python' }, (testingServiceCollection) => {
306
return simulateInlineChat(testingServiceCollection, {
307
files: [fromFixture('notebook/sales.ipynb')],
308
queries: [
309
{
310
file: 'sales.ipynb',
311
activeCell: 13,
312
selection: [0, 0, 0, 0],
313
query: 'WHow many items were orderd in total?',
314
expectedIntent: 'generate',
315
validate: async (outcome, workspace, accessor) => {
316
assert.strictEqual(outcome.type, 'inlineEdit');
317
}
318
}
319
]
320
});
321
});
322
323
stest({ description: 'create a model to predict the likelihood of a flight being delayed', language: 'python' }, (testingServiceCollection) => {
324
return simulateInlineChat(testingServiceCollection, {
325
files: [fromFixture('notebook/model.ipynb')],
326
queries: [
327
{
328
file: 'model.ipynb',
329
activeCell: 1,
330
selection: [0, 0, 0, 0],
331
query: 'create a model to predict the likelihood of a flight being delayed based on the day of the week and the arrival airport. Use Logistic regression and calculate the accuracy of the model.', //
332
expectedIntent: 'generate',
333
validate: async (outcome, workspace, accessor) => {
334
assert.strictEqual(outcome.type, 'inlineEdit');
335
}
336
}
337
]
338
});
339
});
340
});
341
342
ssuite({ title: 'notebook', subtitle: 'generate runtime', location: 'inline' }, () => {
343
stest({ description: 'generate code uses obselete variable', language: 'python' }, (testingServiceCollection) => {
344
const file = fromFixture('notebook/variablesruntime.ipynb');
345
const testScenario: IScenario = {
346
files: [file],
347
queries: [
348
{
349
file: 'variablesruntime.ipynb',
350
activeCell: 1,
351
selection: [2, 0, 2, 0],
352
query: 'Detect and remove outliers for delay columns',
353
expectedIntent: 'edit',
354
validate: async (outcome, workspace, accessor) => {
355
assert.strictEqual(outcome.type, 'inlineEdit');
356
assert.strictEqual(outcome.fileContents.indexOf('[delay_columns]') >= 0 || outcome.fileContents.indexOf('delay_columns =') >= 0 || outcome.fileContents.indexOf('delay_columns:') >= 0, true);
357
}
358
}
359
],
360
extraWorkspaceSetup: async (workspace) => {
361
const notebook = workspace.getNotebookDocuments()[0];
362
if (notebook) {
363
const variables: VariablesResult[] = [
364
{
365
variable: {
366
name: 'delay_columns',
367
value: `['DepDelay', 'ArrDelay']`,
368
type: 'list'
369
},
370
hasNamedChildren: false,
371
indexedChildrenCount: 2
372
}
373
];
374
testingServiceCollection.define(INotebookService, new SyncDescriptor(
375
SimulationNotebookService,
376
[
377
workspace,
378
new ResourceMap<VariablesResult[]>([
379
[
380
notebook.uri,
381
variables
382
]
383
])
384
]
385
));
386
testingServiceCollection.define(IAlternativeNotebookContentService, new SyncDescriptor(
387
SimulationAlternativeNotebookContentService,
388
[]
389
));
390
testingServiceCollection.define(IAlternativeNotebookContentEditGenerator, new SyncDescriptor(
391
AlternativeNotebookContentEditGenerator
392
));
393
testingServiceCollection.define(IDiffService, new SyncDescriptor(
394
DiffServiceImpl
395
));
396
}
397
}
398
};
399
return simulateInlineChat(testingServiceCollection, testScenario);
400
});
401
});
402
403
ssuite({ title: 'notebook', subtitle: 'fix runtime', location: 'inline' }, () => {
404
stest({ description: '/fix notebook execution ImportError, insert at top', language: 'python' }, (testingServiceCollection) => {
405
return simulateInlineChat(testingServiceCollection, {
406
files: [fromFixture('notebook/errors.ipynb')],
407
queries: [
408
{
409
file: 'errors.ipynb',
410
activeCell: 0,
411
selection: [0, 0, 0, 0],
412
query: '/fix ModuleNotFoundError: No module named \'pandas\'',
413
expectedIntent: 'edit',
414
validate: async (outcome, workspace, accessor) => {
415
assert.strictEqual(outcome.type, 'inlineEdit');
416
// assert(outcome.appliedEdits.length > 0, 'at least 1 edit generated');
417
assert.ok(outcome.fileContents.indexOf('import pandas') >= 0);
418
// assert.ok(await isValidPythonFile(accessor, outcome.fileContents));
419
}
420
}
421
]
422
});
423
});
424
425
stest({ description: '/fix ValueError: The truth value of an array with more than one element is ambiguous', language: 'python' }, (testingServiceCollection) => {
426
return simulateInlineChat(testingServiceCollection, {
427
files: [fromFixture('notebook/errors.ipynb')],
428
queries: [
429
{
430
file: 'errors.ipynb',
431
activeCell: 1,
432
selection: [4, 0, 4, 0],
433
query: '/fix ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()',
434
expectedIntent: 'edit',
435
validate: async (outcome, workspace, accessor) => {
436
assert.strictEqual(outcome.type, 'inlineEdit');
437
if (!outcome.fileContents.includes('A | B') && !outcome.fileContents.includes('np.logical_or(A, B)')) {
438
assert.ok(outcome.fileContents.includes('A.any()'));
439
assert.ok(outcome.fileContents.includes('B.any()'));
440
}
441
}
442
}
443
]
444
});
445
});
446
447
stest({ description: '/fix Tensorflow InvalidArgumentError', language: 'python' }, (testingServiceCollection) => {
448
return simulateInlineChat(testingServiceCollection, {
449
files: [fromFixture('notebook/errors.ipynb')],
450
queries: [
451
{
452
file: 'errors.ipynb',
453
activeCell: 2,
454
selection: [1, 0, 1, 0],
455
query: '/fix InvalidArgumentError: {{function_node __wrapped__Reshape_device_/job:localhost/replica:0/task:0/device:CPU:0}} Input to reshape is a tensor with 3 values, but the requested shape has 2 [Op:Reshape]',
456
expectedIntent: 'edit',
457
validate: async (outcome, workspace, accessor) => {
458
assert.strictEqual(outcome.type, 'inlineEdit');
459
assert.ok(outcome.fileContents.includes('reshape'));
460
}
461
}
462
]
463
});
464
});
465
466
stest({ description: '/fix Tensorflow model has not yet been built', language: 'python' }, (testingServiceCollection) => {
467
return simulateInlineChat(testingServiceCollection, {
468
files: [fromNotebookFixture('notebook/errors.ipynb', 3)],
469
queries: [
470
{
471
file: 'errors.ipynb',
472
activeCell: 3,
473
selection: [4, 0, 4, 0],
474
query: '/fix ValueError: This model has not yet been built. Build the model first by calling `build()` or by calling the model on a batch of data.',
475
expectedIntent: 'edit',
476
validate: async (outcome, workspace, accessor) => {
477
assert.strictEqual(outcome.type, 'inlineEdit');
478
// assert.ok(outcome.fileContents.includes('.build'));
479
}
480
}
481
]
482
});
483
});
484
485
stest({ description: '/fix numpy, unsupported operand types', language: 'python' }, (testingServiceCollection) => {
486
return simulateInlineChat(testingServiceCollection, {
487
files: [fromNotebookFixture('notebook/errors.ipynb', 4)],
488
queries: [
489
{
490
file: 'errors.ipynb',
491
activeCell: 4,
492
selection: [3, 0, 3, 0],
493
query: '/fix TypeError: unsupported operand type(s) for +: \'int\' and \'NoneType\'',
494
expectedIntent: 'edit',
495
validate: async (outcome, workspace, accessor) => {
496
assert.strictEqual(outcome.type, 'inlineEdit');
497
assert.ok(
498
!outcome.fileContents.includes('None') ||
499
outcome.fileContents.includes(' != None') ||
500
outcome.fileContents.includes(' == None') || (
501
(outcome.fileContents.includes('np.array([1, np.nan, 3, 4])') && outcome.fileContents.includes('nansum(vals1)'))
502
));
503
}
504
}
505
]
506
});
507
});
508
509
stest({ description: '/fix UnboundLocalError, local variable referenced before assignment', language: 'python' }, (testingServiceCollection) => {
510
return simulateInlineChat(testingServiceCollection, {
511
files: [fromFixture('notebook/errors.ipynb')],
512
queries: [
513
{
514
file: 'errors.ipynb',
515
activeCell: 5,
516
selection: [3, 0, 3, 0],
517
query: '/fix UnboundLocalError: local variable \'a_var\' referenced before assignment',
518
expectedIntent: 'edit',
519
validate: async (outcome, workspace, accessor) => {
520
assert.strictEqual(outcome.type, 'inlineEdit');
521
}
522
}
523
]
524
});
525
});
526
527
stest({ description: '/fix name conflict with builtin function', language: 'python' }, (testingServiceCollection) => {
528
return simulateInlineChat(testingServiceCollection, {
529
files: [fromFixture('notebook/errors.ipynb')],
530
queries: [
531
{
532
file: 'errors.ipynb',
533
activeCell: 6,
534
selection: [0, 0, 4, 16],
535
query: '/fix TypeError: \'int\' object is not callable',
536
expectedIntent: 'edit',
537
validate: async (outcome, workspace, accessor) => {
538
assert.strictEqual(outcome.type, 'inlineEdit');
539
assert.ok(!outcome.fileContents.includes('max = 0'));
540
}
541
}
542
]
543
});
544
});
545
546
stest({ description: '/fix AttributeError: can\'t set attribute', language: 'python' }, (testingServiceCollection) => {
547
return simulateInlineChat(testingServiceCollection, {
548
files: [fromFixture('notebook/errors.ipynb')],
549
queries: [
550
{
551
file: 'errors.ipynb',
552
activeCell: 7,
553
selection: [9, 0, 9, 0],
554
query: '/fix AttributeError: can\'t set attribute',
555
expectedIntent: 'edit',
556
validate: async (outcome, workspace, accessor) => {
557
assert.strictEqual(outcome.type, 'inlineEdit');
558
assert.ok(outcome.fileContents.includes('@x.setter'));
559
}
560
}
561
]
562
});
563
});
564
565
stest({ description: '/fix TypeError: Index does not support mutable operations', language: 'python' }, (testingServiceCollection) => {
566
return simulateInlineChat(testingServiceCollection, {
567
files: [fromFixture('notebook/errors.ipynb')],
568
queries: [
569
{
570
file: 'errors.ipynb',
571
activeCell: 8,
572
selection: [3, 0, 3, 0],
573
query: '/fix TypeError: Index does not support mutable operations',
574
expectedIntent: 'edit',
575
validate: async (outcome, workspace, accessor) => {
576
assert.strictEqual(outcome.type, 'inlineEdit');
577
assert.ok(outcome.fileContents.includes('ind.set_value') || outcome.fileContents.includes('list(ind)') || outcome.fileContents.includes('ind.tolist()') || outcome.fileContents.includes('ind.delete('));
578
}
579
}
580
]
581
});
582
});
583
584
stest({ description: '/fix TypeError: str object is not an iterator', language: 'python' }, (testingServiceCollection) => {
585
return simulateInlineChat(testingServiceCollection, {
586
files: [fromFixture('notebook/errors.ipynb')],
587
queries: [
588
{
589
file: 'errors.ipynb',
590
activeCell: 9,
591
selection: [1, 0, 1, 0],
592
query: '/fix TypeError: str object is not an iterator',
593
expectedIntent: 'edit',
594
validate: async (outcome, workspace, accessor) => {
595
assert.strictEqual(outcome.type, 'inlineEdit');
596
assert.ok(outcome.fileContents.includes('iter('));
597
}
598
}
599
]
600
});
601
});
602
603
stest({ description: '/fix TypeError: can only concatenate str (not "int") to str', language: 'python' }, (testingServiceCollection) => {
604
return simulateInlineChat(testingServiceCollection, {
605
files: [fromNotebookFixture('notebook/errors.ipynb', 10)],
606
queries: [
607
{
608
file: 'errors.ipynb',
609
activeCell: 10,
610
selection: [0, 0, 0, 0],
611
query: '/fix TypeError: can only concatenate str (not "int") to str',
612
expectedIntent: 'edit',
613
validate: async (outcome, workspace, accessor) => {
614
assert.strictEqual(outcome.type, 'inlineEdit');
615
assert.ok(outcome.fileContents.includes('float(') || outcome.fileContents.includes('int(') || outcome.fileContents.includes('str('));
616
}
617
}
618
]
619
});
620
});
621
622
stest({ description: '/fix Missing import, name \'array\' is not defined', language: 'python' }, (testingServiceCollection) => {
623
return simulateInlineChat(testingServiceCollection, {
624
files: [fromFixture('notebook/errors.ipynb')],
625
queries: [
626
{
627
file: 'errors.ipynb',
628
activeCell: 11,
629
selection: [0, 0, 0, 0],
630
query: '/fix NameError: name \'array\' is not defined',
631
expectedIntent: 'edit',
632
validate: async (outcome, workspace, accessor) => {
633
assert.strictEqual(outcome.type, 'inlineEdit');
634
assert.ok(outcome.fileContents.includes('np.array') || outcome.fileContents.includes('from numpy import'));
635
// assert.ok(await isValidPythonFile(accessor, outcome.fileContents));
636
}
637
}
638
]
639
});
640
});
641
642
stest({ description: '/fix can only concatenate list (not "str") to list', language: 'python' }, (testingServiceCollection) => {
643
return simulateInlineChat(testingServiceCollection, {
644
files: [fromFixture('notebook/errors.ipynb')],
645
queries: [
646
{
647
file: 'errors.ipynb',
648
activeCell: 12,
649
selection: [1, 0, 1, 0],
650
query: '/fix TypeError: can only concatenate list (not "str") to list',
651
expectedIntent: 'edit',
652
validate: async (outcome, workspace, accessor) => {
653
assert.strictEqual(outcome.type, 'inlineEdit');
654
assert.ok(outcome.fileContents.includes('[\'bar\']') || outcome.fileContents.includes('foo.append'));
655
assert.ok(await isValidPythonFile(accessor, outcome.fileContents));
656
}
657
}
658
]
659
});
660
});
661
});
662
663
ssuite({ title: 'notebook', subtitle: 'fix', location: 'inline' }, () => {
664
stest({ description: 'cannot instantiate abstract class', language: 'python' }, (testingServiceCollection) => {
665
return simulateInlineChat(testingServiceCollection, {
666
files: [fromFixture('notebook/fixing/fixing0.ipynb')],
667
queries: [
668
{
669
file: 'fixing0.ipynb',
670
selection: [9, 4, 9, 4],
671
activeCell: 0,
672
query: `/fix Cannot instantiate abstract class "Base"\n "Base.foo" is abstract`,
673
expectedIntent: Intent.Fix,
674
diagnostics: 'pyright',
675
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
676
}
677
]
678
});
679
});
680
681
stest({ description: 'all Annotated types should include at least two type arguments', language: 'python' }, (testingServiceCollection) => {
682
return simulateInlineChat(testingServiceCollection, {
683
files: [fromFixture('notebook/fixing/fixing1.ipynb')],
684
queries: [
685
{
686
file: 'fixing1.ipynb',
687
selection: [4, 3, 4, 3],
688
activeCell: 0,
689
query: `/fix Expected one type argument and one or more annotations for "Annotated"`,
690
expectedIntent: Intent.Fix,
691
diagnostics: 'pyright',
692
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
693
}
694
]
695
});
696
});
697
698
stest({ description: 'should not generate an error for variables declared in outer scopes', language: 'python' }, (testingServiceCollection) => {
699
return simulateInlineChat(testingServiceCollection, {
700
files: [fromFixture('notebook/fixing/fixing2.ipynb')],
701
queries: [
702
{
703
file: 'fixing2.ipynb',
704
selection: [24, 8, 24, 8],
705
activeCell: 0,
706
query: `/fix "d" is not defined`,
707
expectedIntent: Intent.Fix,
708
diagnostics: 'pyright',
709
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
710
}
711
]
712
});
713
});
714
715
stest({ description: 'async cannot be used in a non-async function', language: 'python' }, (testingServiceCollection) => {
716
return simulateInlineChat(testingServiceCollection, {
717
files: [fromFixture('notebook/fixing/fixing3.ipynb')],
718
queries: [
719
{
720
file: 'fixing3.ipynb',
721
selection: [17, 4, 17, 4],
722
activeCell: 0,
723
query: `/fix Use of "async" not allowed outside of async function`,
724
expectedIntent: Intent.Fix,
725
diagnostics: 'pyright',
726
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
727
}
728
]
729
});
730
});
731
732
stest({ description: 'await cannot be used in a non-async function', language: 'python' }, (testingServiceCollection) => {
733
return simulateInlineChat(testingServiceCollection, {
734
files: [fromFixture('notebook/fixing/fixing4.ipynb')],
735
queries: [
736
{
737
file: 'fixing4.ipynb',
738
selection: [14, 0, 14, 0],
739
activeCell: 0,
740
query: `/fix "await" allowed only within async function`,
741
expectedIntent: Intent.Fix,
742
diagnostics: 'pyright',
743
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
744
}
745
]
746
});
747
});
748
749
stest({ description: 'bad token', language: 'python' }, (testingServiceCollection) => {
750
return simulateInlineChat(testingServiceCollection, {
751
files: [fromFixture('notebook/fixing/fixing5.ipynb')],
752
queries: [
753
{
754
file: 'fixing5.ipynb',
755
selection: [4, 7, 4, 7],
756
activeCell: 0,
757
query: `/fix Invalid character in identifier`,
758
expectedIntent: Intent.Fix,
759
diagnostics: 'pyright',
760
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
761
}
762
]
763
});
764
});
765
766
stest({ description: 'Bar does not define a do_something2 method', language: 'python' }, (testingServiceCollection) => {
767
return simulateInlineChat(testingServiceCollection, {
768
files: [fromFixture('notebook/fixing/fixing6.ipynb')],
769
queries: [
770
{
771
file: 'fixing6.ipynb',
772
selection: [28, 0, 28, 0],
773
activeCell: 0,
774
query: [
775
`/fix Cannot access member "do_something2" for type "Bar"`,
776
` Member "do_something2" is unknown`
777
].join('\n'),
778
expectedIntent: Intent.Fix,
779
diagnostics: 'pyright',
780
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
781
}
782
]
783
});
784
});
785
786
// Inspired by case 35 of /fix dataset version 10
787
// In the AML run, copilot did not understand the error and did not fix it
788
stest({ description: '(AML-10-35) can not access member', language: 'python' }, (testingServiceCollection) => {
789
return simulateInlineChat(testingServiceCollection, {
790
files: [fromFixture('notebook/fixing/fixing7.ipynb')],
791
queries: [
792
{
793
file: 'fixing7.ipynb',
794
selection: [2, 23, 2, 23],
795
activeCell: 0,
796
query: [
797
`/fix Cannot access member "includes" for type "set[Unknown]"`,
798
` Member "includes" is unknown`
799
].join('\n'),
800
expectedIntent: Intent.Fix,
801
diagnostics: 'pyright',
802
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
803
}
804
]
805
});
806
});
807
808
// Inspired by case 36 of /fix dataset version 10
809
// In the AML run, copilot did not understand the error and did not fix it
810
stest({ description: '(AML-10-36) can not be assigned 2', language: 'python' }, (testingServiceCollection) => {
811
return simulateInlineChat(testingServiceCollection, {
812
files: [fromFixture('notebook/fixing/fixing8.ipynb')],
813
queries: [
814
{
815
file: 'fixing8.ipynb',
816
selection: [4, 19, 4, 19],
817
activeCell: 0,
818
query: `/fix Expression of type "list[None]" cannot be assigned to declared type "List[int] | None"`,
819
expectedIntent: Intent.Fix,
820
diagnostics: 'pyright',
821
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
822
}
823
]
824
});
825
});
826
827
// Inspired by case 4 of /fix dataset version 10
828
// In the AML run, copilot did not understand the error and did not fix it
829
stest({ description: '(AML-10-4) parameter already assigned', language: 'python' }, (testingServiceCollection) => {
830
return simulateInlineChat(testingServiceCollection, {
831
files: [fromFixture('notebook/fixing/fixing9.ipynb')],
832
queries: [
833
{
834
file: 'fixing9.ipynb',
835
selection: [7, 33, 7, 33],
836
activeCell: 0,
837
query: `/fix Parameter "input_shape" is already assigned`,
838
expectedIntent: Intent.Fix,
839
diagnostics: 'pyright',
840
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
841
}
842
]
843
});
844
});
845
846
// Inspired by case 48 of /fix dataset version 10
847
// In the AML run, copilot did not understand the error and did not fix it
848
stest({ description: '(AML-10-48) can not be assigned 3', language: 'python' }, (testingServiceCollection) => {
849
return simulateInlineChat(testingServiceCollection, {
850
files: [fromFixture('notebook/fixing/fixing10.ipynb')],
851
queries: [
852
{
853
file: 'fixing10.ipynb',
854
selection: [9, 14, 9, 14],
855
activeCell: 0,
856
query: [
857
`/fix Argument of type "dict[str, int]" cannot be assigned to parameter "platforms" of type "list[str] | str" in function "setup"`,
858
` Type "dict[str, int]" cannot be assigned to type "list[str] | str"`,
859
` "dict[str, int]" is incompatible with "list[str]"`,
860
` "dict[str, int]" is incompatible with "str"`,
861
].join('\n'),
862
expectedIntent: Intent.Fix,
863
diagnostics: 'pyright',
864
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
865
}
866
]
867
});
868
});
869
870
// Inspired by case 58 of /fix dataset version 10
871
// The AML run has removed a big part of the code and replaced with non-minimal edits, this initial issue was not resolved
872
stest({ description: '(AML-10-58) not defined', language: 'python' }, (testingServiceCollection) => {
873
return simulateInlineChat(testingServiceCollection, {
874
files: [fromFixture('notebook/fixing/fixing11.ipynb')],
875
queries: [
876
{
877
file: 'fixing11.ipynb',
878
selection: [7, 20, 7, 20],
879
activeCell: 0,
880
query: `/fix "T_Or" is not defined`,
881
expectedIntent: Intent.Fix,
882
diagnostics: 'pyright',
883
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
884
}
885
]
886
});
887
});
888
889
// Inspired by case 29 of /fix dataset version 10
890
stest({ description: '(AML-10-29) instance of bool has no to_string member', language: 'python' }, (testingServiceCollection) => {
891
return simulateInlineChat(testingServiceCollection, {
892
files: [fromFixture('notebook/fixing/fixing12.ipynb')],
893
queries: [
894
{
895
file: 'fixing12.ipynb',
896
selection: [1, 19, 1, 19],
897
activeCell: 0,
898
query: `/fix`,
899
expectedIntent: Intent.Fix,
900
diagnostics: 'pyright',
901
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
902
}
903
]
904
});
905
});
906
907
// Inspired by case 110 of /fix dataset version 8
908
stest({ description: '(AML-8-110) not defined', language: 'python' }, (testingServiceCollection) => {
909
return simulateInlineChat(testingServiceCollection, {
910
files: [fromFixture('notebook/fixing/fixing13.ipynb')],
911
queries: [
912
{
913
file: 'fixing13.ipynb',
914
selection: [7, 20, 7, 20],
915
activeCell: 0,
916
query: `/fix Instance methods should take a "self" parameter`,
917
expectedIntent: Intent.Fix,
918
diagnostics: 'pyright',
919
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
920
}
921
]
922
});
923
});
924
925
926
// Inspired by case 73 of /fix dataset version 8
927
stest({ description: '(AML-8-73) no value for argument in function call', language: 'python' }, (testingServiceCollection) => {
928
return simulateInlineChat(testingServiceCollection, {
929
files: [fromFixture('notebook/fixing/fixing14.ipynb')],
930
queries: [
931
{
932
file: 'fixing14.ipynb',
933
selection: [12, 16, 12, 16],
934
activeCell: 0,
935
query: `/fix Argument missing for parameter "error_message"`,
936
expectedIntent: Intent.Fix,
937
diagnostics: 'pyright',
938
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
939
}
940
]
941
});
942
});
943
944
stest({ description: 'undefined variable', language: 'python' }, (testingServiceCollection) => {
945
return simulateInlineChat(testingServiceCollection, {
946
files: [fromFixture('notebook/fixing/fixing15.ipynb')],
947
queries: [
948
{
949
file: 'fixing15.ipynb',
950
selection: [0, 0, 0, 4],
951
activeCell: 0,
952
query: `/fix "Play" is not defined`,
953
expectedIntent: Intent.Fix,
954
diagnostics: 'pyright',
955
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
956
}
957
]
958
});
959
});
960
961
stest({ description: 'general type issue', language: 'python' }, (testingServiceCollection) => {
962
return simulateInlineChat(testingServiceCollection, {
963
files: [fromFixture('notebook/fixing/fixing16.ipynb')],
964
queries: [
965
{
966
file: 'fixing16.ipynb',
967
selection: [29, 22, 29, 25],
968
activeCell: 0,
969
query: [
970
`/fix Argument of type "Msg[Foo]" cannot be assigned to parameter "msg" of type "Msg[FooBar]" in function "handle"`,
971
` "Msg[Foo]" is incompatible with "Msg[FooBar]"`,
972
` Type parameter "T@Msg" is invariant, but "Foo" is not the same as "FooBar"`
973
].join('\n'),
974
expectedIntent: Intent.Fix,
975
diagnostics: 'pyright',
976
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
977
}
978
]
979
});
980
});
981
982
stest({ description: 'optional member access', language: 'python' }, (testingServiceCollection) => {
983
return simulateInlineChat(testingServiceCollection, {
984
files: [fromFixture('notebook/fixing/fixing17.ipynb')],
985
queries: [
986
{
987
file: 'fixing17.ipynb',
988
selection: [12, 23, 12, 28],
989
activeCell: 0,
990
query: `/fix "upper" is not a known member of "None"`,
991
expectedIntent: Intent.Fix,
992
diagnostics: 'pyright',
993
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
994
}
995
]
996
});
997
});
998
999
stest({ description: 'unbound variable', language: 'python' }, (testingServiceCollection) => {
1000
return simulateInlineChat(testingServiceCollection, {
1001
files: [fromFixture('notebook/fixing/fixing18.ipynb')],
1002
queries: [
1003
{
1004
file: 'fixing18.ipynb',
1005
selection: [4, 11, 4, 12],
1006
activeCell: 0,
1007
query: `/fix "a" is possibly unbound`,
1008
expectedIntent: Intent.Fix,
1009
diagnostics: 'pyright',
1010
validate: async (outcome, workspace, accessor) => assertNoCellDiagnosticsAsync(accessor, outcome, workspace, 'pyright')
1011
}
1012
]
1013
});
1014
});
1015
});
1016
1017
const shouldEvaluate = 1 >> 1;
1018
1019
ssuite.optional((opes) => !shouldEvaluate, { title: 'notebook', subtitle: 'mbpp', location: 'inline' }, () => {
1020
const mbppFile = fromFixture('notebook/filtered-mbpp.json');
1021
const mbppTests = JSON.parse(mbppFile.fileContents).tests;
1022
const testScenarios: { task_id: number; text: string; test_list: string[] }[] = mbppTests.map((test: any) => ({
1023
task_id: test.task_id,
1024
text: test.prompt,
1025
test_list: test.test_list.map((line: string) => `${line}\n`)
1026
}));
1027
1028
for (const test of testScenarios) {
1029
const prompt = test.text;
1030
const test_list = test.test_list;
1031
const task_id = test.task_id;
1032
1033
stest({ description: 'mbpp ' + task_id + ' ' + prompt, language: 'python' }, async (testingServiceCollection) => {
1034
const templateFile = fromFixture('notebook/mbpp.ipynb');
1035
1036
const notebook = JSON.parse(templateFile.fileContents);
1037
const cells = notebook.cells as any[];
1038
const cell = cells[1];
1039
cell.source = test_list;
1040
1041
const notebookFile = { kind: 'relativeFile' as const, fileName: templateFile.fileName, fileContents: JSON.stringify(notebook, undefined, 2) };
1042
1043
return simulateInlineChat(testingServiceCollection, {
1044
files: [notebookFile],
1045
queries: [
1046
{
1047
file: notebookFile.fileName,
1048
activeCell: 0,
1049
selection: [0, 0, 0, 0],
1050
query: `${prompt} Your code should pass the tests in the next cell.`,
1051
expectedIntent: 'edit',
1052
validate: async (outcome, _workspace, accessor) => {
1053
assert.strictEqual(outcome.type, 'inlineEdit');
1054
1055
if (shouldEvaluate) {
1056
const testTimeoutPromise = new Promise<void>((_, reject) => {
1057
setTimeout(() => reject(new Error('Test execution timed out')), 60000);
1058
});
1059
1060
await Promise.race([testTimeoutPromise, assertPythonCodeIsValid(accessor, outcome.fileContents, 'Generated Code is not valid', 'Generated not did not execute without errors')]);
1061
1062
const codeWithTests = `${outcome.fileContents}${EOL}${EOL}${EOL}# Tests${EOL}${EOL}${test_list.join('')}`;
1063
await Promise.race([testTimeoutPromise, assertPythonCodeIsValid(accessor, codeWithTests, 'Generated Code with Tests is not valid', 'Generated did not pass the tests')]);
1064
}
1065
}
1066
}
1067
]
1068
});
1069
});
1070
}
1071
});
1072
1073
function generateMBPPNotebookFixture(pathOrDirnameWithinFixturesDir: string, tests: string[]) {
1074
const filePath = path.join(getFixturesDir(), pathOrDirnameWithinFixturesDir);
1075
const baseDirname = path.dirname(filePath);
1076
const fileName = path.relative(baseDirname, filePath);
1077
const uri = URI.parse(filePath);
1078
const fileContents = fs.readFileSync(filePath).toString();
1079
1080
try {
1081
const notebook = JSON.parse(fileContents);
1082
const cells = notebook.cells as any[];
1083
const cell = cells[1];
1084
cell.source = tests;
1085
1086
return { kind: 'relativeFile' as const, uri, fileName, fileContents: JSON.stringify(notebook, undefined, 2) };
1087
} catch {
1088
return { kind: 'relativeFile' as const, uri, fileName, fileContents };
1089
}
1090
}
1091
1092
1093
ssuite.optional((opes) => !shouldEvaluate || true, { title: 'notebook', subtitle: 'notebookEditsMbpp', location: 'panel' }, () => {
1094
const mbppFile = fromFixture('notebook/filtered-mbpp.json');
1095
const mbppTests = JSON.parse(mbppFile.fileContents).tests;
1096
const testScenarios: { task_id: number; text: string; test_list: string[] }[] = mbppTests.map((test: any) => ({
1097
task_id: test.task_id,
1098
text: test.prompt,
1099
test_list: test.test_list.map((line: string) => `${line}\n`)
1100
}));
1101
1102
for (const test of testScenarios) {
1103
const prompt = test.text;
1104
const test_list = test.test_list;
1105
const task_id = test.task_id;
1106
1107
stest({ description: 'mbpp ' + task_id + ' ' + prompt, language: 'python' }, async (testingServiceCollection) => {
1108
const notebookFile = generateMBPPNotebookFixture('notebook/mbpp.ipynb', test_list);
1109
const disposables = new DisposableStore();
1110
1111
const nbJson = JSON.parse(notebookFile.fileContents);
1112
const cells = nbJson.cells as any[];
1113
const cell = cells[1];
1114
cell.source = test_list;
1115
1116
let notebook: NotebookDocument;
1117
const currentFile: IFile = {
1118
kind: 'qualifiedFile',
1119
uri: notebookFile.uri,
1120
fileContents: JSON.stringify(nbJson, undefined, 2)
1121
};
1122
1123
return simulateInlineChat(testingServiceCollection, {
1124
files: [currentFile],
1125
async extraWorkspaceSetup(workspace) {
1126
const extHostNotebook = ExtHostNotebookDocumentData.createJupyterNotebook(notebookFile.uri, notebookFile.fileContents, workspace);
1127
notebook = extHostNotebook.document;
1128
// TODO@DonJayamanne
1129
},
1130
async onBeforeStart(accessor) {
1131
(accessor.get<IAlternativeNotebookContentService>(IAlternativeNotebookContentService) as MockAlternativeNotebookContentService).format = AlternativeNotebookFormat.json;
1132
},
1133
queries: [
1134
{
1135
file: currentFile.uri,
1136
activeCell: 0,
1137
selection: [1, 0, 1, 0],
1138
query: `${prompt} Your code should pass the tests in the next cell.`,
1139
expectedIntent: 'edit',
1140
validate: async (_outcome, _workspace, accessor) => {
1141
if (shouldEvaluate) {
1142
const testTimeoutPromise = new Promise<void>((_, reject) => {
1143
setTimeout(() => reject(new Error('Test execution timed out')), 60000);
1144
});
1145
1146
await Promise.race([testTimeoutPromise, assertPythonCodeIsValid(accessor, notebook.cellAt(0).document.getText(), 'Generated Code is not valid', 'Generated not did not execute without errors')]);
1147
1148
const codeWithTests = `${notebook.cellAt(0).document.getText()}${EOL}${EOL}${EOL}# Tests${EOL}${EOL}${notebook.cellAt(1).document.getText()}`;
1149
await Promise.race([testTimeoutPromise, assertPythonCodeIsValid(accessor, codeWithTests, 'Generated Code with Tests is not valid', 'Generated code did not pass the tests')]);
1150
}
1151
}
1152
}
1153
]
1154
}).finally(() => disposables.dispose());
1155
});
1156
}
1157
});
1158
1159
async function assertPythonCodeIsValid(accessor: ITestingServicesAccessor, pythonCode: string, validationMessage: string, executionMessage: string): Promise<void> {
1160
const cellIsValid = await isValidPythonFile(accessor, pythonCode);
1161
assert.ok(cellIsValid, `${validationMessage}:${EOL}${pythonCode}`);
1162
1163
const cellExecutesWithoutErrors = await canExecutePythonCodeWithoutErrors(accessor, pythonCode);
1164
assert.ok(cellExecutesWithoutErrors, `${executionMessage}:${EOL}${pythonCode}`);
1165
}
1166
1167
async function getNotebookCellDiagnostics(accessor: ITestingServicesAccessor, workspace: SimulationWorkspace, method: DiagnosticProviderId | DiagnosticsProvider): Promise<ITestDiagnostic[]> {
1168
const files = workspace.documents.filter(doc => doc.document.uri.scheme === Schemas.vscodeNotebookCell).map(doc => ({ fileName: workspace.getFilePath(doc.document.uri), fileContents: doc.document.getText() }));
1169
if (typeof method === 'string') {
1170
return await getDiagnostics(accessor, files, method);
1171
} else {
1172
return await method.getDiagnostics(accessor, files);
1173
}
1174
}
1175
1176
async function assertNoCellDiagnosticsAsync(accessor: ITestingServicesAccessor, outcome: IOutcome, workspace: SimulationWorkspace, method: DiagnosticProviderId | DiagnosticsProvider) {
1177
assert.strictEqual(outcome.type, 'inlineEdit');
1178
const diagnostics = await getNotebookCellDiagnostics(accessor, workspace, method);
1179
if (diagnostics.length > 0) {
1180
for (const diagnostic of diagnostics) {
1181
if (diagnostic.message.indexOf('indent') !== -1) {
1182
outcome.annotations.push({ label: 'indentation', message: diagnostic.message, severity: 'warning' });
1183
}
1184
}
1185
}
1186
assert.deepStrictEqual(diagnostics.length, 0, JSON.stringify(diagnostics, undefined, 2));
1187
}
1188
1189