diff --git a/.eslintrc.json b/.eslintrc.json
index e7506985bd3..fada4705598 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -614,6 +614,13 @@
"*"
]
},
+ {
+ "target": "**/test/integration/**",
+ "restrictions": [
+ "**/test/integration/**",
+ "*"
+ ]
+ },
{
"target": "{**/api/**.test.ts,}",
"restrictions": "{**/vs/**,assert,sinon,crypto,vscode}"
diff --git a/build/azure-pipelines/darwin/entitlements.plist b/build/azure-pipelines/darwin/entitlements.plist
new file mode 100644
index 00000000000..46f67566114
--- /dev/null
+++ b/build/azure-pipelines/darwin/entitlements.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+
+
diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml
index 50c85822a53..55f55e0eade 100644
--- a/build/azure-pipelines/darwin/product-build-darwin.yml
+++ b/build/azure-pipelines/darwin/product-build-darwin.yml
@@ -44,6 +44,13 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
+
+ security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
+ security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
+ security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
+ echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
+ security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
displayName: Prepare tooling
- script: |
@@ -127,7 +134,12 @@ steps:
- script: |
set -e
- pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd
+ codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist $(agent.builddirectory)/VSCode-darwin/*.app
+ displayName: Set Hardened Entitlements
+
+- script: |
+ set -e
+ pushd $(agent.builddirectory)/VSCode-darwin && zip -r -X -y $(agent.builddirectory)/VSCode-darwin.zip * && popd
displayName: Archive build
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
@@ -141,13 +153,53 @@ steps:
{
"keyCode": "CP-401337-Apple",
"operationSetCode": "MacAppDeveloperSign",
- "parameters": [ ],
+ "parameters": [
+ {
+ "parameterName": "Hardening",
+ "parameterValue": "--options=runtime"
+ }
+ ],
+ "toolName": "sign",
+ "toolVersion": "1.0"
+ }
+ ]
+ SessionTimeout: 60
+ displayName: Codesign
+
+- script: |
+ zip -d $(agent.builddirectory)/VSCode-darwin.zip "*.pkg"
+ displayName: Clean Archive
+
+- script: |
+ APP_ROOT=$(agent.builddirectory)/VSCode-darwin
+ APP_NAME="`ls $APP_ROOT | head -n 1`"
+ BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier")
+ echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER"
+ displayName: Export bundle identifier
+
+- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
+ inputs:
+ ConnectedServiceName: 'ESRP CodeSign'
+ FolderPath: '$(agent.builddirectory)'
+ Pattern: 'VSCode-darwin.zip'
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "keyCode": "CP-401337-Apple",
+ "operationSetCode": "MacAppNotarize",
+ "parameters": [
+ {
+ "parameterName": "BundleId",
+ "parameterValue": "$(BundleIdentifier)"
+ }
+ ],
"toolName": "sign",
"toolVersion": "1.0"
}
]
SessionTimeout: 120
- displayName: Codesign
+ displayName: Notarization
- script: |
set -e
diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh
index a8067a5eefb..58f110c5df5 100755
--- a/build/azure-pipelines/darwin/publish.sh
+++ b/build/azure-pipelines/darwin/publish.sh
@@ -1,9 +1,6 @@
#!/usr/bin/env bash
set -e
-# remove pkg from archive
-zip -d ../VSCode-darwin.zip "*.pkg"
-
# publish the build
node build/azure-pipelines/common/createAsset.js \
darwin \
diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js
index 9af81603c8a..c0b16af0ea7 100644
--- a/build/gulpfile.hygiene.js
+++ b/build/gulpfile.hygiene.js
@@ -53,7 +53,7 @@ const indentationFilter = [
'!src/vs/base/common/marked/marked.js',
'!src/vs/base/node/terminateProcess.sh',
'!src/vs/base/node/cpuUsage.sh',
- '!test/assert.js',
+ '!test/unit/assert.js',
// except specific folders
'!test/automation/out/**',
@@ -82,7 +82,7 @@ const indentationFilter = [
'!src/vs/*/**/*.d.ts',
'!src/typings/**/*.d.ts',
'!extensions/**/*.d.ts',
- '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns}',
+ '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
'!build/{lib,download}/**/*.js',
'!build/**/*.sh',
'!build/azure-pipelines/**/*.js',
diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js
index 83e31e8b853..5a937a80bd8 100644
--- a/build/npm/postinstall.js
+++ b/build/npm/postinstall.js
@@ -72,4 +72,5 @@ runtime "${runtime}"`;
yarnInstall(`build`); // node modules required for build
yarnInstall('test/automation'); // node modules required for smoketest
yarnInstall('test/smoke'); // node modules required for smoketest
+yarnInstall('test/integration/browser'); // node modules required for integration
yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron
diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
index aba94541010..275ecf3003c 100644
--- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
+++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
@@ -69,17 +69,16 @@ suite('Debug', function () {
})
}));
- const capabilitiesPromise = new Promise(resolve => capabilitiesReceived = resolve);
const initializedPromise = new Promise(resolve => initializedReceived = resolve);
const configurationDonePromise = new Promise(resolve => configurationDoneReceived = resolve);
// Do not await debug start to return due to https://github.com/microsoft/vscode/issues/90134
debug.startDebugging(workspace.workspaceFolders![0], 'Launch debug.js');
- await capabilitiesPromise;
await initializedPromise;
await configurationDonePromise;
- assert.notEqual(debug.activeDebugSession, undefined);
- assert.equal(debug.activeDebugSession?.name, 'Launch debug.js');
+ // Do not verify activeDebugSession due to same flakiness that sometimes start debugging does not return
+ // assert.notEqual(debug.activeDebugSession, undefined);
+ // assert.equal(debug.activeDebugSession?.name, 'Launch debug.js');
await firstVariablesRetrieved;
assert.equal(stoppedEvents, 1);
diff --git a/package.json b/package.json
index 861d97d248f..e5da56c97a7 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,6 @@
"eslint": "6.8.0",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",
- "express": "^4.13.1",
"fancy-log": "^1.3.3",
"fast-plist": "0.1.2",
"glob": "^5.0.13",
@@ -158,7 +157,8 @@
"vscode-nls-dev": "^3.3.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.3.8",
- "webpack-stream": "^5.1.1"
+ "webpack-stream": "^5.1.1",
+ "yaserver": "^0.2.0"
},
"repository": {
"type": "git",
diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts
index 6b35d6afc9c..495a1ddf636 100644
--- a/src/vs/base/test/common/uri.test.ts
+++ b/src/vs/base/test/common/uri.test.ts
@@ -404,7 +404,7 @@ suite('URI', () => {
path = 'foo/bar';
assert.equal(URI.file(path).path, '/foo/bar');
path = './foo/bar';
- assert.equal(URI.file(path).path, '/./foo/bar'); // todo@joh missing normalization
+ assert.equal(URI.file(path).path, '/./foo/bar'); // missing normalization
const fileUri1 = URI.parse(`file:foo/bar`);
assert.equal(fileUri1.path, '/foo/bar');
diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts
index cc8a37fcf6b..6feeecc03ac 100644
--- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts
+++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts
@@ -5,6 +5,7 @@
import { RGBA8 } from 'vs/editor/common/core/rgba';
import { Constants, getCharIndex } from './minimapCharSheet';
+import { toUint8 } from 'vs/base/common/uint';
export class MinimapCharRenderer {
_minimapCharRendererBrand: void;
@@ -20,7 +21,7 @@ export class MinimapCharRenderer {
private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray {
let result = new Uint8ClampedArray(input.length);
for (let i = 0, len = input.length; i < len; i++) {
- result[i] = input[i] * ratio;
+ result[i] = toUint8(input[i] * ratio);
}
return result;
}
diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts
index 585f5d01509..46c6c8e27a9 100644
--- a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts
+++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts
@@ -7,6 +7,7 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap
import { allCharCodes } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet';
import { prebakedMiniMaps } from 'vs/editor/browser/viewParts/minimap/minimapPreBaked';
import { Constants } from './minimapCharSheet';
+import { toUint8 } from 'vs/base/common/uint';
/**
* Creates character renderers. It takes a 'scale' that determines how large
@@ -135,7 +136,7 @@ export class MinimapCharRendererFactory {
const final = value / samples;
brightest = Math.max(brightest, final);
- dest[targetIndex++] = final;
+ dest[targetIndex++] = toUint8(final);
}
}
diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts
index 1f2df5a96b7..0afaa0abca4 100644
--- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts
+++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts
@@ -45,11 +45,13 @@ function canSyncModel(modelService: IModelService, resource: URI): boolean {
}
export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService {
- public _serviceBrand: undefined;
+
+ _serviceBrand: undefined;
private readonly _modelService: IModelService;
private readonly _workerManager: WorkerManager;
private readonly _logService: ILogService;
+
constructor(
@IModelService modelService: IModelService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@@ -60,7 +62,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
this._workerManager = this._register(new WorkerManager(this._modelService));
this._logService = logService;
- // todo@joh make sure this happens only once
+ // register default link-provider and default completions-provider
this._register(modes.LinkProviderRegistry.register('*', {
provideLinks: (model, token) => {
if (!canSyncModel(this._modelService, model.uri)) {
diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
index d61207f384e..1bb98526f71 100644
--- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
+++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
@@ -154,6 +154,7 @@ suite('Decoration Render Options', () => {
sheet.indexOf('background: url(\'file:///c%3A/files/miles/more.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c%3A/files/miles/more.png") center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c:/files/miles/more.png") center center no-repeat;') > 0
+ || sheet.indexOf('background-image: url("file:///c:/files/miles/more.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
}
diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts
index 56281b80706..b0b0762997c 100644
--- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts
+++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts
@@ -10,22 +10,6 @@ import { MinimapCharRendererFactory } from 'vs/editor/browser/viewParts/minimap/
suite('MinimapCharRenderer', () => {
- let sampleData: Uint8ClampedArray | null = null;
-
- suiteSetup(() => {
- sampleData = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
- });
-
- suiteTeardown(() => {
- sampleData = null;
- });
-
- setup(() => {
- for (let i = 0; i < sampleData!.length; i++) {
- sampleData![i] = 0;
- }
- });
-
const sampleD = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x00, 0x00, 0x00, 0x00,
@@ -45,7 +29,13 @@ suite('MinimapCharRenderer', () => {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
- function setSampleData(charCode: number, data: number[]) {
+ function getSampleData() {
+ const charCode = 'd'.charCodeAt(0);
+ const result = new Uint8ClampedArray(Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
+ for (let i = 0; i < result.length; i++) {
+ result[i] = 0;
+ }
+
const rowWidth = Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT;
let chIndex = charCode - Constants.START_CH_CODE;
@@ -55,13 +45,15 @@ suite('MinimapCharRenderer', () => {
let outputOffset = globalOutputOffset;
for (let j = 0; j < Constants.SAMPLED_CHAR_WIDTH; j++) {
for (let channel = 0; channel < Constants.RGBA_CHANNELS_CNT; channel++) {
- sampleData![outputOffset] = data[inputOffset];
+ result[outputOffset] = sampleD[inputOffset];
inputOffset++;
outputOffset++;
}
}
globalOutputOffset += rowWidth;
}
+
+ return result;
}
function createFakeImageData(width: number, height: number): ImageData {
@@ -73,8 +65,8 @@ suite('MinimapCharRenderer', () => {
}
test('letter d @ 2x', () => {
- setSampleData('d'.charCodeAt(0), sampleD);
- let renderer = MinimapCharRendererFactory.createFromSampleData(sampleData!, 2);
+ const sampleData = getSampleData();
+ let renderer = MinimapCharRendererFactory.createFromSampleData(sampleData, 2);
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
@@ -94,16 +86,16 @@ suite('MinimapCharRenderer', () => {
}
assert.deepEqual(actual, [
- 0x2E, 0x2E, 0x2E, 0xFF, 0xAD, 0xAD, 0xAD, 0xFF,
+ 0x2D, 0x2D, 0x2D, 0xFF, 0xAC, 0xAC, 0xAC, 0xFF,
0xC6, 0xC6, 0xC6, 0xFF, 0xC8, 0xC8, 0xC8, 0xFF,
- 0xC1, 0xC1, 0xC1, 0xFF, 0xCC, 0xCC, 0xCC, 0xFF,
+ 0xC0, 0xC0, 0xC0, 0xFF, 0xCB, 0xCB, 0xCB, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
]);
});
test('letter d @ 1x', () => {
- setSampleData('d'.charCodeAt(0), sampleD);
- let renderer = MinimapCharRendererFactory.createFromSampleData(sampleData!, 1);
+ const sampleData = getSampleData();
+ let renderer = MinimapCharRendererFactory.createFromSampleData(sampleData, 1);
let background = new RGBA8(0, 0, 0, 255);
let color = new RGBA8(255, 255, 255, 255);
@@ -125,7 +117,7 @@ suite('MinimapCharRenderer', () => {
assert.deepEqual(actual, [
0xCB, 0xCB, 0xCB, 0xFF,
- 0x82, 0x82, 0x82, 0xFF,
+ 0x81, 0x81, 0x81, 0xFF,
]);
});
diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts
index 5724540aa1f..3cf230cb66f 100644
--- a/src/vs/platform/userDataSync/common/extensionsSync.ts
+++ b/src/vs/platform/userDataSync/common/extensionsSync.ts
@@ -136,7 +136,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
- this.logService.info('Extensions: Failed to synchronise extensions as there is a new remote version available. Synchronizing again...');
+ this.logService.info('Extensions: Failed to synchronize extensions as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
@@ -211,16 +211,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (remote) {
// update remote
- this.logService.info('Extensions: Updating remote extensions...');
+ this.logService.trace('Extensions: Updating remote extensions...');
const content = JSON.stringify(remote);
const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
remoteUserData = { ref, content };
+ this.logService.info('Extensions: Updated remote extensions');
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
// update last sync
- this.logService.info('Extensions: Updating last synchronised extensions...');
+ this.logService.trace('Extensions: Updating last synchronized extensions...');
await this.updateLastSyncUserData({ ...remoteUserData, skippedExtensions });
+ this.logService.info('Extensions: Updated last synchronized extensions');
}
}
@@ -232,8 +234,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
const extensionsToRemove = installedExtensions.filter(({ identifier }) => removed.some(r => areSameExtensions(identifier, r)));
await Promise.all(extensionsToRemove.map(async extensionToRemove => {
- this.logService.info('Extensions: Removing local extension.', extensionToRemove.identifier.id);
+ this.logService.trace('Extensions: Uninstalling local extension...', extensionToRemove.identifier.id);
await this.extensionManagementService.uninstall(extensionToRemove);
+ this.logService.info('Extensions: Uninstalled local extension.', extensionToRemove.identifier.id);
removeFromSkipped.push(extensionToRemove.identifier);
}));
}
@@ -246,11 +249,13 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
// Builtin Extension: Sync only enablement state
if (installedExtension && installedExtension.type === ExtensionType.System) {
if (e.enabled) {
- this.logService.info('Extensions: Enabling extension.', e.identifier.id);
+ this.logService.trace('Extensions: Enabling extension...', e.identifier.id);
await this.extensionEnablementService.enableExtension(e.identifier);
+ this.logService.info('Extensions: Enabled extension', e.identifier.id);
} else {
- this.logService.info('Extensions: Disabling extension.', e.identifier.id);
+ this.logService.trace('Extensions: Disabling extension...', e.identifier.id);
await this.extensionEnablementService.disableExtension(e.identifier);
+ this.logService.info('Extensions: Disabled extension.', e.identifier.id);
}
removeFromSkipped.push(e.identifier);
return;
@@ -260,22 +265,25 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (extension) {
try {
if (e.enabled) {
- this.logService.info('Extensions: Enabling extension.', e.identifier.id, extension.version);
+ this.logService.trace('Extensions: Enabling extension...', e.identifier.id, extension.version);
await this.extensionEnablementService.enableExtension(extension.identifier);
+ this.logService.info('Extensions: Enabled extension', e.identifier.id, extension.version);
} else {
- this.logService.info('Extensions: Disabling extension.', e.identifier.id, extension.version);
+ this.logService.trace('Extensions: Disabling extension...', e.identifier.id, extension.version);
await this.extensionEnablementService.disableExtension(extension.identifier);
+ this.logService.info('Extensions: Disabled extension', e.identifier.id, extension.version);
}
// Install only if the extension does not exist
if (!installedExtension || installedExtension.manifest.version !== extension.version) {
- this.logService.info('Extensions: Installing extension.', e.identifier.id, extension.version);
+ this.logService.trace('Extensions: Installing extension...', e.identifier.id, extension.version);
await this.extensionManagementService.installFromGallery(extension);
+ this.logService.info('Extensions: Installed extension.', e.identifier.id, extension.version);
removeFromSkipped.push(extension.identifier);
}
} catch (error) {
addToSkipped.push(e);
this.logService.error(error);
- this.logService.info(localize('skip extension', "Skipping synchronising extension {0}", extension.displayName || extension.identifier.id));
+ this.logService.info(localize('skip extension', "Skipped synchronizing extension {0}", extension.displayName || extension.identifier.id));
}
} else {
addToSkipped.push(e);
diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts
index c20024d1f20..bd125de964a 100644
--- a/src/vs/platform/userDataSync/common/globalStateSync.ts
+++ b/src/vs/platform/userDataSync/common/globalStateSync.ts
@@ -118,7 +118,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
- this.logService.info('UI State: Failed to synchronise ui state as there is a new remote version available. Synchronizing again...');
+ this.logService.info('UI State: Failed to synchronize ui state as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
@@ -183,22 +183,25 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
if (local) {
// update local
- this.logService.info('UI State: Updating local ui state...');
+ this.logService.trace('UI State: Updating local ui state...');
await this.writeLocalGlobalState(local);
+ this.logService.info('UI State: Updated local ui state');
}
if (remote) {
// update remote
- this.logService.info('UI State: Updating remote ui state...');
+ this.logService.trace('UI State: Updating remote ui state...');
const content = JSON.stringify(remote);
const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
+ this.logService.info('UI State: Updated remote ui state');
remoteUserData = { ref, content };
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
// update last sync
- this.logService.info('UI State: Updating last synchronised ui state...');
+ this.logService.trace('UI State: Updating last synchronized ui state...');
await this.updateLastSyncUserData(remoteUserData);
+ this.logService.info('UI State: Updated last synchronized ui state');
}
}
diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts
index 76083e2a537..464f50fc04d 100644
--- a/src/vs/platform/userDataSync/common/keybindingsSync.ts
+++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts
@@ -176,10 +176,9 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
await this.apply(content, true);
this.setStatus(SyncStatus.Idle);
} catch (e) {
- this.logService.error(e);
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
- throw new Error('Failed to resolve conflicts as there is a new local version available.');
+ throw new UserDataSyncError('Failed to resolve conflicts as there is a new local version available.', UserDataSyncErrorCode.NewLocal);
}
throw e;
}
@@ -235,13 +234,13 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
- this.logService.info('Keybindings: Failed to synchronise keybindings as there is a new remote version available. Synchronizing again...');
+ this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new remote version available. Synchronizing again...');
return this.sync();
}
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
// Rejected as there is a new local version. Syncing again.
- this.logService.info('Keybindings: Failed to synchronise keybindings as there is a new local version available. Synchronizing again...');
+ this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...');
return this.sync();
}
throw e;
@@ -274,15 +273,17 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
}
if (hasLocalChanged) {
- this.logService.info('Keybindings: Updating local keybindings');
+ this.logService.trace('Keybindings: Updating local keybindings...');
await this.updateLocalFileContent(content, fileContent);
+ this.logService.info('Keybindings: Updated local keybindings');
}
if (hasRemoteChanged) {
- this.logService.info('Keybindings: Updating remote keybindings');
+ this.logService.trace('Keybindings: Updating remote keybindings...');
const remoteContents = this.updateSyncContent(content, remoteUserData.content);
const ref = await this.updateRemoteUserData(remoteContents, forcePush ? null : remoteUserData.ref);
remoteUserData = { ref, content: remoteContents };
+ this.logService.info('Keybindings: Updated remote keybindings');
}
// Delete the preview
@@ -292,9 +293,10 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
}
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== undefined || fileContent !== null)) {
- this.logService.info('Keybindings: Updating last synchronised keybindings');
+ this.logService.trace('Keybindings: Updating last synchronized keybindings...');
const lastSyncContent = this.updateSyncContent(content !== undefined ? content : fileContent!.value.toString(), null);
await this.updateLastSyncUserData({ ref: remoteUserData.ref, content: lastSyncContent });
+ this.logService.info('Keybindings: Updated last synchronized keybindings');
}
this.syncPreviewResultPromise = null;
diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts
index bd702546688..9b223976b9a 100644
--- a/src/vs/platform/userDataSync/common/settingsSync.ts
+++ b/src/vs/platform/userDataSync/common/settingsSync.ts
@@ -245,10 +245,9 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
await this.apply(true);
this.setStatus(SyncStatus.Idle);
} catch (e) {
- this.logService.error(e);
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
- throw new Error('New local version available.');
+ throw new UserDataSyncError('Failed to resolve conflicts as there is a new local version available.', UserDataSyncErrorCode.NewLocal);
}
throw e;
}
@@ -282,13 +281,13 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
- this.logService.info('Settings: Failed to synchronise settings as there is a new remote version available. Synchronizing again...');
+ this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...');
return this.sync();
}
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
// Rejected as there is a new local version. Syncing again.
- this.logService.info('Settings: Failed to synchronise settings as there is a new local version available. Synchronizing again...');
+ this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
return this.sync();
}
throw e;
@@ -311,15 +310,17 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
if (hasLocalChanged) {
- this.logService.info('Settings: Updating local settings');
+ this.logService.trace('Settings: Updating local settings...');
await this.updateLocalFileContent(content, fileContent);
+ this.logService.info('Settings: Updated local settings');
}
if (hasRemoteChanged) {
const formatUtils = await this.getFormattingOptions();
// Update ignored settings from remote
content = updateIgnoredSettings(content, remoteUserData.content || '{}', getIgnoredSettings(this.configurationService, content), formatUtils);
- this.logService.info('Settings: Updating remote settings');
+ this.logService.trace('Settings: Updating remote settings...');
const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
+ this.logService.info('Settings: Updated remote settings');
remoteUserData = { ref, content };
}
@@ -330,8 +331,9 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
- this.logService.info('Settings: Updating last synchronised settings');
+ this.logService.trace('Settings: Updating last synchronized settings...');
await this.updateLastSyncUserData(remoteUserData);
+ this.logService.info('Settings: Updated last synchronized settings');
}
this.syncPreviewResultPromise = null;
diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
index 0a169d45b34..e401fb270aa 100644
--- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
@@ -84,7 +84,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
this.sync(loop, true);
}
} else {
- this.logService.trace('Not syncing as it is disabled.');
+ this.logService.trace('Auto Sync: Not syncing as it is disabled.');
}
}
@@ -107,7 +107,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
async triggerAutoSync(): Promise {
if (this.enabled) {
return this.syncDelayer.trigger(() => {
- this.logService.info('Sync: Triggerred.');
+ this.logService.info('Auto Sync: Triggerred.');
return this.sync(false, true);
}, this.successiveFailures
? 1000 * 1 * Math.min(this.successiveFailures, 60) /* Delay by number of seconds as number of failures up to 1 minute */
diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts
index 68b16a286c9..60495654ccd 100644
--- a/src/vs/platform/userDataSync/common/userDataSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataSync.ts
@@ -130,6 +130,7 @@ export enum UserDataSyncErrorCode {
Rejected = 'Rejected',
TooLarge = 'TooLarge',
NoRef = 'NoRef',
+ NewLocal = 'NewLocal',
Unknown = 'Unknown',
}
@@ -137,6 +138,18 @@ export class UserDataSyncError extends Error {
constructor(message: string, public readonly code: UserDataSyncErrorCode, public readonly source?: SyncSource) {
super(message);
+ this.name = `${this.code} (UserDataSyncError) ${this.source}`;
+ }
+
+ static toUserDataSyncError(error: Error): UserDataSyncError {
+ if (error instanceof UserDataSyncStoreError) {
+ return error;
+ }
+ const match = /^(.+) \(UserDataSyncError\) (.+)?$/.exec(error.name);
+ if (match && match[1]) {
+ return new UserDataSyncError(error.message, match[1], match[2]);
+ }
+ return new UserDataSyncError(error.message, UserDataSyncErrorCode.Unknown);
}
}
@@ -222,7 +235,7 @@ export interface IUserDataSyncService extends ISynchroniser {
export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService');
export interface IUserDataAutoSyncService {
_serviceBrand: any;
- onError: Event<{ code: UserDataSyncErrorCode, source?: SyncSource }>;
+ readonly onError: Event<{ code: UserDataSyncErrorCode, source?: SyncSource }>;
triggerAutoSync(): Promise;
}
diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts
index 9d67caea8bb..10a4c4599c7 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts
@@ -3,10 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter, Event } from 'vs/base/common/event';
import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync';
import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
@@ -19,6 +18,10 @@ type SyncConflictsClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
+type SyncErrorClassification = {
+ source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
+};
+
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
_serviceBrand: any;
@@ -73,7 +76,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.pull();
} catch (e) {
- this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`);
+ this.handleSyncError(e, synchroniser.source);
}
}
}
@@ -89,7 +92,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.push();
} catch (e) {
- this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`);
+ this.handleSyncError(e, synchroniser.source);
}
}
}
@@ -114,10 +117,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
break;
}
} catch (e) {
- if (e instanceof UserDataSyncStoreError) {
- throw e;
- }
- this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`);
+ this.handleSyncError(e, synchroniser.source);
}
}
this.logService.trace(`Finished Syncing. Took ${new Date().getTime() - startTime}ms`);
@@ -249,7 +249,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.resetLocal();
} catch (e) {
- this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`);
+ this.logService.error(`${synchroniser.source}: ${toErrorMessage(e)}`);
+ this.logService.error(e);
}
}
this.logService.info('Completed resetting local cache');
@@ -287,9 +288,21 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return SyncStatus.Idle;
}
+ private handleSyncError(e: Error, source: SyncSource): void {
+ if (e instanceof UserDataSyncStoreError) {
+ switch (e.code) {
+ case UserDataSyncErrorCode.TooLarge:
+ this.telemetryService.publicLog2<{ source: string }, SyncErrorClassification>('sync/errorTooLarge', { source });
+ }
+ throw e;
+ }
+ this.logService.error(e);
+ this.logService.error(`${source}: ${toErrorMessage(e)}`);
+ }
+
private computeConflictsSource(): SyncSource | null {
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
- return synchroniser ? this.getSyncSource(synchroniser) : null;
+ return synchroniser ? synchroniser.source : null;
}
private getSynchroniserInConflicts(): IUserDataSynchroniser | null {
@@ -297,19 +310,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return synchroniser || null;
}
- private getSyncSource(synchroniser: ISynchroniser): SyncSource {
- if (synchroniser instanceof SettingsSynchroniser) {
- return SyncSource.Settings;
- }
- if (synchroniser instanceof KeybindingsSynchroniser) {
- return SyncSource.Keybindings;
- }
- if (synchroniser instanceof ExtensionsSynchroniser) {
- return SyncSource.Extensions;
- }
- return SyncSource.GlobalState;
- }
-
private getSynchroniser(source: SyncSource): IUserDataSynchroniser {
switch (source) {
case SyncSource.Settings: return this.settingsSynchroniser;
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index f4711ab88a4..3637da92959 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -170,10 +170,15 @@ declare module 'vscode' {
/**
* Fired when the list of tunnels has changed.
+ * @deprecated use onDidChangeTunnels instead
*/
// TODO@alexr
// eslint-disable-next-line vscode-dts-event-naming
export const onDidTunnelsChange: Event;
+ /**
+ * Fired when the list of tunnels has changed.
+ */
+ export const onDidChangeTunnels: Event;
}
export interface ResourceLabelFormatter {
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index 0ce895559ea..96b5fdbfb5e 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -757,9 +757,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTunnelService.getTunnels();
},
+ onDidChangeTunnels: (listener, thisArg?, disposables?) => {
+ checkProposedApiEnabled(extension);
+ return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
+
+ },
onDidTunnelsChange: (listener, thisArg?, disposables?) => {
checkProposedApiEnabled(extension);
- return extHostTunnelService.onDidTunnelsChange(listener, thisArg, disposables);
+ return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
},
registerTimelineProvider: (scheme: string, provider: vscode.TimelineProvider) => {
diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts
index b81fc443758..98436355eb8 100644
--- a/src/vs/workbench/api/common/extHostTunnelService.ts
+++ b/src/vs/workbench/api/common/extHostTunnelService.ts
@@ -33,7 +33,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
readonly _serviceBrand: undefined;
openTunnel(forward: TunnelOptions): Promise;
getTunnels(): Promise;
- onDidTunnelsChange: vscode.Event;
+ onDidChangeTunnels: vscode.Event;
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise;
}
@@ -41,7 +41,7 @@ export const IExtHostTunnelService = createDecorator('IEx
export class ExtHostTunnelService implements IExtHostTunnelService {
_serviceBrand: undefined;
- onDidTunnelsChange: vscode.Event = (new Emitter()).event;
+ onDidChangeTunnels: vscode.Event = (new Emitter()).event;
async openTunnel(forward: TunnelOptions): Promise {
return undefined;
diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts
index 8930d91e856..4138fd2192c 100644
--- a/src/vs/workbench/api/node/extHostTunnelService.ts
+++ b/src/vs/workbench/api/node/extHostTunnelService.ts
@@ -39,8 +39,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable | undefined) | undefined;
private _showCandidatePort: (host: string, port: number, detail: string) => Thenable = () => { return Promise.resolve(true); };
private _extensionTunnels: Map> = new Map();
- private _onDidTunnelsChange: Emitter = new Emitter();
- onDidTunnelsChange: vscode.Event = this._onDidTunnelsChange.event;
+ private _onDidChangeTunnels: Emitter = new Emitter();
+ onDidChangeTunnels: vscode.Event = this._onDidChangeTunnels.event;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@@ -107,7 +107,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
}
async $onDidTunnelsChange(): Promise {
- this._onDidTunnelsChange.fire();
+ this._onDidChangeTunnels.fire();
}
$forwardPort(tunnelOptions: TunnelOptions): Promise | undefined {
diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts
index d364ec671d2..37984510df1 100644
--- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts
+++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts
@@ -447,17 +447,15 @@ class FileElementTemplate {
}));
if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) {
- // rename: NEW NAME (old name)
- this._label.setFile(element.edit.newUri, {
- matches: createMatches(score),
- fileKind: FileKind.FILE,
- fileDecorations: { colors: true, badges: false },
+ // rename: oldName → newName
+ this._label.setResource({
+ resource: element.edit.uri,
+ name: localize('rename.label', "{0} → {1}", this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true })),
+ }, {
+ fileDecorations: { colors: true, badges: false }
});
- this._details.innerText = localize(
- 'detail.rename', "(renaming from {0})",
- this._labelService.getUriLabel(element.edit.uri, { relative: true })
- );
+ this._details.innerText = localize('detail.rename', "(renaming)");
} else {
// create, delete, edit: NAME
diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts
index c45488e0464..323c3be37c8 100644
--- a/src/vs/workbench/contrib/debug/browser/debugHover.ts
+++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts
@@ -273,11 +273,13 @@ export class DebugHoverWidget implements IContentWidget {
return;
}
+ if (dom.isAncestor(document.activeElement, this.domNode)) {
+ this.editor.focus();
+ }
this._isVisible = false;
this.editor.deltaDecorations(this.highlightDecorations, []);
this.highlightDecorations = [];
this.editor.layoutContentWidget(this);
- this.editor.focus();
}
getPosition(): IContentWidgetPosition | null {
diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
index 4ac7696f9b4..a572425da7c 100644
--- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
+++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
-import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService, IUserDataAutoSyncService, USER_DATA_SYNC_SCHEME, toRemoteContentResource, getSyncSourceFromRemoteContentResource, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService, IUserDataAutoSyncService, USER_DATA_SYNC_SCHEME, toRemoteContentResource, getSyncSourceFromRemoteContentResource, UserDataSyncErrorCode, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync';
import { localize } from 'vs/nls';
import { Disposable, MutableDisposable, toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -67,10 +67,6 @@ type FirstTimeSyncClassification = {
action: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
-type SyncErrorClassification = {
- source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
-};
-
export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution {
private static readonly ENABLEMENT_SETTING = 'sync.enable';
@@ -278,15 +274,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private onAutoSyncError(code: UserDataSyncErrorCode, source?: SyncSource): void {
switch (code) {
case UserDataSyncErrorCode.TooLarge:
- this.telemetryService.publicLog2<{ source: string }, SyncErrorClassification>('sync/errorTooLarge', { source: source! });
if (source === SyncSource.Keybindings || source === SyncSource.Settings) {
const sourceArea = getSyncAreaLabel(source);
- this.disableSync();
this.notificationService.notify({
severity: Severity.Error,
- message: localize('too large', "Turned off sync because size of the {0} file to sync is larger than {1}. Please open the file and reduce the size and turn on sync", sourceArea, '1MB'),
+ message: localize('too large', "Disabled synchronizing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea, sourceArea, '100kb'),
actions: {
- primary: [new Action('open sync log', localize('open file', "Show {0} file", sourceArea), undefined, true,
+ primary: [new Action('open sync file', localize('open file', "Show {0} file", sourceArea), undefined, true,
() => source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))]
}
});
@@ -472,8 +466,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
- private disableSync(): Promise {
- return this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, undefined, ConfigurationTarget.USER);
+ private disableSync(source?: SyncSource): Promise {
+ let key: string = UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING;
+ switch (source) {
+ case SyncSource.Settings: key = 'sync.enableSettings'; break;
+ case SyncSource.Keybindings: key = 'sync.enableKeybindings'; break;
+ case SyncSource.Extensions: key = 'sync.enableExtensions'; break;
+ case SyncSource.GlobalState: key = 'sync.enableUIState'; break;
+ }
+ return this.configurationService.updateValue(key, false, ConfigurationTarget.USER);
}
private async signIn(): Promise {
@@ -787,11 +788,15 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
try {
await this.userDataSyncService.accept(conflictsSource!, model.getValue());
} catch (e) {
- this.userDataSyncService.restart().then(() => {
- if (conflictsSource === this.userDataSyncService.conflictsSource) {
- this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again."));
- }
- });
+ if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.NewLocal) {
+ this.userDataSyncService.restart().then(() => {
+ if (conflictsSource === this.userDataSyncService.conflictsSource) {
+ this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again."));
+ }
+ });
+ } else {
+ this.notificationService.error(e);
+ }
}
}
}
diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts
index 2d103e7bf89..199f78af912 100644
--- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts
+++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts
@@ -402,7 +402,6 @@ export class BulkEditService implements IBulkEditService {
}
// try to find code editor
- // todo@joh, prefer editor that gets edited
if (!codeEditor) {
let candidate = this._editorService.activeTextEditorWidget;
if (isCodeEditor(candidate)) {
diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts
index 748960c515e..4afc0cdea9e 100644
--- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts
+++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts
@@ -13,9 +13,7 @@ import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
import 'vs/workbench/services/extensions/worker/extHost.services';
//#region --- Define, capture, and override some globals
-//todo@joh do not allow extensions to call postMessage and other globals...
-// declare WorkerSelf#postMessage
declare function postMessage(data: any, transferables?: Transferable[]): void;
declare namespace self {
diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts
index f796d9d28b1..3b3d71f9eb0 100644
--- a/src/vs/workbench/services/timer/electron-browser/timerService.ts
+++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts
@@ -227,8 +227,6 @@ export interface IStartupMetrics {
* * Reading of package.json-files is avoided by caching them all in a single file (after the read,
* until another extension is installed)
* * Happens in parallel to other things, depends on async timing
- *
- * todo@joh/ramya this measures an artifical dealy we have added, see https://github.com/Microsoft/vscode/blob/2f07ddae8bf56e969e3f4ba1447258ebc999672f/src/vs/workbench/services/extensions/electron-browser/extensionService.ts#L311-L326
*/
readonly ellapsedExtensions: number;
@@ -264,8 +262,6 @@ export interface IStartupMetrics {
* * Measured with the `willRestoreEditors` and `didRestoreEditors` performance marks.
* * This should be looked at per editor and per editor type.
* * Happens in parallel to other things, depends on async timing
- *
- * todo@joh/ramya We should probably measures each editor individually?
*/
readonly ellapsedEditorRestore: number;
@@ -274,8 +270,6 @@ export interface IStartupMetrics {
*
* * Happens in the renderer-process
* * Measured with the `willStartWorkbench` and `didStartWorkbench` performance marks.
- *
- * todo@joh/ramya Not sure if this is useful because this includes too much
*/
readonly ellapsedWorkbench: number;
diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts
index 09f62bc9082..b1af7c97888 100644
--- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts
+++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts
@@ -3,12 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { SyncStatus, SyncSource, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncStatus, SyncSource, IUserDataSyncService, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { CancellationToken } from 'vs/base/common/cancellation';
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
@@ -30,7 +31,16 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
@ISharedProcessService sharedProcessService: ISharedProcessService
) {
super();
- this.channel = sharedProcessService.getChannel('userDataSync');
+ const userDataSyncChannel = sharedProcessService.getChannel('userDataSync');
+ this.channel = {
+ call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise {
+ return userDataSyncChannel.call(command, arg, cancellationToken)
+ .then(null, error => { throw UserDataSyncError.toUserDataSyncError(error); });
+ },
+ listen(event: string, arg?: any): Event {
+ return userDataSyncChannel.listen(event, arg);
+ }
+ };
this.channel.call('_getInitialStatus').then(status => {
this.updateStatus(status);
this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status)));
diff --git a/test/browser.js b/test/browser.js
deleted file mode 100644
index 6d90695a690..00000000000
--- a/test/browser.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-var express = require('express');
-var glob = require('glob');
-var path = require('path');
-var fs = require('fs');
-
-var port = 8887;
-var root = path.dirname(__dirname);
-
-function template(str, env) {
- return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) {
- return env[part];
- });
-}
-
-var app = express();
-
-app.use('/out', express.static(path.join(root, 'out')));
-app.use('/test', express.static(path.join(root, 'test')));
-app.use('/node_modules', express.static(path.join(root, 'node_modules')));
-
-app.get('/', function (req, res) {
- glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', {
- cwd: path.join(root, 'out'),
- // ignore: ['**/test/{node,electron*}/**/*.js']
- }, function (err, files) {
- if (err) { return res.sendStatus(500); }
-
- var modules = files
- .map(function (file) { return file.replace(/\.js$/, ''); });
-
- fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) {
- if (err) { return res.sendStatus(500); }
-
- res.send(template(templateString, {
- modules: JSON.stringify(modules)
- }));
- });
- });
-});
-
-app.listen(port, function () {
- console.log('http://localhost:8887/');
-});
\ No newline at end of file
diff --git a/test/integration/browser/.gitignore b/test/integration/browser/.gitignore
new file mode 100644
index 00000000000..e7d563c46ad
--- /dev/null
+++ b/test/integration/browser/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+npm-debug.log
+Thumbs.db
+node_modules/
+out/
diff --git a/test/integration/browser/README.md b/test/integration/browser/README.md
new file mode 100644
index 00000000000..529130a7f7f
--- /dev/null
+++ b/test/integration/browser/README.md
@@ -0,0 +1,13 @@
+# VS Code Integration test
+
+### Run
+
+```bash
+
+# Dev (Electron)
+scripts/test-integration.sh
+
+# Dev (Web)
+node test/integration/browser/out/index.js
+
+```
diff --git a/test/integration/browser/package.json b/test/integration/browser/package.json
new file mode 100644
index 00000000000..ed4f447f681
--- /dev/null
+++ b/test/integration/browser/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "code-oss-dev-integration-test",
+ "version": "0.1.0",
+ "main": "./index.js",
+ "scripts": {
+ "postinstall": "npm run compile",
+ "compile": "yarn tsc"
+ },
+ "devDependencies": {
+ "@types/mkdirp": "0.5.1",
+ "@types/node": "^12.11.7",
+ "@types/rimraf": "2.0.2",
+ "rimraf": "^2.6.1",
+ "tmp": "0.0.33",
+ "typescript": "3.7.5"
+ }
+}
diff --git a/test/integration/browser/index.js b/test/integration/browser/src/index.ts
similarity index 62%
rename from test/integration/browser/index.js
rename to test/integration/browser/src/index.ts
index 17508685fcb..11b1e49e73d 100644
--- a/test/integration/browser/index.js
+++ b/test/integration/browser/src/index.ts
@@ -3,23 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-//@ts-check
+import * as path from 'path';
+import * as cp from 'child_process';
+import * as playwright from 'playwright';
+import * as url from 'url';
+import * as tmp from 'tmp';
+import * as rimraf from 'rimraf';
-const path = require('path');
-const cp = require('child_process');
-const playwright = require('playwright');
-const url = require('url');
-
-// opts
const optimist = require('optimist')
.describe('debug', 'do not run browsers headless').boolean('debug')
.describe('browser', 'browser in which integration tests should run').string('browser').default('browser', 'chromium')
.describe('help', 'show the help').alias('help', 'h');
-// logic
-const argv = optimist.argv;
-
-let serverProcess;
+let serverProcess: cp.ChildProcess | undefined = undefined;
function teardownServer() {
if (serverProcess) {
@@ -28,18 +24,17 @@ function teardownServer() {
}
}
-/**
- * @param {string} browserType
- * @param {string} endpoint
- */
-async function runTestsInBrowser(browserType, endpoint) {
- const browser = await playwright[browserType].launch({ headless: !Boolean(argv.debug) });
+async function runTestsInBrowser(browserType: string, endpoint: string): Promise {
+ const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug) });
const page = (await browser.defaultContext().pages())[0];
- const integrationTestsPath = path.join(__dirname, '..', '..', '..', 'extensions', 'vscode-api-tests');
- const testWorkspaceUri = url.format({ pathname: path.join(integrationTestsPath, 'testWorkspace'), protocol: 'vscode-remote:', slashes: true, host: 'localhost:9888' });
- const testExtensionUri = url.format({ pathname: path.join(integrationTestsPath), protocol: 'vscode-remote:', slashes: true, host: 'localhost:9888' });
- const testFilesUri = url.format({ pathname: path.join(integrationTestsPath, 'out', 'singlefolder-tests'), protocol: 'vscode-remote:', slashes: true, host: 'localhost:9888' });
+ const host = url.parse(endpoint).host;
+ const protocol = 'vscode-remote';
+
+ const integrationTestsPath = path.join(__dirname, '..', '..', '..', '..', 'extensions', 'vscode-api-tests');
+ const testWorkspaceUri = url.format({ pathname: path.join(integrationTestsPath, 'testWorkspace'), protocol, host, slashes: true });
+ const testExtensionUri = url.format({ pathname: path.join(integrationTestsPath), protocol, host, slashes: true });
+ const testFilesUri = url.format({ pathname: path.join(integrationTestsPath, 'out', 'singlefolder-tests'), protocol, host, slashes: true });
const folderParam = testWorkspaceUri;
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"]]`;
@@ -51,7 +46,7 @@ async function runTestsInBrowser(browserType, endpoint) {
// emitter.emit(type, data1, data2)
// });
- page.on('console', async msg => {
+ page.on('console', async (msg: playwright.ConsoleMessage) => {
const msgText = msg.text();
console[msg.type()](msgText, await Promise.all(msg.args().map(async arg => await arg.jsonValue())));
@@ -63,12 +58,15 @@ async function runTestsInBrowser(browserType, endpoint) {
});
}
-async function launch() {
- // workspacePath = _workspacePath;
- // const agentFolder = userDataDir;
- // await promisify(mkdir)(agentFolder);
+async function launchServer(): Promise {
+ const tmpDir = tmp.dirSync({ prefix: 't' });
+ const testDataPath = tmpDir.name;
+ process.once('exit', () => rimraf.sync(testDataPath));
+
+ const userDataDir = path.join(testDataPath, 'd');
+
const env = {
- // VSCODE_AGENT_FOLDER: agentFolder,
+ VSCODE_AGENT_FOLDER: userDataDir,
...process.env
};
@@ -76,7 +74,9 @@ async function launch() {
if (process.env.VSCODE_REMOTE_SERVER_PATH) {
serverLocation = path.join(process.env.VSCODE_REMOTE_SERVER_PATH, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`);
} else {
- serverLocation = path.join(__dirname, '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`);
+ serverLocation = path.join(__dirname, '..', '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`);
+
+ process.env.VSCODE_DEV = '1';
}
serverProcess = cp.spawn(
@@ -85,15 +85,15 @@ async function launch() {
{ env }
);
- serverProcess.stderr.on('data', e => console.log('Server stderr: ' + e));
- serverProcess.stdout.on('data', e => console.log('Server stdout: ' + e));
+ serverProcess?.stderr?.on('data', e => console.log(`Server stderr: ${e}`));
+ serverProcess?.stdout?.on('data', e => console.log(`Server stdout: ${e}`));
process.on('exit', teardownServer);
process.on('SIGINT', teardownServer);
process.on('SIGTERM', teardownServer);
return new Promise(r => {
- serverProcess.stdout.on('data', d => {
+ serverProcess?.stdout?.on('data', d => {
const matches = d.toString('ascii').match(/Web UI available at (.+)/);
if (matches !== null) {
r(matches[1]);
@@ -102,6 +102,6 @@ async function launch() {
});
}
-launch().then(async endpoint => {
- return runTestsInBrowser(argv.browser, endpoint);
+launchServer().then(async endpoint => {
+ return runTestsInBrowser(optimist.argv.browser, endpoint);
}, console.error);
diff --git a/test/integration/browser/tsconfig.json b/test/integration/browser/tsconfig.json
new file mode 100644
index 00000000000..6f0b40e93e5
--- /dev/null
+++ b/test/integration/browser/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "noImplicitAny": false,
+ "removeComments": false,
+ "preserveConstEnums": true,
+ "target": "es2017",
+ "strictNullChecks": true,
+ "noUnusedParameters": false,
+ "noUnusedLocals": true,
+ "outDir": "out",
+ "sourceMap": true,
+ "lib": [
+ "es2016",
+ "dom"
+ ]
+ },
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/test/integration/browser/yarn.lock b/test/integration/browser/yarn.lock
new file mode 100644
index 00000000000..059c3c8f3cf
--- /dev/null
+++ b/test/integration/browser/yarn.lock
@@ -0,0 +1,143 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/events@*":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
+ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
+"@types/glob@*":
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
+ integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+ dependencies:
+ "@types/events" "*"
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
+"@types/minimatch@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/mkdirp@0.5.1":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.1.tgz#ea887cd024f691c1ca67cce20b7606b053e43b0f"
+ integrity sha512-XA4vNO6GCBz8Smq0hqSRo4yRWMqr4FPQrWjhJt6nKskzly4/p87SfuJMFYGRyYb6jo2WNIQU2FDBsY5r1BibUA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/node@*":
+ version "13.7.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4"
+ integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==
+
+"@types/node@^12.11.7":
+ version "12.12.26"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9"
+ integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA==
+
+"@types/rimraf@2.0.2":
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e"
+ integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==
+ dependencies:
+ "@types/glob" "*"
+ "@types/node" "*"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+glob@^7.1.3:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+rimraf@^2.6.1:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+tmp@0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+typescript@3.7.5:
+ version "3.7.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
+ integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
diff --git a/test/smoke/README.md b/test/smoke/README.md
index 1060250f7fd..ddd05e7eb65 100644
--- a/test/smoke/README.md
+++ b/test/smoke/README.md
@@ -1,6 +1,6 @@
# VS Code Smoke Test
-Make sure you are on **Node v10.x**.
+Make sure you are on **Node v12.x**.
### Run
diff --git a/test/unit/README.md b/test/unit/README.md
index 96d866fe6ad..206f4baccf6 100644
--- a/test/unit/README.md
+++ b/test/unit/README.md
@@ -26,6 +26,10 @@ You can run tests inside a browser instance too:
node test\unit\browser\index.js
+## Run (with node)
+
+ yarn run mocha --run src/vs/editor/test/browser/controller/cursor.test.ts
+
## Debug
To debug tests use `--debug` when running the test script. Also, the set of tests can be reduced with the `--run` and `--runGlob` flags. Both require a file path/pattern. Like so:
diff --git a/test/assert.js b/test/unit/assert.js
similarity index 100%
rename from test/assert.js
rename to test/unit/assert.js
diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js
index 9464baee18e..64790c8245a 100644
--- a/test/unit/browser/index.js
+++ b/test/unit/browser/index.js
@@ -7,6 +7,7 @@
const path = require('path');
const glob = require('glob');
+const fs = require('fs');
const events = require('events');
const mocha = require('mocha');
const url = require('url');
@@ -17,7 +18,8 @@ const playwright = require('playwright');
const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec';
const optimist = require('optimist')
.describe('grep', 'only run tests matching ').alias('grep', 'g').alias('grep', 'f').string('grep')
- .describe('run', 'only run tests matching ').alias('run', 'glob').string('runGlob')
+ .describe('run', 'only run tests matching ').string('run')
+ .describe('glob', 'only run tests matching ').string('glob')
.describe('build', 'run with build output (out-build)').boolean('build')
.describe('debug', 'do not run browsers headless').boolean('debug')
.describe('browser', 'browsers in which tests should run').string('browser').default('browser', ['chromium'])
@@ -59,35 +61,53 @@ const withReporter = (function () {
const outdir = argv.build ? 'out-build' : 'out';
const out = path.join(__dirname, `../../../${outdir}`);
+function ensureIsArray(a) {
+ return Array.isArray(a) ? a : [a];
+}
+
const testModules = (async function () {
- const defaultGlob = '**/*.test.js';
const excludeGlob = '**/{node,electron-browser,electron-main}/**/*.test.js';
- const pattern = argv.glob || defaultGlob
+ let isDefaultModules = true;
+ let promise;
- return new Promise((resolve, reject) => {
- glob(pattern, { cwd: out }, (err, files) => {
- if (err) {
- reject(err);
- return;
- }
+ if (argv.run) {
+ // use file list (--run)
+ isDefaultModules = false;
+ promise = Promise.resolve(ensureIsArray(argv.run).map(file => {
+ file = file.replace(/^src/, 'out');
+ file = file.replace(/\.ts$/, '.js');
+ return path.relative(out, file);
+ }));
- const modules = [];
- const badFiles = [];
+ } else {
+ // glob patterns (--glob)
+ const defaultGlob = '**/*.test.js';
+ const pattern = argv.glob || defaultGlob
+ isDefaultModules = argv.glob === defaultGlob;
- for (let file of files) {
- if (minimatch(file, excludeGlob)) {
- badFiles.push(file);
+ promise = new Promise((resolve, reject) => {
+ glob(pattern, { cwd: out }, (err, files) => {
+ if (err) {
+ reject(err);
} else {
- modules.push(file.replace(/\.js$/, ''));
+ resolve(files)
}
- }
-
- if (badFiles.length > 0 && pattern !== defaultGlob) {
- console.warn(`DROPPED ${badFiles.length} files because '${pattern}' includes files from invalid layers.${badFiles.map(file => `\n\t-${file}`)}`);
- }
- resolve(modules);
+ });
});
+ }
+
+ return promise.then(files => {
+ const modules = [];
+ for (let file of files) {
+ if (!minimatch(file, excludeGlob)) {
+ modules.push(file.replace(/\.js$/, ''));
+
+ } else if (!isDefaultModules) {
+ console.warn(`DROPPONG ${file} because it cannot be run inside a browser`);
+ }
+ }
+ return modules;
})
})();
diff --git a/test/unit/browser/renderer.html b/test/unit/browser/renderer.html
index 52a512f26b0..2f46b408f41 100644
--- a/test/unit/browser/renderer.html
+++ b/test/unit/browser/renderer.html
@@ -46,7 +46,7 @@
baseUrl: new URL('../../../src', baseUrl).href,
paths: {
'vs': new URL('../../../out/vs', baseUrl).href,
- assert: new URL('../../assert.js', baseUrl).href,
+ assert: new URL('../assert.js', baseUrl).href,
sinon: new URL('../../../node_modules/sinon/pkg/sinon-1.17.7.js', baseUrl).href
}
});
diff --git a/test/coverage.js b/test/unit/coverage.js
similarity index 94%
rename from test/coverage.js
rename to test/unit/coverage.js
index e01452047f5..e93f84c8c4f 100644
--- a/test/coverage.js
+++ b/test/unit/coverage.js
@@ -12,7 +12,7 @@ const iLibSourceMaps = require('istanbul-lib-source-maps');
const iLibReport = require('istanbul-lib-report');
const iReports = require('istanbul-reports');
-const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '..'));
+const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '../../'));
exports.initialize = function (loaderConfig) {
const instrumenter = iLibInstrument.createInstrumenter();
@@ -47,7 +47,7 @@ exports.createReport = function (isSingle) {
transformed.data = newData;
const context = iLibReport.createContext({
- dir: path.join(__dirname, `../.build/coverage${isSingle ? '-single' : ''}`),
+ dir: path.join(REPO_PATH, `.build/coverage${isSingle ? '-single' : ''}`),
coverageMap: transformed
});
const tree = context.getTree('flat');
diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js
index 12c86ad8221..734c0837841 100644
--- a/test/unit/electron/renderer.js
+++ b/test/unit/electron/renderer.js
@@ -11,7 +11,7 @@ const path = require('path');
const glob = require('glob');
const util = require('util');
const bootstrap = require('../../../src/bootstrap');
-const coverage = require('../../coverage');
+const coverage = require('../coverage');
// Disabled custom inspect. See #38847
if (util.inspect && util.inspect['defaultOptions']) {
diff --git a/test/unit/node/all.js b/test/unit/node/all.js
index 6f1fb97a875..a1421c5c72c 100644
--- a/test/unit/node/all.js
+++ b/test/unit/node/all.js
@@ -11,7 +11,7 @@ const path = require('path');
const glob = require('glob');
const jsdom = require('jsdom-no-contextify');
const TEST_GLOB = '**/test/**/*.test.js';
-const coverage = require('../../coverage');
+const coverage = require('../coverage');
const optimist = require('optimist')
.usage('Run the Code tests. All mocha options apply.')
@@ -44,7 +44,7 @@ function main() {
nodeMain: __filename,
baseUrl: path.join(REPO_ROOT, 'src'),
paths: {
- 'vs/css': '../test/css.mock',
+ 'vs/css': '../test/unit/node/css.mock',
'vs': `../${out}/vs`,
'lib': `../${out}/lib`,
'bootstrap-fork': `../${out}/bootstrap-fork`
@@ -166,7 +166,7 @@ function main() {
}
if (process.argv.some(function (a) { return /^--browser/.test(a); })) {
- require('../../browser');
+ require('./browser');
} else {
main();
}
diff --git a/test/unit/node/browser.js b/test/unit/node/browser.js
new file mode 100644
index 00000000000..a564b7672e2
--- /dev/null
+++ b/test/unit/node/browser.js
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+const yaserver = require('yaserver');
+const http = require('http');
+const glob = require('glob');
+const path = require('path');
+const fs = require('fs');
+
+const REPO_ROOT = path.join(__dirname, '../../../');
+const PORT = 8887;
+
+function template(str, env) {
+ return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) {
+ return env[part];
+ });
+}
+
+yaserver.createServer({ rootDir: REPO_ROOT }).then((staticServer) => {
+ const server = http.createServer((req, res) => {
+ if (req.url === '' || req.url === '/') {
+ glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', {
+ cwd: path.join(REPO_ROOT, 'out'),
+ // ignore: ['**/test/{node,electron*}/**/*.js']
+ }, function (err, files) {
+ if (err) { console.log(err); process.exit(0); }
+
+ var modules = files
+ .map(function (file) { return file.replace(/\.js$/, ''); });
+
+ fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) {
+ if (err) { console.log(err); process.exit(0); }
+
+ res.end(template(templateString, {
+ modules: JSON.stringify(modules)
+ }));
+ });
+ });
+ } else {
+ return staticServer.handle(req, res);
+ }
+ });
+
+ server.listen(PORT, () => {
+ console.log(`http://localhost:${PORT}/`);
+ });
+});
diff --git a/test/css.mock.js b/test/unit/node/css.mock.js
similarity index 100%
rename from test/css.mock.js
rename to test/unit/node/css.mock.js
diff --git a/test/index.html b/test/unit/node/index.html
similarity index 92%
rename from test/index.html
rename to test/unit/node/index.html
index 384c987fed1..55f9bb0f708 100644
--- a/test/index.html
+++ b/test/unit/node/index.html
@@ -16,7 +16,7 @@
require.config({
baseUrl: '/out',
paths: {
- assert: '/test/assert.js',
+ assert: '/test/unit/assert.js',
sinon: '/node_modules/sinon/pkg/sinon-1.17.7.js'
}
});
@@ -26,4 +26,4 @@
});