Skip to content

Commit 60fbc79

Browse files
authored
Port rchiodo's fixes (#199)
* Fix two problems with escaping (#14228) * Remove unneeded cell keys when exporting (#14241) * Remove transient output when exporting from the interactive window * Add news entry * Test was failing with true jupyter (#14261) * Potential fix for ipywidget flakiness (#14281) * Try running tests with space in root path (#14113) * Add test with a space (only works on flake) * Push to insiders.yml only * Remove test that doesn't really do anything * REmove unused bits * Change path to have unicode too * Get test to run * Set root path differently * Valid dir * A different way * Another way * Try creating the directory first * Another try * Only one env * Pass parameters correctly * Try without unicode * Set working directory directly on xvfb actions * Working-directory not workingDirectory * Cached ts files output * Remove test with space branch for insiders * Update vscode-python-pr-validation.yaml (#14285) REmove missing branch? Might make it work again * Get rid of AZDO yamls. Not used anymore * Dont run on push (#14307) * Fix random failures on functional tests (#14331) * Splitting test log * Fix problem with kernels ports being reused * Make kernel launcher port round robin only for testing * Make formatters change only apply during testing * Add news entry * Apply black formatting * Code review feedback and skip flakey remote password test * Another flakey test * More CR feedback * Missed a spot * More of the functional tests are failing (#14346) * Splitting test log * Fix problem with kernels ports being reused * Make kernel launcher port round robin only for testing * Make formatters change only apply during testing * Add news entry * Apply black formatting * Code review feedback and skip flakey remote password test * Another flakey test * More CR feedback * Missed a spot * Some more log parser changes and try to get interrupt to be less flakey * Fix interrupt killing kernel and add more logging for export * More logging * See if updating fixes the problem * Dont delete temp files * Upload webview output to figure out trust failure * Add name to step * Try another way to upload * Upload doesn't seem to work * Try a different way to upload * Try without webview logging as this makes the test pass * Try fixing test another way. Any logging is making the test pass * Compile error * Add more logging to figure out why raw kernel did not start (#14374) * Some more logging * Some more logging * Move PR changes into pr.yml * Fix multiprocessing problems with setting __file__ (#14376) * Fix multiprocessing problems with setting __file__ * Update news entry * Problem with wait for idle not propagating outwards * Fix unnecessary ask for python extension install * Don't error on warning for kernel install
1 parent b993b34 commit 60fbc79

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+651
-1381
lines changed

.github/workflows/pr.yml

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ env:
2121
COVERAGE_REPORTS: tests-coverage-reports
2222
CI_PYTHON_PATH: python
2323
TEST_RESULTS_DIRECTORY: .
24-
TEST_RESULTS_GLOB: '**/test-results.xml'
24+
TEST_RESULTS_GLOB: '**/test-results*.xml'
2525

2626
jobs:
2727
build-vsix:
@@ -201,7 +201,7 @@ jobs:
201201
check_name: Ts-Unit Test Report
202202

203203
tests:
204-
name: Tests (with Python)
204+
name: Functional Jupyter Tests
205205
runs-on: ${{ matrix.os }}
206206
if: github.repository == 'microsoft/vscode-jupyter'
207207
strategy:
@@ -210,13 +210,8 @@ jobs:
210210
# We're not running CI on macOS for now because it's one less matrix entry to lower the number of runners used,
211211
# macOS runners are expensive, and we assume that Ubuntu is enough to cover the UNIX case.
212212
os: [ubuntu-latest]
213-
python: [3.8] # Use flaky tests to run against more versions of Python.
214-
# python-unit: Python tests
215-
# functional: Tests with mocked VS Code, & mocked Python)
216-
# functional-with-jupyter: Tests with mocked VS Code & real jupyter
217-
# single-workspace: Tests with VS Code
218-
# vscode: Tests with VS Code, Python extension & real Jupyter
219-
test-suite: [python-unit, functional, test-based-on-pr-body]
213+
python: [3.8]
214+
test-suite: [group1, group2, group3, group4]
220215
steps:
221216
- name: Checkout
222217
uses: actions/checkout@v2
@@ -240,7 +235,7 @@ jobs:
240235
# Caching (https://github.com/actions/cache/blob/main/examples.md#python---pip
241236
- name: Cache pip on linux
242237
uses: actions/cache@v2
243-
if: startsWith(matrix.test-suite, 'functional') && matrix.os == 'ubuntu-latest'
238+
if: matrix.os == 'ubuntu-latest'
244239
with:
245240
path: ~/.cache/pip
246241
key: ${{ runner.os }}-pip-${{env.PYTHON_VERSION}}-${{ hashFiles('requirements.txt') }}-${{hashFiles('build/debugger-install-requirements.txt')}}-${{hashFiles('test-requirements.txt')}}-${{hashFiles('ipython-test-requirements.txt')}}-${{hashFiles('functional-test-requirements.txt')}}-${{hashFiles('conda-functional-requirements.txt')}}
@@ -249,7 +244,7 @@ jobs:
249244
250245
- name: Cache pip on mac
251246
uses: actions/cache@v2
252-
if: startsWith(matrix.test-suite, 'functional') && matrix.os == 'macos-latest'
247+
if: matrix.os == 'macos-latest'
253248
with:
254249
path: ~/Library/Caches/pip
255250
key: ${{ runner.os }}-pip-${{env.PYTHON_VERSION}}-${{ hashFiles('requirements.txt') }}-${{hashFiles('build/debugger-install-requirements.txt')}}-${{hashFiles('test-requirements.txt')}}-${{hashFiles('ipython-test-requirements.txt')}}-${{hashFiles('functional-test-requirements.txt')}}-${{hashFiles('conda-functional-requirements.txt')}}
@@ -258,7 +253,7 @@ jobs:
258253
259254
- name: Cache pip on windows
260255
uses: actions/cache@v2
261-
if: startsWith(matrix.test-suite, 'functional') && matrix.os == 'windows-latest'
256+
if: matrix.os == 'windows-latest'
262257
with:
263258
path: ~\AppData\Local\pip\Cache
264259
key: ${{ runner.os }}-pip-${{env.PYTHON_VERSION}}-${{ hashFiles('requirements.txt') }}-${{hashFiles('build/debugger-install-requirements.txt')}}-${{hashFiles('test-requirements.txt')}}-${{hashFiles('ipython-test-requirements.txt')}}-${{hashFiles('functional-test-requirements.txt')}}-${{hashFiles('conda-functional-requirements.txt')}}
@@ -299,12 +294,12 @@ jobs:
299294
# For faster/better builds of sdists.
300295
- run: python -m pip install wheel
301296
shell: bash
302-
if: matrix.test-suite != 'test-based-on-pr-body' || contains(github.event.pull_request.body, '[x] Run ')
303297

304298
# debugpy is not shipped, only installed for local tests.
305299
# In production, we get debugpy from python extension.
306300
- name: Install functional test requirements
307301
run: |
302+
python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade -r ./requirements.txt
308303
python -m pip --disable-pip-version-check install -r build/debugger-install-requirements.txt
309304
python ./pythonFiles/install_debugpy.py
310305
python -m pip install numpy
@@ -314,56 +309,28 @@ jobs:
314309
python -m pip install --upgrade -r ./build/conda-functional-requirements.txt
315310
python -m ipykernel install --user
316311
# This step is slow.
317-
# If running the placeholder suite & not required to run any tests, then don't install python dependencies.
318-
if: matrix.test-suite != 'test-based-on-pr-body' || contains(github.event.pull_request.body, '[x] Run ')
319312

320313
- name: Install dependencies (npm ci)
321314
run: npm ci --prefer-offline
322315
# This step is slow.
323-
# If running the placeholder suite & not required to run any tests, then don't install python dependencies.
324-
if: matrix.test-suite != 'test-based-on-pr-body' || contains(github.event.pull_request.body, '[x] Run ')
325-
326-
# Run the Python and IPython tests in our codebase.
327-
- name: Run Python and IPython unit tests
328-
run: |
329-
python pythonFiles/tests/run_all.py
330-
python -m IPython pythonFiles/tests/run_all.py
331-
if: matrix.test-suite == 'python-unit'
332316

333317
- name: Compile if not cached
334318
run: npx gulp prePublishNonBundle
335-
# If running the placeholder suite & not required to run any tests, then don't compile.
336-
# if: steps.out-cache.outputs.cache-hit == false && (matrix.test-suite != 'test-based-on-pr-body' || contains(github.event.pull_request.body, '[x] Run '))
337-
if: matrix.test-suite != 'test-based-on-pr-body' || contains(github.event.pull_request.body, '[x] Run ')
338319

339320
- name: Run functional tests
340-
run: npm run test:functional
341-
id: test_functional
342-
if: matrix.test-suite == 'functional'
343-
344-
- name: Run single-workspace tests
345-
env:
346-
CI_PYTHON_VERSION: ${{matrix.python}}
347-
uses: GabrielBB/[email protected]
348-
with:
349-
run: npm run testSingleWorkspace
350-
if: matrix.test-suite == 'single-workspace' || (matrix.test-suite == 'test-based-on-pr-body' && contains(github.event.pull_request.body, '[x] Run single-workspace test'))
351-
352-
- name: Run functional tests with Jupyter
353-
run: npm run test:functional -- --grep="MimeTypes"
354-
id: test_functional_jupyter
321+
run: npm run test:functional:parallel -- --${{matrix.test-suite}}
355322
env:
356323
VSCODE_PYTHON_ROLLING: 1
357324
VSC_PYTHON_FORCE_LOGGING: 1
358-
if: matrix.test-suite == 'functional-with-jupyter' || (matrix.test-suite == 'test-based-on-pr-body' && contains(github.event.pull_request.body, '[x] Run functional-with-jupyter test'))
325+
id: test_functional_group
359326

360327
- name: Publish Test Report
361328
uses: scacap/action-surefire-report@v1
362329
with:
363330
github_token: ${{ secrets.GITHUB_TOKEN }}
364331
report_paths: ${{ env.TEST_RESULTS_GLOB }}
365-
check_name: Functional Test Report
366-
if: (steps.test_functional.outcome == 'failure' || steps.test_functional_jupyter.outcome == 'failure') && failure()
332+
check_name: Functional Test Report ${{matrix.test-suite}}
333+
if: steps.test_functional_group.outcome == 'failure' && failure()
367334

368335
- name: Run DataScience tests with VSCode & Jupyter
369336
uses: GabrielBB/[email protected]

.vscode/launch.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,9 @@
296296
// Some tests require multiple python interpreters (do not rely on discovery for functional tests, be explicit).
297297
"XCI_PYTHON_PATH2": "<Python Path>",
298298
// Remove 'X' prefix to dump output for debugger. Directory has to exist prior to launch
299-
"XDEBUGPY_LOG_DIR": "${workspaceRoot}/tmp/Debug_Output"
299+
"XDEBUGPY_LOG_DIR": "${workspaceRoot}/tmp/Debug_Output",
300+
// Remove 'X' prefix to dump webview redux action log
301+
"XVSC_PYTHON_WEBVIEW_LOG_FILE": "${workspaceRoot}/test-webview.log"
300302
},
301303
"outFiles": [
302304
"${workspaceFolder}/out/**/*.js",

ThirdPartyNotices-Repository.txt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Microsoft Python extension for Visual Studio Code incorporates third party mater
2020
16. ipywidgets (https://github.com/jupyter-widgets)
2121
17. vscode-cpptools (https://github.com/microsoft/vscode-cpptools)
2222
18. font-awesome (https://github.com/FortAwesome/Font-Awesome)
23+
19. mocha (https://github.com/mochajs/mocha)
2324

2425
%%
2526
Go for Visual Studio Code NOTICES, INFORMATION, AND LICENSE BEGIN HERE
@@ -1167,4 +1168,33 @@ trademarks does not indicate endorsement of the trademark holder by Font
11671168
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
11681169
to represent the company, product, or service to which they refer.**
11691170
=========================================
1170-
END OF font-awesome NOTICES, INFORMATION, AND LICENSE
1171+
END OF font-awesome NOTICES, INFORMATION, AND LICENSE
1172+
1173+
%% mocha NOTICES, INFORMATION, AND LICENSE BEGIN HERE
1174+
=========================================
1175+
1176+
(The MIT License)
1177+
1178+
Copyright (c) 2011-2020 OpenJS Foundation and contributors, https://openjsf.org
1179+
1180+
Permission is hereby granted, free of charge, to any person obtaining
1181+
a copy of this software and associated documentation files (the
1182+
'Software'), to deal in the Software without restriction, including
1183+
without limitation the rights to use, copy, modify, merge, publish,
1184+
distribute, sublicense, and/or sell copies of the Software, and to
1185+
permit persons to whom the Software is furnished to do so, subject to
1186+
the following conditions:
1187+
1188+
The above copyright notice and this permission notice shall be
1189+
included in all copies or substantial portions of the Software.
1190+
1191+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
1192+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1193+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1194+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1195+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1196+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1197+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1198+
1199+
=========================================
1200+
END OF mocha NOTICES, INFORMATION, AND LICENSE

build/.mocha-multi-reporters.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"reporterEnabled": "spec,mocha-junit-reporter",
2+
"reporterEnabled": "./build/ci/scripts/spec_with_pid,mocha-junit-reporter",
33
"mochaJunitReporterReporterOptions": {
44
"includePending": true
55
}

build/ci/scripts/runFunctionalTests.js

Lines changed: 109 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,93 @@
88
var path = require('path');
99
var glob = require('glob');
1010
var child_process = require('child_process');
11+
var fs = require('fs-extra');
1112

1213
// Create a base for the output file
1314
var originalMochaFile = process.env['MOCHA_FILE'];
1415
var mochaFile = originalMochaFile || './test-results.xml';
1516
var mochaBaseFile = path.join(path.dirname(mochaFile), path.basename(mochaFile, '.xml'));
1617
var mochaFileExt = '.xml';
18+
var groupCount = 4;
19+
20+
function gatherArgs(extraArgs, file) {
21+
return [
22+
file,
23+
'--require=out/test/unittests.js',
24+
'--exclude=out/**/*.jsx',
25+
'--reporter=mocha-multi-reporters',
26+
'--reporter-option=configFile=build/.mocha-multi-reporters.config',
27+
'--ui=tdd',
28+
'--recursive',
29+
'--colors',
30+
'--exit',
31+
'--timeout=180000',
32+
...extraArgs
33+
];
34+
}
35+
36+
async function generateGroups(files) {
37+
// Go through each file putting it into a bucket. Each bucket will attempt to
38+
// have equal size
39+
40+
// Start with largest files first (sort by size)
41+
var stats = await Promise.all(files.map((f) => fs.stat(f)));
42+
var filesWithSize = files.map((f, i) => {
43+
return {
44+
file: f,
45+
size: stats[i].size
46+
};
47+
});
48+
var sorted = filesWithSize.sort((a, b) => b.size - a.size);
49+
50+
// Generate buckets that try to hold the largest file first
51+
var buckets = new Array(groupCount).fill().map((_, i) => {
52+
return {
53+
index: i,
54+
totalSize: 0,
55+
files: []
56+
};
57+
});
58+
var lowestBucket = buckets[0];
59+
sorted.forEach((fs) => {
60+
buckets[lowestBucket.index].totalSize += fs.size;
61+
buckets[lowestBucket.index].files.push(fs.file);
62+
lowestBucket = buckets.find((b) => b.totalSize < lowestBucket.totalSize) || lowestBucket;
63+
});
64+
65+
// Return these groups of files
66+
return buckets.map((b) => b.files);
67+
}
68+
69+
async function runIndividualTest(extraArgs, file, index) {
70+
var subMochaFile = `${mochaBaseFile}_${index}_${path.basename(file)}${mochaFileExt}`;
71+
var args = gatherArgs(extraArgs, file);
72+
console.log(`Running functional test for file ${file} ...`);
73+
var exitCode = await new Promise((resolve) => {
74+
// Spawn the sub node process
75+
var proc = child_process.fork('./node_modules/mocha/bin/_mocha', args, {
76+
env: { ...process.env, MOCHA_FILE: subMochaFile }
77+
});
78+
proc.on('exit', resolve);
79+
});
80+
81+
// If failed keep track
82+
if (exitCode !== 0) {
83+
console.log(`Functional tests for ${file} failed.`);
84+
} else {
85+
console.log(`Functional test for ${file} succeeded`);
86+
}
87+
88+
return exitCode;
89+
}
1790

1891
// Wrap async code in a function so can wait till done
1992
async function main() {
2093
console.log('Globbing files for functional tests');
2194

2295
// Glob all of the files that we usually send to mocha as a group (see mocha.functional.opts.xml)
2396
var files = await new Promise((resolve, reject) => {
24-
glob('./out/test/**/*.functional.test.js', (ex, res) => {
97+
glob('./out/test/datascience/**/*.functional.test.js', (ex, res) => {
2598
if (ex) {
2699
reject(ex);
27100
} else {
@@ -30,38 +103,42 @@ async function main() {
30103
});
31104
});
32105

106+
// Figure out what group is running (should be something like --group1, --group2 etc.)
107+
var groupArgIndex = process.argv.findIndex((a) => a.includes('--group'));
108+
var groupIndex = groupArgIndex >= 0 ? parseInt(process.argv[groupArgIndex].slice(7), 10) - 1 : -1;
109+
110+
// Generate 4 groups based on sorting by size
111+
var groups = await generateGroups(files);
112+
files = groupIndex >= 0 ? groups[groupIndex] : files;
113+
console.log(`Running for group ${groupIndex}`);
114+
115+
// Extract any extra args for the individual mocha processes
116+
var extraArgs =
117+
groupIndex >= 0 && process.argv.length > 3
118+
? process.argv.slice(3)
119+
: process.argv.length > 2
120+
? process.argv.slice(2)
121+
: [];
122+
33123
// Iterate over them, running mocha on each
34124
var returnCode = 0;
35125

36-
// Go through each one at a time
126+
// Start timing now (don't care about glob time)
127+
var startTime = Date.now();
128+
129+
// Run all of the tests (in parallel or sync based on env)
37130
try {
38-
for (var index = 0; index < files.length; index += 1) {
39-
// Each run with a file will expect a $MOCHA_FILE$ variable. Generate one for each
40-
// Note: this index is used as a pattern when setting mocha file in the test_phases.yml
41-
var subMochaFile = `${mochaBaseFile}_${index}_${path.basename(files[index])}${mochaFileExt}`;
42-
process.env['MOCHA_FILE'] = subMochaFile;
43-
var exitCode = await new Promise((resolve) => {
44-
// Spawn the sub node process
45-
var proc = child_process.fork('./node_modules/mocha/bin/_mocha', [
46-
files[index],
47-
'--require=out/test/unittests.js',
48-
'--exclude=out/**/*.jsx',
49-
'--reporter=mocha-multi-reporters',
50-
'--reporter-option=configFile=build/.mocha-multi-reporters.config',
51-
'--ui=tdd',
52-
'--recursive',
53-
'--colors',
54-
'--exit',
55-
'--timeout=180000'
56-
]);
57-
proc.on('exit', resolve);
58-
});
59-
60-
// If failed keep track
61-
if (exitCode !== 0) {
62-
console.log(`Functional tests for ${files[index]} failed.`);
63-
returnCode = exitCode;
131+
if (process.env.VSCODE_PYTHON_FORCE_TEST_SYNC) {
132+
for (var i = 0; i < files.length; i += 1) {
133+
// Synchronous, one at a time
134+
returnCode = returnCode | (await runIndividualTest(extraArgs, files[i], i));
64135
}
136+
} else {
137+
// Parallel, all at once
138+
const returnCodes = await Promise.all(files.map(runIndividualTest.bind(undefined, extraArgs)));
139+
140+
// Or all of the codes together
141+
returnCode = returnCodes.reduce((p, c) => p | c);
65142
}
66143
} catch (ex) {
67144
console.log(`Functional tests run failure: ${ex}.`);
@@ -73,8 +150,10 @@ async function main() {
73150
process.env['MOCHA_FILE'] = originalMochaFile;
74151
}
75152

76-
// Indicate error code
77-
console.log(`Functional test run result: ${returnCode}`);
153+
var endTime = Date.now();
154+
155+
// Indicate error code and total time of the run
156+
console.log(`Functional test run result: ${returnCode} after ${(endTime - startTime) / 1_000} seconds`);
78157
process.exit(returnCode);
79158
}
80159

0 commit comments

Comments
 (0)