Skip to content

Enable integration test #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 21, 2021
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
13 changes: 7 additions & 6 deletions extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ suite('vscode API - debug', function () {
disposeAll(toDispose);
});

test.skip('start debugging', async function () {
test('start debugging', async function () {
let stoppedEvents = 0;
let variablesReceived: () => void;
let initializedReceived: () => void;
Expand Down Expand Up @@ -105,13 +105,14 @@ suite('vscode API - debug', function () {
await fourthVariablesRetrieved;
assert.strictEqual(stoppedEvents, 4);

const fifthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
await commands.executeCommand('workbench.action.debug.stepOut');
await fifthVariablesRetrieved;
assert.strictEqual(stoppedEvents, 5);
// const fifthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve);
// await commands.executeCommand('workbench.action.debug.stepOut');
// await fifthVariablesRetrieved;
// assert.strictEqual(stoppedEvents, 5);

let sessionTerminated: () => void;
toDispose.push(debug.onDidTerminateDebugSession(() => {
toDispose.push(debug.onDidTerminateDebugSession(async () => {
await new Promise(c => setTimeout(c, 500));
sessionTerminated();
}));
const sessionTerminatedPromise = new Promise<void>(resolve => sessionTerminated = resolve);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
*--------------------------------------------------------------------------------------------*/

import { deepEqual, deepStrictEqual, doesNotThrow, equal, strictEqual, throws } from 'assert';
import { ConfigurationTarget, Disposable, env, EnvironmentVariableMutator, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalOptions, TerminalState, UIKind, window, workspace } from 'vscode';
import { ConfigurationTarget, Disposable, /* env, */ EnvironmentVariableMutator, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalOptions, TerminalState, /* UIKind, */ window, workspace } from 'vscode';
import { assertNoRpc } from '../utils';

// Disable terminal tests:
// - Web https://github.com/microsoft/vscode/issues/92826
(env.uiKind === UIKind.Web ? suite.skip : suite)('vscode API - terminal', () => {
suite('vscode API - terminal', () => {
let extensionContext: ExtensionContext;

suiteSetup(async () => {
Expand Down
31 changes: 31 additions & 0 deletions resources/server/test/test-web-integration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -e

if [[ "$OSTYPE" == "darwin"* ]]; then
realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; }
ROOT=$(dirname $(dirname $(dirname $(dirname $(realpath "$0")))))
else
ROOT=$(dirname $(dirname $(dirname $(dirname $(readlink -f $0)))))
fi

cd $ROOT

# Tests in the extension host
TEST_SCRIPT="$ROOT/test/integration/browser/out/index.js"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests "$@"

# This seems it's electron only?
# /usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test/unit "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$ROOT/extensions/emmet/test-workspace --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test "$@"

/usr/bin/env node "$TEST_SCRIPT" --workspacePath=$(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/ipynb --extensionTestsPath=$ROOT/extensions/ipynb/out/test "$@"
3 changes: 3 additions & 0 deletions resources/server/web.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ else
ROOT=$(dirname $(dirname $(dirname $(readlink -f $0))))
fi

export NODE_ENV=development
export VSCODE_DEV=1

SERVER_SCRIPT="$ROOT/out/server.js"
exec /usr/bin/env node "$SERVER_SCRIPT" "$@"
89 changes: 86 additions & 3 deletions src/vs/server/browser/workbench/workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
*--------------------------------------------------------------------------------------------*/

import { isStandalone } from 'vs/base/browser/browser';
import { streamToBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Event } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';
import { isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { request } from 'vs/base/parts/request/browser/request';
import { localize } from 'vs/nls';
import { parseLogLevel } from 'vs/platform/log/common/log';
import { defaultWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory';
import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
import { create, ICredentialsProvider, IHomeIndicator, IProductQualityChangeHandler, IWindowIndicator, IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/workbench.web.api';
import { create, Disposable, ICredentialsProvider, IHomeIndicator, IProductQualityChangeHandler, IURLCallbackProvider, IWindowIndicator, IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/workbench.web.api';

function doCreateUri(path: string, queryValues: Map<string, string>): URI {
let query: string | undefined = undefined;
Expand Down Expand Up @@ -159,6 +161,86 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
}
}

class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider {

static readonly FETCH_INTERVAL = 500; // fetch every 500ms
static readonly FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min

static readonly QUERY_KEYS = {
REQUEST_ID: 'vscode-requestId',
SCHEME: 'vscode-scheme',
AUTHORITY: 'vscode-authority',
PATH: 'vscode-path',
QUERY: 'vscode-query',
FRAGMENT: 'vscode-fragment'
};

private readonly _onCallback = this._register(new Emitter<URI>());
readonly onCallback = this._onCallback.event;

create(options?: Partial<UriComponents>): URI {
const queryValues: Map<string, string> = new Map();

const requestId = generateUuid();
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);

const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined };

if (scheme) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.SCHEME, scheme);
}

if (authority) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority);
}

if (path) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.PATH, path);
}

if (query) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.QUERY, query);
}

if (fragment) {
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment);
}

// Start to poll on the callback being fired
this.periodicFetchCallback(requestId, Date.now());

return doCreateUri('/callback', queryValues);
}

private async periodicFetchCallback(requestId: string, startTime: number): Promise<void> {

// Ask server for callback results
const queryValues: Map<string, string> = new Map();
queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId);

const result = await request({
url: doCreateUri('/fetch-callback', queryValues).toString(true)
}, CancellationToken.None);

// Check for callback results
const content = await streamToBuffer(result.stream);
if (content.byteLength > 0) {
try {
this._onCallback.fire(URI.revive(JSON.parse(content.toString())));
} catch (error) {
console.error(error);
}

return; // done
}

// Continue fetching unless we hit the timeout
if (Date.now() - startTime < PollingURLCallbackProvider.FETCH_TIMEOUT) {
setTimeout(() => this.periodicFetchCallback(requestId, startTime), PollingURLCallbackProvider.FETCH_INTERVAL);
}
}
}

class WorkspaceProvider implements IWorkspaceProvider {

static QUERY_PARAM_EMPTY_WINDOW = 'ew';
Expand Down Expand Up @@ -411,6 +493,7 @@ class WindowIndicator implements IWindowIndicator {
windowIndicator,
productQualityChangeHandler,
workspaceProvider,
urlCallbackProvider: new PollingURLCallbackProvider(),
credentialsProvider: new LocalStorageCredentialsProvider()
});
})();
92 changes: 87 additions & 5 deletions src/vs/server/node/server.main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { isPromiseCanceledError, onUnexpectedError, setUnexpectedErrorHandler }
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { join } from 'vs/base/common/path';
import { dirname, join } from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { ReadableStreamEventPayload } from 'vs/base/common/stream';
Expand Down Expand Up @@ -73,7 +73,7 @@ import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/
export type IRawURITransformerFactory = (remoteAuthority: string) => IRawURITransformer;
export const IRawURITransformerFactory = createDecorator<IRawURITransformerFactory>('rawURITransformerFactory');

const APP_ROOT = path.join(__dirname, '..', '..', '..', '..');
const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath);
const uriTransformerPath = path.join(APP_ROOT, 'out/serverUriTransformer');
const rawURITransformerFactory: IRawURITransformerFactory = <any>require.__$__nodeRequire(uriTransformerPath);

Expand Down Expand Up @@ -174,6 +174,28 @@ function serveError(req: http.IncomingMessage, res: http.ServerResponse, errorCo
res.end(errorMessage);
}

function getFirstQueryValue(parsedUrl: url.UrlWithParsedQuery, key: string): string | undefined {
const result = parsedUrl.query[key];
return Array.isArray(result) ? result[0] : result;
}

function getFirstQueryValues(parsedUrl: url.UrlWithParsedQuery, ignoreKeys?: string[]): Map<string, string> {
const queryValues: Map<string, string> = new Map();

for (const key in parsedUrl.query) {
if (ignoreKeys && ignoreKeys.indexOf(key) >= 0) {
continue;
}

const value = getFirstQueryValue(parsedUrl, key);
if (typeof value === 'string') {
queryValues.set(key, value);
}
}

return queryValues;
}

async function serveFile(logService: ILogService, req: http.IncomingMessage, res: http.ServerResponse, filePath: string, responseHeaders: http.OutgoingHttpHeaders = {}) {
try {

Expand All @@ -198,7 +220,7 @@ async function serveFile(logService: ILogService, req: http.IncomingMessage, res
// Data
fs.createReadStream(filePath).pipe(res);
} catch (error) {
logService.error(error.toString());
logService.error(error);
res.writeHead(404, { 'Content-Type': 'text/plain' });
return res.end('Not found');
}
Expand Down Expand Up @@ -226,6 +248,57 @@ async function handleRoot(req: http.IncomingMessage, resp: http.ServerResponse,
return resp.end(entryPointContent);
}

const mapCallbackUriToRequestId = new Map<string, string>();
async function handleCallback(logService: ILogService, req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithParsedQuery) {
const wellKnownKeys = ['vscode-requestId', 'vscode-scheme', 'vscode-authority', 'vscode-path', 'vscode-query', 'vscode-fragment'];
const [requestId, vscodeScheme, vscodeAuthority, vscodePath, vscodeQuery, vscodeFragment] = wellKnownKeys.map(key => {
const value = getFirstQueryValue(parsedUrl, key);
if (value) {
return decodeURIComponent(value);
}

return value;
});

if (!requestId) {
res.writeHead(400, { 'Content-Type': 'text/plain' });
return res.end(`Bad request.`);
}

// merge over additional query values that we got
let query = vscodeQuery;
let index = 0;
getFirstQueryValues(parsedUrl, wellKnownKeys).forEach((value, key) => {
if (!query) {
query = '';
}

const prefix = (index++ === 0) ? '' : '&';
query += `${prefix}${key}=${value}`;
});

// add to map of known callbacks
mapCallbackUriToRequestId.set(requestId, JSON.stringify({ scheme: vscodeScheme || product.urlProtocol, authority: vscodeAuthority, path: vscodePath, query, fragment: vscodeFragment }));
return serveFile(logService, req, res, FileAccess.asFileUri('vs/code/browser/workbench/callback.html', require).fsPath, { 'Content-Type': 'text/html' });
}

async function handleFetchCallback(req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithParsedQuery) {
const requestId = getFirstQueryValue(parsedUrl, 'vscode-requestId');
if (!requestId) {
res.writeHead(400, { 'Content-Type': 'text/plain' });
return res.end(`Bad request.`);
}

const knownCallbackUri = mapCallbackUriToRequestId.get(requestId);
if (knownCallbackUri) {
mapCallbackUriToRequestId.delete(requestId);
}

res.writeHead(200, { 'Content-Type': 'text/json' });
return res.end(knownCallbackUri);
}


interface ServerParsedArgs extends NativeParsedArgs {
port?: string
}
Expand Down Expand Up @@ -253,14 +326,15 @@ export interface IServerOptions {
}

export async function main(options: IServerOptions): Promise<void> {
const devMode = !!process.env['VSCODE_DEV'];
const connectionToken = generateUuid();

const parsedArgs = parseArgs(process.argv, SERVER_OPTIONS);
parsedArgs['user-data-dir'] = URI.file(path.join(os.homedir(), product.dataFolderName)).fsPath;
const productService = { _serviceBrand: undefined, ...product };
const environmentService = new NativeEnvironmentService(parsedArgs, productService);

const devMode = !environmentService.isBuilt;

// see src/vs/code/electron-main/main.ts#142
const bufferLogService = new BufferLogService();
const logService = new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentService)), bufferLogService]);
Expand Down Expand Up @@ -613,6 +687,15 @@ export async function main(options: IServerOptions): Promise<void> {
if (pathname === '/') {
return handleRoot(req, res, devMode ? options.mainDev || WEB_MAIN_DEV : options.main || WEB_MAIN, environmentService);
}

if (pathname === '/callback') {
return handleCallback(logService, req, res, parsedUrl);
}

if (pathname === '/fetch-callback') {
return handleFetchCallback(req, res, parsedUrl);
}

if (pathname === '/manifest.json') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({
Expand All @@ -634,7 +717,6 @@ export async function main(options: IServerOptions): Promise<void> {
}
//#region static end

// TODO uri callbacks ?
logService.error(`${req.method} ${req.url} not found`);
return serveError(req, res, 404, 'Not found.');
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions test/integration/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","5f19eee5dc9588ca96192f89587b5878b7d7180d"],["skipWelcome","true"]]`;

if (path.extname(testWorkspaceUri) === '.code-workspace') {
await page.goto(`${endpoint.href}&workspace=${testWorkspaceUri}&payload=${payloadParam}`);
await page.goto(`${endpoint.href}?workspace=${testWorkspaceUri}&payload=${payloadParam}`);
} else {
await page.goto(`${endpoint.href}&folder=${testWorkspaceUri}&payload=${payloadParam}`);
await page.goto(`${endpoint.href}?folder=${testWorkspaceUri}&payload=${payloadParam}`);
}

await page.exposeFunction('codeAutomationLog', (type: string, args: any[]) => {
Expand Down
4 changes: 4 additions & 0 deletions test/smoke/src/areas/terminal/terminal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export function setup(opts: minimist.ParsedArgs) {

it('shows terminal and runs command', async function () {
const app = this.app as Application;

// Canvas may cause problems when running in a container
await app.workbench.settingsEditor.addUserSetting('terminal.integrated.gpuAcceleration', '"off"');

await app.workbench.terminal.showTerminal();
await app.workbench.terminal.runCommand('ls');
await app.workbench.terminal.waitForTerminalText(lines => lines.some(l => l.includes('app.js')));
Expand Down