Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Extension/.scripts/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
* ------------------------------------------------------------------------------------------ */

import { downloadAndUnzipVSCode, resolveCliArgsFromVSCodeExecutablePath } from '@vscode/test-electron';
import { createHash } from 'crypto';
import { tmpdir } from 'os';
import { resolve } from 'path';
import { verbose } from '../src/Utility/Text/streams';
import { mkdir, readJson, rimraf, write } from './common';
import { getVSCodeTestIsolate } from './vscodeTestPath';

export const isolated = resolve(tmpdir(), '.vscode-test', createHash('sha256').update(__dirname).digest('hex').substring(0, 6));
export const isolated = getVSCodeTestIsolate(__dirname);
export const extensionsDir = resolve(isolated, 'extensions');
export const userDir = resolve(isolated, 'user-data');
export const settings = resolve(userDir, "User", 'settings.json');
Expand Down
51 changes: 51 additions & 0 deletions Extension/.scripts/vscodeTestPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */

import { createHash } from 'crypto';
import { homedir } from 'os';
import { posix, win32 } from 'path';

function isFullyQualifiedPath(value: string, platform: NodeJS.Platform): boolean {
const path = platform === 'win32' ? win32 : posix;
return path.isAbsolute(value) && (platform !== 'win32' || path.parse(value).root.length > 1);
}

export function getVSCodeTestIsolate(
scriptDirectory: string,
platform: NodeJS.Platform = process.platform,
environment: NodeJS.ProcessEnv = process.env,
homeDirectory: string = homedir()): string {
const path = platform === 'win32' ? win32 : posix;
const override = environment.CPPTOOLS_VSCODE_TEST_ROOT;
let root: string;

if (override) {
if (!isFullyQualifiedPath(override, platform)) {
throw new Error('CPPTOOLS_VSCODE_TEST_ROOT must be a fully qualified absolute path.');
}
root = override;
} else {
switch (platform) {
case 'win32': {
const localAppData = environment.LOCALAPPDATA;
const cacheDirectory = localAppData && isFullyQualifiedPath(localAppData, platform) ? localAppData : path.resolve(homeDirectory, 'AppData', 'Local');
root = path.resolve(cacheDirectory, 'Microsoft', 'vscode-cpptools', 'vscode-test');
break;
}
case 'darwin':
root = path.resolve(homeDirectory, 'Library', 'Caches', 'vscode-cpptools', 'vscode-test');
break;
default: {
const xdgCacheHome = environment.XDG_CACHE_HOME;
const cacheDirectory = xdgCacheHome && isFullyQualifiedPath(xdgCacheHome, platform) ? xdgCacheHome : path.resolve(homeDirectory, '.cache');
root = path.resolve(cacheDirectory, 'vscode-cpptools', 'vscode-test');
break;
}
}
}

const worktreeHash = createHash('sha256').update(scriptDirectory).digest('hex').substring(0, 6);
return path.resolve(root, worktreeHash);
}
28 changes: 22 additions & 6 deletions Extension/readme.developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,33 @@ The scripts for this repository now support running VS Code and the extension in
completely isolated environment (separate install of VS Code, private extensions and
user folders, etc).

The scripts that install VS Code place it in a `$ENV:TMP/.vscode-test/<UID>` folder where
`<UID>` is a has calculated from the extension folder (this permits multiple checkouts of
the source repository and each gets it's own isolated environment).
The scripts that install VS Code retain it in a per-user cache directory:

* Windows: `%LOCALAPPDATA%\Microsoft\vscode-cpptools\vscode-test\<UID>` (or
`%USERPROFILE%\AppData\Local\Microsoft\vscode-cpptools\vscode-test\<UID>` if `%LOCALAPPDATA%` is unavailable)
* macOS: `~/Library/Caches/vscode-cpptools/vscode-test/<UID>`
* Linux: `${XDG_CACHE_HOME:-~/.cache}/vscode-cpptools/vscode-test/<UID>`

The environment-based cache locations are used only when they are fully qualified. On Windows,
they must include a drive or UNC share. Unsupported values fall back to the per-user locations shown above.

`<UID>` is a six-character hash calculated from the checkout's `.scripts` directory path. This permits multiple
checkouts of the source repository, with each checkout retaining its own isolated `cache`,
`extensions`, and `user-data` folders across runs. Set `CPPTOOLS_VSCODE_TEST_ROOT` to a fully qualified
absolute directory to override the platform-specific `vscode-test` root. The same Windows drive or UNC
share requirement applies. The checkout-specific `<UID>` is still appended to the override.

The [`test scripts`](#yarn-test) will automatically install and use this isolated environment.

You can invoke VS Code from the command line using the [`yarn code`](#yarn-code) script.

If you want to remove the isolate environment use the `yarn code reset` or `yarn test reset` scripts
to delete the folders and remove all of the configuration files. Next time you use the `yarn test` or
`yarn code` commands, it will reinstall a fresh isolated environment.
If you want to remove the isolated environment use the `yarn code reset` or `yarn test reset` scripts
to delete only the current checkout's hashed folder and remove all of its configuration files. Next
time you use the `yarn test` or `yarn code` commands, it will reinstall a fresh isolated environment.

Isolates created by earlier versions under the system temporary directory are not migrated or
removed automatically. After ensuring that no test runs are using them, you can remove the old
`.vscode-test` folder from the system temporary directory once to reclaim that space.

The Isolated environment has the theme automatically set to blue so that it is visually distinct from
your normal VS Code environment.
Expand Down
102 changes: 102 additions & 0 deletions Extension/test/unit/vscodeTestPath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */

import * as assert from 'assert';
import { createHash } from 'crypto';
import { describe, it } from 'mocha';
import { posix, win32 } from 'path';
import { getVSCodeTestIsolate } from '../../.scripts/vscodeTestPath';

const posixScriptDirectory = '/worktrees/agent/Extension/.scripts';
const windowsScriptDirectory = 'C:\\worktrees\\agent\\Extension\\.scripts';

function getWorktreeHash(scriptDirectory: string): string {
return createHash('sha256').update(scriptDirectory).digest('hex').substring(0, 6);
}

describe('VS Code test isolate path', () => {
it('uses XDG_CACHE_HOME on Linux', () => {
assert.strictEqual(
getVSCodeTestIsolate(posixScriptDirectory, 'linux', { XDG_CACHE_HOME: '/cache' }, '/home/developer'),
posix.resolve('/cache', 'vscode-cpptools', 'vscode-test', getWorktreeHash(posixScriptDirectory)));
});

it('falls back to the user cache directory on Linux', () => {
const expected = posix.resolve('/home/developer', '.cache', 'vscode-cpptools', 'vscode-test', getWorktreeHash(posixScriptDirectory));

assert.strictEqual(
getVSCodeTestIsolate(posixScriptDirectory, 'linux', {}, '/home/developer'),
expected);
assert.strictEqual(
getVSCodeTestIsolate(posixScriptDirectory, 'linux', { XDG_CACHE_HOME: 'relative-cache' }, '/home/developer'),
expected);
});

it('uses the user cache directory on macOS', () => {
assert.strictEqual(
getVSCodeTestIsolate(posixScriptDirectory, 'darwin', {}, '/Users/developer'),
posix.resolve('/Users/developer', 'Library', 'Caches', 'vscode-cpptools', 'vscode-test', getWorktreeHash(posixScriptDirectory)));
});

it('uses LOCALAPPDATA on Windows', () => {
assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { LOCALAPPDATA: 'D:\\LocalAppData' }, 'C:\\Users\\developer'),
win32.resolve('D:\\LocalAppData', 'Microsoft', 'vscode-cpptools', 'vscode-test', getWorktreeHash(windowsScriptDirectory)));
});

it('falls back to the user profile on Windows', () => {
const expected = win32.resolve('C:\\Users\\developer', 'AppData', 'Local', 'Microsoft', 'vscode-cpptools', 'vscode-test', getWorktreeHash(windowsScriptDirectory));

assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', {}, 'C:\\Users\\developer'),
expected);
assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { LOCALAPPDATA: 'relative-cache' }, 'C:\\Users\\developer'),
expected);
assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { LOCALAPPDATA: '\\relative-cache' }, 'C:\\Users\\developer'),
expected);
});

it('honors CPPTOOLS_VSCODE_TEST_ROOT without sharing worktree isolates', () => {
const environment = { CPPTOOLS_VSCODE_TEST_ROOT: '/test-root', XDG_CACHE_HOME: '/cache' };
const first = getVSCodeTestIsolate('/worktrees/first/Extension/.scripts', 'linux', environment, '/home/developer');
const firstAgain = getVSCodeTestIsolate('/worktrees/first/Extension/.scripts', 'linux', environment, '/home/developer');
const second = getVSCodeTestIsolate('/worktrees/second/Extension/.scripts', 'linux', environment, '/home/developer');

assert.strictEqual(first, firstAgain);
assert.match(posix.basename(first), /^[0-9a-f]{6}$/);
assert.notStrictEqual(first, second);
assert.strictEqual(posix.dirname(first), '/test-root');
assert.strictEqual(posix.dirname(second), '/test-root');
});

it('accepts fully qualified Windows CPPTOOLS_VSCODE_TEST_ROOT values', () => {
const driveRoot = 'D:\\test-root';
const uncRoot = '\\\\server\\share\\test-root';

assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { CPPTOOLS_VSCODE_TEST_ROOT: driveRoot }, 'C:\\Users\\developer'),
win32.resolve(driveRoot, getWorktreeHash(windowsScriptDirectory)));
assert.strictEqual(
getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { CPPTOOLS_VSCODE_TEST_ROOT: uncRoot }, 'C:\\Users\\developer'),
win32.resolve(uncRoot, getWorktreeHash(windowsScriptDirectory)));
});

it('rejects non-fully-qualified CPPTOOLS_VSCODE_TEST_ROOT values', () => {
assert.throws(
() => getVSCodeTestIsolate(posixScriptDirectory, 'linux', { CPPTOOLS_VSCODE_TEST_ROOT: 'test-root' }, '/home/developer'),
/CPPTOOLS_VSCODE_TEST_ROOT must be a fully qualified absolute path/);
assert.throws(
() => getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { CPPTOOLS_VSCODE_TEST_ROOT: 'C:' }, 'C:\\Users\\developer'),
/CPPTOOLS_VSCODE_TEST_ROOT must be a fully qualified absolute path/);
assert.throws(
() => getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { CPPTOOLS_VSCODE_TEST_ROOT: '\\test-root' }, 'C:\\Users\\developer'),
/CPPTOOLS_VSCODE_TEST_ROOT must be a fully qualified absolute path/);
assert.throws(
() => getVSCodeTestIsolate(windowsScriptDirectory, 'win32', { CPPTOOLS_VSCODE_TEST_ROOT: '/test-root' }, 'C:\\Users\\developer'),
/CPPTOOLS_VSCODE_TEST_ROOT must be a fully qualified absolute path/);
});
});
Loading