Merge pull request #284 from aeisenberg/aeisenberg/log-files
feat: Save log files per query
This commit is contained in:
Коммит
54ad3649b1
|
@ -80,7 +80,7 @@ jobs:
|
|||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cd extensions/ql-vscode
|
||||
$env:CODEQL_PATH=$(Join-Path $env:GITHUB_WORKSPACE -ChildPath 'codeql-home/codeql/codeql.cmd')
|
||||
$env:CODEQL_PATH=$(Join-Path $env:GITHUB_WORKSPACE -ChildPath 'codeql-home/codeql/codeql.exe')
|
||||
npm run test
|
||||
|
||||
- name: Run integration tests (Linux)
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql"
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql",
|
||||
"${workspaceRoot}/../vscode-codeql-starter/vscode-codeql-starter.code-workspace"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
|
|
|
@ -22,9 +22,12 @@ dependencies:
|
|||
'@types/node': 12.12.30
|
||||
'@types/node-fetch': 2.5.5
|
||||
'@types/npm-packlist': 1.1.1
|
||||
'@types/proxyquire': 1.3.28
|
||||
'@types/react': 16.9.23
|
||||
'@types/react-dom': 16.9.5
|
||||
'@types/sarif': 2.1.2
|
||||
'@types/sinon': 7.5.2
|
||||
'@types/sinon-chai': 3.2.3
|
||||
'@types/through2': 2.0.34
|
||||
'@types/tmp': 0.1.0
|
||||
'@types/unzipper': 0.10.2
|
||||
|
@ -57,10 +60,12 @@ dependencies:
|
|||
npm-packlist: 1.4.8
|
||||
npm-run-all: 4.1.5
|
||||
plugin-error: 1.0.1
|
||||
proxyquire: 2.1.3
|
||||
react: 16.13.0
|
||||
react-dom: 16.13.0_react@16.13.0
|
||||
reflect-metadata: 0.1.13
|
||||
sinon: 9.0.1
|
||||
sinon-chai: 3.5.0_chai@4.2.0+sinon@9.0.1
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
|
@ -453,6 +458,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
/@types/proxyquire/1.3.28:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==
|
||||
/@types/react-dom/16.9.5:
|
||||
dependencies:
|
||||
'@types/react': 16.9.23
|
||||
|
@ -470,6 +479,17 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TELZl5h48KaB6SFZqTuaMEw1hrGuusbBcH+yfMaaHdS2pwDr3RTH4CVN0LyY1kqSiDm9PPvAMx8FJ0LUZreOCQ==
|
||||
/@types/sinon-chai/3.2.3:
|
||||
dependencies:
|
||||
'@types/chai': 4.2.11
|
||||
'@types/sinon': 7.5.2
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==
|
||||
/@types/sinon/7.5.2:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==
|
||||
/@types/source-list-map/0.1.2:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -2595,6 +2615,15 @@ packages:
|
|||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
/fill-keys/1.0.2:
|
||||
dependencies:
|
||||
is-object: 1.0.1
|
||||
merge-descriptors: 1.0.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=
|
||||
/fill-range/4.0.0:
|
||||
dependencies:
|
||||
extend-shallow: 2.0.1
|
||||
|
@ -3557,6 +3586,10 @@ packages:
|
|||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
||||
/is-object/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-iVJojF7C/9awPsyF52ngKQMINHA=
|
||||
/is-plain-obj/2.1.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -4089,6 +4122,10 @@ packages:
|
|||
node: '>= 0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=
|
||||
/merge-descriptors/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
||||
/micromatch/3.1.10:
|
||||
dependencies:
|
||||
arr-diff: 4.0.0
|
||||
|
@ -4264,6 +4301,10 @@ packages:
|
|||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==
|
||||
/module-not-found-error/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=
|
||||
/move-concurrently/1.0.1:
|
||||
dependencies:
|
||||
aproba: 1.2.0
|
||||
|
@ -5118,6 +5159,14 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
/proxyquire/2.1.3:
|
||||
dependencies:
|
||||
fill-keys: 1.0.2
|
||||
module-not-found-error: 1.0.1
|
||||
resolve: 1.15.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==
|
||||
/prr/1.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -5674,6 +5723,16 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
/sinon-chai/3.5.0_chai@4.2.0+sinon@9.0.1:
|
||||
dependencies:
|
||||
chai: 4.2.0
|
||||
sinon: 9.0.1
|
||||
dev: false
|
||||
peerDependencies:
|
||||
chai: ^4.0.0
|
||||
sinon: '>=4.0.0 <10.0.0'
|
||||
resolution:
|
||||
integrity: sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==
|
||||
/sinon/9.0.1:
|
||||
dependencies:
|
||||
'@sinonjs/commons': 1.7.1
|
||||
|
@ -7178,9 +7237,12 @@ packages:
|
|||
'@types/mocha': 5.2.7
|
||||
'@types/node': 12.12.30
|
||||
'@types/node-fetch': 2.5.5
|
||||
'@types/proxyquire': 1.3.28
|
||||
'@types/react': 16.9.23
|
||||
'@types/react-dom': 16.9.5
|
||||
'@types/sarif': 2.1.2
|
||||
'@types/sinon': 7.5.2
|
||||
'@types/sinon-chai': 3.2.3
|
||||
'@types/tmp': 0.1.0
|
||||
'@types/unzipper': 0.10.2
|
||||
'@types/vscode': 1.43.0
|
||||
|
@ -7206,9 +7268,11 @@ packages:
|
|||
mocha-sinon: 2.1.0
|
||||
node-fetch: 2.6.0
|
||||
npm-run-all: 4.1.5
|
||||
proxyquire: 2.1.3
|
||||
react: 16.13.0
|
||||
react-dom: 16.13.0_react@16.13.0
|
||||
sinon: 9.0.1
|
||||
sinon-chai: 3.5.0_chai@4.2.0+sinon@9.0.1
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
|
@ -7230,9 +7294,10 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/vscode-codeql'
|
||||
resolution:
|
||||
integrity: sha512-XEQBLdsSWxofNXxh6Uwah2wvOUgMi/yGuU2afjauGOegrsH1G2DdKZ4kbbhfxKsEE7xxlQb4G8mu7SqjPt1Dag==
|
||||
integrity: sha512-PvC3L2Tp+VYm+hMzTgXfdBJPLJopSQpVsT8Ym7kdIxZj/cyTWzO3A+n7HnrH5q/B4DJSRfiDwjp73GwjGhbteQ==
|
||||
tarball: 'file:projects/vscode-codeql.tgz'
|
||||
version: 0.0.0
|
||||
registry: ''
|
||||
specifiers:
|
||||
'@microsoft/node-core-library': ~3.13.0
|
||||
'@microsoft/rush-lib': ~5.20.0
|
||||
|
@ -7257,9 +7322,12 @@ specifiers:
|
|||
'@types/node': ^12.0.8
|
||||
'@types/node-fetch': ~2.5.2
|
||||
'@types/npm-packlist': ~1.1.1
|
||||
'@types/proxyquire': ~1.3.28
|
||||
'@types/react': ^16.8.17
|
||||
'@types/react-dom': ^16.8.4
|
||||
'@types/sarif': ~2.1.2
|
||||
'@types/sinon': ~7.5.2
|
||||
'@types/sinon-chai': ~3.2.3
|
||||
'@types/through2': ~2.0.34
|
||||
'@types/tmp': ^0.1.0
|
||||
'@types/unzipper': ~0.10.1
|
||||
|
@ -7292,10 +7360,12 @@ specifiers:
|
|||
npm-packlist: ~1.4.4
|
||||
npm-run-all: ^4.1.5
|
||||
plugin-error: ^1.0.1
|
||||
proxyquire: ~2.1.3
|
||||
react: ^16.8.6
|
||||
react-dom: ^16.8.6
|
||||
reflect-metadata: ~0.1.13
|
||||
sinon: ~9.0.0
|
||||
sinon-chai: ~3.5.0
|
||||
style-loader: ~0.23.1
|
||||
through2: ^3.0.1
|
||||
tmp: ^0.1.0
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
ecmaFeatures: {
|
||||
modules: true,
|
||||
},
|
||||
project: ['tsconfig.json', './src/**/tsconfig.json'],
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
env: {
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
"scope": "machine",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Path to the CodeQL executable that should be used by the CodeQL extension. The executable is named `codeql` on Linux/Mac and `codeql.cmd` on Windows. This overrides all other CodeQL CLI settings."
|
||||
"description": "Path to the CodeQL executable that should be used by the CodeQL extension. The executable is named `codeql` on Linux/Mac and `codeql.exe` on Windows. This overrides all other CodeQL CLI settings."
|
||||
},
|
||||
"codeQL.runningQueries.numberOfThreads": {
|
||||
"type": "integer",
|
||||
|
@ -208,6 +208,10 @@
|
|||
"command": "codeQLQueryHistory.itemClicked",
|
||||
"title": "Query History Item"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"title": "Show Query Log"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryResults.nextPathStep",
|
||||
"title": "CodeQL: Show Next Step on Path"
|
||||
|
@ -272,6 +276,11 @@
|
|||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLTests.showOutputDifferences",
|
||||
"group": "qltest@1",
|
||||
|
@ -328,6 +337,10 @@
|
|||
"command": "codeQLQueryHistory.itemClicked",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.setLabel",
|
||||
"when": "false"
|
||||
|
@ -447,6 +460,11 @@
|
|||
"@typescript-eslint/eslint-plugin": "~2.23.0",
|
||||
"@typescript-eslint/parser": "~2.23.0",
|
||||
"chai-as-promised": "~7.1.1",
|
||||
"@types/chai-as-promised": "~7.1.2"
|
||||
"@types/chai-as-promised": "~7.1.2",
|
||||
"@types/sinon": "~7.5.2",
|
||||
"sinon-chai": "~3.5.0",
|
||||
"@types/sinon-chai": "~3.2.3",
|
||||
"proxyquire": "~2.1.3",
|
||||
"@types/proxyquire": "~1.3.28"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,10 @@ export type Entry = File | Directory;
|
|||
*/
|
||||
export type DirectoryHierarchyMap = Map<string, Map<string, vscode.FileType>>;
|
||||
|
||||
export type ZipFileReference = { sourceArchiveZipPath: string, pathWithinSourceArchive: string };
|
||||
export type ZipFileReference = {
|
||||
sourceArchiveZipPath: string;
|
||||
pathWithinSourceArchive: string;
|
||||
};
|
||||
|
||||
/** Encodes a reference to a source file within a zipped source archive into a single URI. */
|
||||
export function encodeSourceArchiveUri(ref: ZipFileReference): vscode.Uri {
|
||||
|
@ -169,7 +172,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
|||
async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
const ref = decodeSourceArchiveUri(uri);
|
||||
const archive = await this.getArchive(ref.sourceArchiveZipPath);
|
||||
let contents = archive.dirMap.get(ref.pathWithinSourceArchive);
|
||||
const contents = archive.dirMap.get(ref.pathWithinSourceArchive);
|
||||
const result = contents === undefined ? [] : Array.from(contents.entries());
|
||||
if (result === undefined) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
|
@ -239,7 +242,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
|
|||
}
|
||||
|
||||
private async _lookupAsFile(uri: vscode.Uri): Promise<File> {
|
||||
let entry = await this._lookup(uri);
|
||||
const entry = await this._lookup(uri);
|
||||
if (entry instanceof File) {
|
||||
return entry;
|
||||
}
|
||||
|
|
|
@ -243,11 +243,12 @@ export class CodeQLCliServer implements Disposable {
|
|||
// Kill the process if it isn't already dead.
|
||||
this.killProcessIfRunning();
|
||||
// Report the error (if there is a stderr then use that otherwise just report the error cod or nodejs error)
|
||||
if (stderrBuffers.length == 0) {
|
||||
throw new Error(`${description} failed: ${err}`)
|
||||
} else {
|
||||
throw new Error(`${description} failed: ${Buffer.concat(stderrBuffers).toString("utf8")}`);
|
||||
}
|
||||
const newError =
|
||||
stderrBuffers.length == 0
|
||||
? new Error(`${description} failed: ${err}`)
|
||||
: new Error(`${description} failed: ${Buffer.concat(stderrBuffers).toString("utf8")}`);
|
||||
newError.stack += (err.stack || '');
|
||||
throw newError;
|
||||
} finally {
|
||||
this.logger.log(Buffer.concat(stderrBuffers).toString("utf8"));
|
||||
// Remove the listeners we set up.
|
||||
|
@ -604,7 +605,7 @@ export class CodeQLCliServer implements Disposable {
|
|||
resolveQlpacks(additionalPacks: string[], searchPath?: string[]): Promise<QlpacksInfo> {
|
||||
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
|
||||
if (searchPath !== undefined) {
|
||||
args.push('--search-path', searchPath.join(path.delimiter));
|
||||
args.push('--search-path', path.join(...searchPath));
|
||||
}
|
||||
|
||||
return this.runJsonCodeQlCliCommand<QlpacksInfo>(
|
||||
|
|
|
@ -64,11 +64,11 @@ const DEBUG_SETTING = new Setting('debug', RUNNING_QUERIES_SETTING);
|
|||
const QUERY_SERVER_RESTARTING_SETTINGS = [NUMBER_OF_THREADS_SETTING, MEMORY_SETTING, DEBUG_SETTING];
|
||||
|
||||
export interface QueryServerConfig {
|
||||
codeQlPath: string,
|
||||
debug: boolean,
|
||||
numThreads: number,
|
||||
queryMemoryMb?: number,
|
||||
timeoutSecs: number,
|
||||
codeQlPath: string;
|
||||
debug: boolean;
|
||||
numThreads: number;
|
||||
queryMemoryMb?: number;
|
||||
timeoutSecs: number;
|
||||
onDidChangeQueryServerConfiguration?: Event<void>;
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export interface QueryServerConfig {
|
|||
const QUERY_HISTORY_SETTINGS = [QUERY_HISTORY_FORMAT_SETTING];
|
||||
|
||||
export interface QueryHistoryConfig {
|
||||
format: string,
|
||||
format: string;
|
||||
onDidChangeQueryHistoryConfiguration: Event<void>;
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ abstract class ConfigListener extends DisposableObject {
|
|||
|
||||
export class DistributionConfigListener extends ConfigListener implements DistributionConfig {
|
||||
public get customCodeQlPath(): string | undefined {
|
||||
return CUSTOM_CODEQL_PATH_SETTING.getValue() ? CUSTOM_CODEQL_PATH_SETTING.getValue() : undefined;
|
||||
return CUSTOM_CODEQL_PATH_SETTING.getValue() || undefined;
|
||||
}
|
||||
|
||||
public get includePrerelease(): boolean {
|
||||
|
@ -119,7 +119,7 @@ export class DistributionConfigListener extends ConfigListener implements Distri
|
|||
}
|
||||
|
||||
public get personalAccessToken(): string | undefined {
|
||||
return PERSONAL_ACCESS_TOKEN_SETTING.getValue() ? PERSONAL_ACCESS_TOKEN_SETTING.getValue() : undefined;
|
||||
return PERSONAL_ACCESS_TOKEN_SETTING.getValue() || undefined;
|
||||
}
|
||||
|
||||
public get onDidChangeDistributionConfiguration(): Event<void> {
|
||||
|
|
|
@ -19,7 +19,7 @@ import { getCodeQlCliVersion, tryParseVersionString, Version } from "./cli-versi
|
|||
|
||||
/**
|
||||
* Default value for the owner name of the extension-managed distribution on GitHub.
|
||||
*
|
||||
*
|
||||
* We set the default here rather than as a default config value so that this default is invoked
|
||||
* upon blanking the setting.
|
||||
*/
|
||||
|
@ -27,7 +27,7 @@ const DEFAULT_DISTRIBUTION_OWNER_NAME = "github";
|
|||
|
||||
/**
|
||||
* Default value for the repository name of the extension-managed distribution on GitHub.
|
||||
*
|
||||
*
|
||||
* We set the default here rather than as a default config value so that this default is invoked
|
||||
* upon blanking the setting.
|
||||
*/
|
||||
|
@ -35,7 +35,7 @@ const DEFAULT_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-binaries";
|
|||
|
||||
/**
|
||||
* Version constraint for the CLI.
|
||||
*
|
||||
*
|
||||
* This applies to both extension-managed and CLI distributions.
|
||||
*/
|
||||
export const DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT: VersionConstraint = {
|
||||
|
@ -46,8 +46,8 @@ export const DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT: VersionConstraint = {
|
|||
}
|
||||
|
||||
export interface DistributionProvider {
|
||||
getCodeQlPathWithoutVersionCheck(): Promise<string | undefined>,
|
||||
onDidChangeDistribution?: Event<void>
|
||||
getCodeQlPathWithoutVersionCheck(): Promise<string | undefined>;
|
||||
onDidChangeDistribution?: Event<void>;
|
||||
}
|
||||
|
||||
export class DistributionManager implements DistributionProvider {
|
||||
|
@ -99,7 +99,7 @@ export class DistributionManager implements DistributionProvider {
|
|||
*/
|
||||
public async getCodeQlPathWithoutVersionCheck(): Promise<string | undefined> {
|
||||
// Check config setting, then extension specific distribution, then PATH.
|
||||
if (this._config.customCodeQlPath !== undefined) {
|
||||
if (this._config.customCodeQlPath) {
|
||||
if (!await fs.pathExists(this._config.customCodeQlPath)) {
|
||||
showAndLogErrorMessage(`The CodeQL executable path is specified as "${this._config.customCodeQlPath}" ` +
|
||||
"by a configuration setting, but a CodeQL executable could not be found at that path. Please check " +
|
||||
|
@ -130,7 +130,7 @@ export class DistributionManager implements DistributionProvider {
|
|||
/**
|
||||
* Check for updates to the extension-managed distribution. If one has not already been installed,
|
||||
* this will return an update available result with the latest available release.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async checkForUpdatesToExtensionManagedDistribution(
|
||||
|
@ -152,7 +152,7 @@ export class DistributionManager implements DistributionProvider {
|
|||
|
||||
/**
|
||||
* Installs a release of the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public installExtensionManagedDistributionRelease(release: Release,
|
||||
|
@ -200,7 +200,7 @@ class ExtensionSpecificDistributionManager {
|
|||
/**
|
||||
* Check for updates to the extension-managed distribution. If one has not already been installed,
|
||||
* this will return an update available result with the latest available release.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async checkForUpdatesToDistribution(): Promise<DistributionUpdateCheckResult> {
|
||||
|
@ -216,7 +216,7 @@ class ExtensionSpecificDistributionManager {
|
|||
|
||||
/**
|
||||
* Installs a release of the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* Returns a failed promise if an unexpected error occurs during installation.
|
||||
*/
|
||||
public async installDistributionRelease(release: Release,
|
||||
|
@ -247,8 +247,8 @@ class ExtensionSpecificDistributionManager {
|
|||
|
||||
if (progressCallback && contentLength !== null) {
|
||||
const totalNumBytes = parseInt(contentLength, 10);
|
||||
const bytesToDisplayMB = (numBytes: number) => `${(numBytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
const updateProgress = () => {
|
||||
const bytesToDisplayMB = (numBytes: number): string => `${(numBytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
const updateProgress = (): void => {
|
||||
progressCallback({
|
||||
step: numBytesDownloaded,
|
||||
maxStep: totalNumBytes,
|
||||
|
@ -282,7 +282,7 @@ class ExtensionSpecificDistributionManager {
|
|||
|
||||
/**
|
||||
* Remove the extension-managed distribution.
|
||||
*
|
||||
*
|
||||
* This should not be called for a distribution that is currently in use, as remove may fail.
|
||||
*/
|
||||
private async removeDistribution(): Promise<void> {
|
||||
|
@ -357,7 +357,7 @@ export class ReleasesApiConsumer {
|
|||
this._repoName = repoName;
|
||||
}
|
||||
|
||||
public async getLatestRelease(versionConstraint: VersionConstraint, includePrerelease: boolean = false): Promise<Release> {
|
||||
public async getLatestRelease(versionConstraint: VersionConstraint, includePrerelease = false): Promise<Release> {
|
||||
const apiPath = `/repos/${this._ownerName}/${this._repoName}/releases`;
|
||||
const allReleases: GithubRelease[] = await (await this.makeApiCall(apiPath)).json();
|
||||
const compatibleReleases = allReleases.filter(release => {
|
||||
|
@ -428,7 +428,7 @@ export class ReleasesApiConsumer {
|
|||
private async makeRawRequest(
|
||||
requestUrl: string,
|
||||
headers: { [key: string]: string },
|
||||
redirectCount: number = 0): Promise<fetch.Response> {
|
||||
redirectCount = 0): Promise<fetch.Response> {
|
||||
const response = await fetch.default(requestUrl, {
|
||||
headers,
|
||||
redirect: "manual"
|
||||
|
@ -480,7 +480,7 @@ export async function extractZipArchive(archivePath: string, outPath: string): P
|
|||
|
||||
/**
|
||||
* Comparison of semantic versions.
|
||||
*
|
||||
*
|
||||
* Returns a positive number if a is greater than b.
|
||||
* Returns 0 if a equals b.
|
||||
* Returns a negative number if a is less than b.
|
||||
|
@ -502,7 +502,7 @@ export function versionCompare(a: Version, b: Version): number {
|
|||
}
|
||||
|
||||
function codeQlLauncherName(): string {
|
||||
return (os.platform() === "win32") ? "codeql.cmd" : "codeql";
|
||||
return (os.platform() === "win32") ? "codeql.exe" : "codeql";
|
||||
}
|
||||
|
||||
function isRedirectStatusCode(statusCode: number): boolean {
|
||||
|
@ -520,13 +520,16 @@ export enum FindDistributionResultKind {
|
|||
NoDistribution
|
||||
}
|
||||
|
||||
export type FindDistributionResult = CompatibleDistributionResult | UnknownCompatibilityDistributionResult |
|
||||
IncompatibleDistributionResult | NoDistributionResult;
|
||||
export type FindDistributionResult =
|
||||
| CompatibleDistributionResult
|
||||
| UnknownCompatibilityDistributionResult
|
||||
| IncompatibleDistributionResult
|
||||
| NoDistributionResult;
|
||||
|
||||
interface CompatibleDistributionResult {
|
||||
codeQlPath: string;
|
||||
kind: FindDistributionResultKind.CompatibleDistribution;
|
||||
version: Version
|
||||
version: Version;
|
||||
}
|
||||
|
||||
interface UnknownCompatibilityDistributionResult {
|
||||
|
@ -551,11 +554,14 @@ export enum DistributionUpdateCheckResultKind {
|
|||
UpdateAvailable
|
||||
}
|
||||
|
||||
type DistributionUpdateCheckResult = AlreadyCheckedRecentlyResult | AlreadyUpToDateResult | InvalidLocationResult |
|
||||
UpdateAvailableResult;
|
||||
type DistributionUpdateCheckResult =
|
||||
| AlreadyCheckedRecentlyResult
|
||||
| AlreadyUpToDateResult
|
||||
| InvalidLocationResult
|
||||
| UpdateAvailableResult;
|
||||
|
||||
export interface AlreadyCheckedRecentlyResult {
|
||||
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult
|
||||
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult;
|
||||
}
|
||||
|
||||
export interface AlreadyUpToDateResult {
|
||||
|
|
|
@ -76,10 +76,10 @@ function registerErrorStubs(excludedCommands: string[], stubGenerator: (command:
|
|||
}
|
||||
|
||||
export async function activate(ctx: ExtensionContext): Promise<void> {
|
||||
// Initialise logging, and ensure all loggers are disposed upon exit.
|
||||
ctx.subscriptions.push(logger);
|
||||
logger.log('Starting CodeQL extension');
|
||||
|
||||
initializeLogging(ctx);
|
||||
|
||||
const distributionConfigListener = new DistributionConfigListener();
|
||||
ctx.subscriptions.push(distributionConfigListener);
|
||||
const distributionManager = new DistributionManager(ctx, distributionConfigListener, DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT);
|
||||
|
@ -238,10 +238,6 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
const qlConfigurationListener = await QueryServerConfigListener.createQueryServerConfigListener(distributionManager);
|
||||
ctx.subscriptions.push(qlConfigurationListener);
|
||||
|
||||
ctx.subscriptions.push(queryServerLogger);
|
||||
ctx.subscriptions.push(ideServerLogger);
|
||||
|
||||
|
||||
const cliServer = new CodeQLCliServer(distributionManager, logger);
|
||||
ctx.subscriptions.push(cliServer);
|
||||
|
||||
|
@ -327,4 +323,13 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
ctx.subscriptions.push(client.start());
|
||||
}
|
||||
|
||||
function initializeLogging(ctx: ExtensionContext): void {
|
||||
logger.init(ctx);
|
||||
queryServerLogger.init(ctx);
|
||||
ideServerLogger.init(ctx);
|
||||
ctx.subscriptions.push(logger);
|
||||
ctx.subscriptions.push(queryServerLogger);
|
||||
ctx.subscriptions.push(ideServerLogger);
|
||||
}
|
||||
|
||||
const checkForUpdatesCommand = 'codeQL.checkForUpdatesToCLI';
|
||||
|
|
|
@ -17,8 +17,8 @@ export async function spawnIdeServer(config: QueryServerConfig): Promise<StreamI
|
|||
['execute', 'language-server'],
|
||||
['--check-errors', 'ON_CHANGE'],
|
||||
ideServerLogger,
|
||||
data => ideServerLogger.logWithoutTrailingNewline(data.toString()),
|
||||
data => ideServerLogger.logWithoutTrailingNewline(data.toString()),
|
||||
data => ideServerLogger.log(data.toString(), { trailingNewline: false }),
|
||||
data => ideServerLogger.log(data.toString(), { trailingNewline: false }),
|
||||
progressReporter
|
||||
);
|
||||
return { writer: child.stdin!, reader: child.stdout! };
|
||||
|
|
|
@ -1,17 +1,36 @@
|
|||
import { window as Window, OutputChannel, Progress } from 'vscode';
|
||||
import { window as Window, OutputChannel, Progress, ExtensionContext, Disposable } from 'vscode';
|
||||
import { DisposableObject } from 'semmle-vscode-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
interface LogOptions {
|
||||
/** If false, don't output a trailing newline for the log entry. Default true. */
|
||||
trailingNewline?: boolean;
|
||||
|
||||
/** If specified, add this log entry to the log file at the specified location. */
|
||||
additionalLogLocation?: string;
|
||||
}
|
||||
|
||||
export interface Logger {
|
||||
/** Writes the given log message, followed by a newline. */
|
||||
log(message: string): void;
|
||||
/** Writes the given log message, not followed by a newline. */
|
||||
logWithoutTrailingNewline(message: string): void;
|
||||
/** Writes the given log message, optionally followed by a newline. */
|
||||
log(message: string, options?: LogOptions): Promise<void>;
|
||||
/**
|
||||
* Reveal this channel in the UI.
|
||||
*
|
||||
* @param preserveFocus When `true` the channel will not take focus.
|
||||
*/
|
||||
show(preserveFocus?: boolean): void;
|
||||
|
||||
/**
|
||||
* Remove the log at the specified location
|
||||
* @param location log to remove
|
||||
*/
|
||||
removeAdditionalLogLocation(location: string | undefined): void;
|
||||
|
||||
/**
|
||||
* The base location location where all side log files are stored.
|
||||
*/
|
||||
getBaseLocation(): string | undefined;
|
||||
}
|
||||
|
||||
export type ProgressReporter = Progress<{ message: string }>;
|
||||
|
@ -19,24 +38,98 @@ export type ProgressReporter = Progress<{ message: string }>;
|
|||
/** A logger that writes messages to an output channel in the Output tab. */
|
||||
export class OutputChannelLogger extends DisposableObject implements Logger {
|
||||
public readonly outputChannel: OutputChannel;
|
||||
private readonly additionalLocations = new Map<string, AdditionalLogLocation>();
|
||||
private additionalLogLocationPath: string | undefined;
|
||||
|
||||
constructor(title: string) {
|
||||
constructor(private title: string) {
|
||||
super();
|
||||
this.outputChannel = Window.createOutputChannel(title);
|
||||
this.push(this.outputChannel);
|
||||
}
|
||||
|
||||
log(message: string) {
|
||||
this.outputChannel.appendLine(message);
|
||||
init(ctx: ExtensionContext): void {
|
||||
this.additionalLogLocationPath = path.join(ctx.storagePath || ctx.globalStoragePath, this.title);
|
||||
|
||||
// clear out any old state from previous runs
|
||||
fs.remove(this.additionalLogLocationPath);
|
||||
}
|
||||
|
||||
logWithoutTrailingNewline(message: string) {
|
||||
this.outputChannel.append(message);
|
||||
/**
|
||||
* This function is asynchronous and will only resolve once the message is written
|
||||
* to the side log (if required). It is not necessary to await the results of this
|
||||
* function if you don't need to guarantee that the log writing is complete before
|
||||
* continuing.
|
||||
*/
|
||||
async log(message: string, options = { } as LogOptions): Promise<void> {
|
||||
if (options.trailingNewline === undefined) {
|
||||
options.trailingNewline = true;
|
||||
}
|
||||
|
||||
if (options.trailingNewline) {
|
||||
this.outputChannel.appendLine(message);
|
||||
} else {
|
||||
this.outputChannel.append(message);
|
||||
}
|
||||
|
||||
if (this.additionalLogLocationPath && options.additionalLogLocation) {
|
||||
const logPath = path.join(this.additionalLogLocationPath, options.additionalLogLocation);
|
||||
let additional = this.additionalLocations.get(logPath);
|
||||
if (!additional) {
|
||||
const msg = `| Log being saved to ${logPath} |`;
|
||||
const separator = new Array(msg.length).fill('-').join('');
|
||||
this.outputChannel.appendLine(separator);
|
||||
this.outputChannel.appendLine(msg);
|
||||
this.outputChannel.appendLine(separator);
|
||||
additional = new AdditionalLogLocation(logPath);
|
||||
this.additionalLocations.set(logPath, additional);
|
||||
this.track(additional);
|
||||
}
|
||||
|
||||
await additional.log(message, options);
|
||||
}
|
||||
}
|
||||
|
||||
show(preserveFocus?: boolean) {
|
||||
show(preserveFocus?: boolean): void {
|
||||
this.outputChannel.show(preserveFocus);
|
||||
}
|
||||
|
||||
removeAdditionalLogLocation(location: string | undefined): void {
|
||||
if (this.additionalLogLocationPath && location) {
|
||||
const logPath = location.startsWith(this.additionalLogLocationPath)
|
||||
? location
|
||||
: path.join(this.additionalLogLocationPath, location);
|
||||
const additional = this.additionalLocations.get(logPath);
|
||||
if (additional) {
|
||||
this.disposeAndStopTracking(additional);
|
||||
this.additionalLocations.delete(logPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBaseLocation() {
|
||||
return this.additionalLogLocationPath;
|
||||
}
|
||||
}
|
||||
|
||||
class AdditionalLogLocation extends Disposable {
|
||||
constructor(private location: string) {
|
||||
super(() => { /**/ });
|
||||
}
|
||||
|
||||
async log(message: string, options = { } as LogOptions): Promise<void> {
|
||||
if (options.trailingNewline === undefined) {
|
||||
options.trailingNewline = true;
|
||||
}
|
||||
await fs.ensureFile(this.location);
|
||||
|
||||
await fs.appendFile(this.location, message + (options.trailingNewline ? '\n' : ''), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
await fs.remove(this.location);
|
||||
}
|
||||
}
|
||||
|
||||
/** The global logger for the extension. */
|
||||
|
@ -46,7 +139,9 @@ export const logger = new OutputChannelLogger('CodeQL Extension Log');
|
|||
export const queryServerLogger = new OutputChannelLogger('CodeQL Query Server');
|
||||
|
||||
/** The logger for messages from the language server. */
|
||||
export const ideServerLogger = new OutputChannelLogger('CodeQL Language Server');
|
||||
export const ideServerLogger = new OutputChannelLogger(
|
||||
'CodeQL Language Server'
|
||||
);
|
||||
|
||||
/** The logger for messages from tests. */
|
||||
export const testLogger = new OutputChannelLogger('CodeQL Tests');
|
||||
|
|
|
@ -778,6 +778,11 @@ export interface EvaluationResult {
|
|||
* An error message if an error happened
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* Full path to file with all log messages emitted while this query was active, if one exists
|
||||
*/
|
||||
logFileLocation?: string;
|
||||
}
|
||||
|
||||
export type QueryResultType = number;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { ExtensionContext, window as Window } from 'vscode';
|
|||
import { CompletedQuery } from './query-results';
|
||||
import { QueryHistoryConfig } from './config';
|
||||
import { QueryWithResults } from './run-queries';
|
||||
import * as helpers from './helpers';
|
||||
|
||||
/**
|
||||
* query-history.ts
|
||||
|
@ -15,14 +16,14 @@ import { QueryWithResults } from './run-queries';
|
|||
*/
|
||||
|
||||
export type QueryHistoryItemOptions = {
|
||||
label?: string, // user-settable label
|
||||
queryText?: string, // stored query for quick query
|
||||
label?: string; // user-settable label
|
||||
queryText?: string; // stored query for quick query
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to icon to display next to a failed query history item.
|
||||
*/
|
||||
const FAILED_QUERY_HISTORY_ITEM_ICON: string = 'media/red-x.svg';
|
||||
const FAILED_QUERY_HISTORY_ITEM_ICON = 'media/red-x.svg';
|
||||
|
||||
/**
|
||||
* Tree data provider for the query history view.
|
||||
|
@ -122,7 +123,7 @@ export class QueryHistoryManager {
|
|||
ctx: ExtensionContext;
|
||||
treeView: vscode.TreeView<CompletedQuery>;
|
||||
selectedCallback: ((item: CompletedQuery) => void) | undefined;
|
||||
lastItemClick: { time: Date, item: CompletedQuery } | undefined;
|
||||
lastItemClick: { time: Date; item: CompletedQuery } | undefined;
|
||||
|
||||
async invokeCallbackOn(queryHistoryItem: CompletedQuery) {
|
||||
if (this.selectedCallback !== undefined) {
|
||||
|
@ -144,6 +145,7 @@ export class QueryHistoryManager {
|
|||
|
||||
async handleRemoveHistoryItem(queryHistoryItem: CompletedQuery) {
|
||||
this.treeDataProvider.remove(queryHistoryItem);
|
||||
queryHistoryItem.dispose();
|
||||
const current = this.treeDataProvider.getCurrent();
|
||||
if (current !== undefined) {
|
||||
this.treeView.reveal(current);
|
||||
|
@ -187,6 +189,20 @@ export class QueryHistoryManager {
|
|||
}
|
||||
}
|
||||
|
||||
async handleShowQueryLog(queryHistoryItem: CompletedQuery) {
|
||||
if (queryHistoryItem.logFileLocation) {
|
||||
try {
|
||||
await vscode.window.showTextDocument(vscode.Uri.parse(queryHistoryItem.logFileLocation), {
|
||||
viewColumn: vscode.ViewColumn.Beside
|
||||
});
|
||||
} catch (e) {
|
||||
helpers.showAndLogErrorMessage(`Could not open log file ${queryHistoryItem.logFileLocation}`);
|
||||
}
|
||||
} else {
|
||||
helpers.showAndLogWarningMessage('No log file available');
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
ctx: ExtensionContext,
|
||||
private queryHistoryConfigListener: QueryHistoryConfig,
|
||||
|
@ -208,6 +224,7 @@ export class QueryHistoryManager {
|
|||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.openQuery', this.handleOpenQuery));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
|
||||
return this.handleItemClicked(item);
|
||||
}));
|
||||
|
|
|
@ -14,7 +14,9 @@ export class CompletedQuery implements QueryWithResults {
|
|||
readonly query: QueryInfo;
|
||||
readonly result: messages.EvaluationResult;
|
||||
readonly database: DatabaseInfo;
|
||||
readonly logFileLocation?: string
|
||||
options: QueryHistoryItemOptions;
|
||||
dispose: () => void;
|
||||
|
||||
/**
|
||||
* Map from result set name to SortedResultSetInfo.
|
||||
|
@ -31,15 +33,18 @@ export class CompletedQuery implements QueryWithResults {
|
|||
interpretedResultsSortState: InterpretedResultsSortState | undefined;
|
||||
|
||||
constructor(
|
||||
evalaution: QueryWithResults,
|
||||
evaluation: QueryWithResults,
|
||||
public config: QueryHistoryConfig,
|
||||
) {
|
||||
this.query = evalaution.query;
|
||||
this.result = evalaution.result;
|
||||
this.database = evalaution.database;
|
||||
this.query = evaluation.query;
|
||||
this.result = evaluation.result;
|
||||
this.database = evaluation.database;
|
||||
this.logFileLocation = evaluation.logFileLocation;
|
||||
this.options = evaluation.options;
|
||||
this.dispose = evaluation.dispose;
|
||||
|
||||
this.time = new Date().toLocaleString();
|
||||
this.sortedResultsInfo = new Map();
|
||||
this.options = evalaution.options;
|
||||
}
|
||||
|
||||
get databaseName(): string {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
// Import from the specific module within `semmle-vscode-utils`, rather than via `index.ts`, because
|
||||
// we avoid taking an accidental runtime dependency on `vscode` this way.
|
||||
import { DisposableObject } from 'semmle-vscode-utils/out/disposable-object';
|
||||
|
@ -8,9 +9,10 @@ import * as cli from './cli';
|
|||
import { QueryServerConfig } from './config';
|
||||
import { Logger, ProgressReporter } from './logging';
|
||||
import { completeQuery, EvaluationResult, progress, ProgressMessage, WithProgressId } from './messages';
|
||||
import * as messages from './messages';
|
||||
|
||||
type ServerOpts = {
|
||||
logger: Logger
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
/** A running query server process and its associated message connection. */
|
||||
|
@ -25,7 +27,7 @@ class ServerProcess implements Disposable {
|
|||
this.logger = logger;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose(): void {
|
||||
this.logger.log('Stopping query server...');
|
||||
this.connection.dispose();
|
||||
this.child.stdin!.end();
|
||||
|
@ -53,6 +55,7 @@ export class QueryServerClient extends DisposableObject {
|
|||
nextCallback: number;
|
||||
nextProgress: number;
|
||||
withProgressReporting: WithProgressReporting;
|
||||
public activeQueryName: string | undefined;
|
||||
|
||||
constructor(readonly config: QueryServerConfig, readonly cliServer: cli.CodeQLCliServer, readonly opts: ServerOpts, withProgressReporting: WithProgressReporting) {
|
||||
super();
|
||||
|
@ -70,10 +73,12 @@ export class QueryServerClient extends DisposableObject {
|
|||
this.evaluationResultCallbacks = {};
|
||||
}
|
||||
|
||||
get logger() { return this.opts.logger; }
|
||||
get logger(): Logger {
|
||||
return this.opts.logger;
|
||||
}
|
||||
|
||||
/** Stops the query server by disposing of the current server process. */
|
||||
private stopQueryServer() {
|
||||
private stopQueryServer(): void {
|
||||
if (this.serverProcess !== undefined) {
|
||||
this.disposeAndStopTracking(this.serverProcess);
|
||||
} else {
|
||||
|
@ -82,23 +87,23 @@ export class QueryServerClient extends DisposableObject {
|
|||
}
|
||||
|
||||
/** Restarts the query server by disposing of the current server process and then starting a new one. */
|
||||
async restartQueryServer() {
|
||||
async restartQueryServer(): Promise<void> {
|
||||
this.stopQueryServer();
|
||||
await this.startQueryServer();
|
||||
}
|
||||
|
||||
async showLog() {
|
||||
showLog(): void {
|
||||
this.logger.show();
|
||||
}
|
||||
|
||||
/** Starts a new query server process, sending progress messages to the status bar. */
|
||||
async startQueryServer() {
|
||||
async startQueryServer(): Promise<void> {
|
||||
// Use an arrow function to preserve the value of `this`.
|
||||
return this.withProgressReporting((progress, _) => this.startQueryServerImpl(progress));
|
||||
}
|
||||
|
||||
/** Starts a new query server process, sending progress messages to the given reporter. */
|
||||
private async startQueryServerImpl(progressReporter: ProgressReporter) {
|
||||
private async startQueryServerImpl(progressReporter: ProgressReporter): Promise<void> {
|
||||
const ramArgs = await this.cliServer.resolveRam(this.config.queryMemoryMb, progressReporter);
|
||||
const args = ['--threads', this.config.numThreads.toString()].concat(ramArgs);
|
||||
if (this.config.debug) {
|
||||
|
@ -110,7 +115,10 @@ export class QueryServerClient extends DisposableObject {
|
|||
['execute', 'query-server'],
|
||||
args,
|
||||
this.logger,
|
||||
data => this.logger.logWithoutTrailingNewline(data.toString()),
|
||||
data => this.logger.log(data.toString(), {
|
||||
trailingNewline: false,
|
||||
additionalLogLocation: this.activeQueryName
|
||||
}),
|
||||
undefined, // no listener for stdout
|
||||
progressReporter
|
||||
);
|
||||
|
@ -121,12 +129,16 @@ export class QueryServerClient extends DisposableObject {
|
|||
this.logger.log(`No callback associated with run id ${res.runId}, continuing without executing any callback`);
|
||||
}
|
||||
else {
|
||||
const baseLocation = this.logger.getBaseLocation();
|
||||
if (baseLocation && this.activeQueryName) {
|
||||
res.logFileLocation = path.join(baseLocation, this.activeQueryName);
|
||||
}
|
||||
this.evaluationResultCallbacks[res.runId](res);
|
||||
}
|
||||
return {};
|
||||
})
|
||||
connection.onNotification(progress, res => {
|
||||
let callback = this.progressCallbacks[res.id];
|
||||
const callback = this.progressCallbacks[res.id];
|
||||
if (callback) {
|
||||
callback(res);
|
||||
}
|
||||
|
@ -148,7 +160,7 @@ export class QueryServerClient extends DisposableObject {
|
|||
return id;
|
||||
}
|
||||
|
||||
unRegisterCallback(id: number) {
|
||||
unRegisterCallback(id: number): void {
|
||||
delete this.evaluationResultCallbacks[id];
|
||||
}
|
||||
|
||||
|
@ -157,8 +169,10 @@ export class QueryServerClient extends DisposableObject {
|
|||
}
|
||||
|
||||
async sendRequest<P, R, E, RO>(type: RequestType<WithProgressId<P>, R, E, RO>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise<R> {
|
||||
let id = this.nextProgress++;
|
||||
const id = this.nextProgress++;
|
||||
this.progressCallbacks[id] = progress;
|
||||
|
||||
this.updateActiveQuery(type.method, parameter);
|
||||
try {
|
||||
if (this.serverProcess === undefined) {
|
||||
throw new Error('No query server process found.');
|
||||
|
@ -168,4 +182,19 @@ export class QueryServerClient extends DisposableObject {
|
|||
delete this.progressCallbacks[id];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the active query every time there is a new request to compile.
|
||||
* The active query is used to specify the side log.
|
||||
*
|
||||
* This isn't ideal because in situations where there are queries running
|
||||
* in parallel, each query's log messages are interleaved. Fixing this
|
||||
* properly will require a change in the query server.
|
||||
*/
|
||||
private updateActiveQuery(method: string, parameter: any): void {
|
||||
if (method === messages.compileQuery.method) {
|
||||
const queryPath = parameter?.queryToCheck?.queryPath || 'unknown';
|
||||
this.activeQueryName = `query-${path.basename(queryPath)}-${this.nextProgress}.log`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function isQuickQueryPath(queryPath: string): boolean {
|
|||
|
||||
async function getQlPackFor(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(helpers.getOnDiskWorkspaceFolders());
|
||||
const packs: { packDir: string | undefined, packName: string }[] =
|
||||
const packs: { packDir: string | undefined; packName: string }[] =
|
||||
Object.entries(qlpacks).map(([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`);
|
||||
|
@ -60,7 +60,7 @@ function getBaseText(dbschemeBase: string) {
|
|||
return 'select ""';
|
||||
}
|
||||
|
||||
async function getQuickQueriesDir(ctx: ExtensionContext): Promise<string> {
|
||||
function getQuickQueriesDir(ctx: ExtensionContext): string {
|
||||
const storagePath = ctx.storagePath;
|
||||
if (storagePath === undefined) {
|
||||
throw new Error('Workspace storage path is undefined');
|
||||
|
|
|
@ -136,10 +136,13 @@ export class QueryInfo {
|
|||
},
|
||||
queryToCheck: this.program,
|
||||
resultPath: this.compiledQueryPath,
|
||||
target: !!this.quickEvalPosition ? { quickEval: { quickEvalPos: this.quickEvalPosition } } : { query: {} }
|
||||
target: this.quickEvalPosition ? {
|
||||
quickEval: { quickEvalPos: this.quickEvalPosition }
|
||||
} : {
|
||||
query: {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
compiled = await helpers.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Compiling Query",
|
||||
|
@ -170,10 +173,13 @@ export interface QueryWithResults {
|
|||
readonly result: messages.EvaluationResult;
|
||||
readonly database: DatabaseInfo;
|
||||
readonly options: QueryHistoryItemOptions;
|
||||
readonly logFileLocation?: string;
|
||||
readonly dispose: () => void;
|
||||
}
|
||||
|
||||
export async function clearCacheInDatabase(qs: qsClient.QueryServerClient, dbItem: DatabaseItem):
|
||||
Promise<messages.ClearCacheResult> {
|
||||
export async function clearCacheInDatabase(
|
||||
qs: qsClient.QueryServerClient, dbItem: DatabaseItem
|
||||
): Promise<messages.ClearCacheResult> {
|
||||
if (dbItem.contents === undefined) {
|
||||
throw new Error('Can\'t clear the cache in an invalid database.');
|
||||
}
|
||||
|
@ -253,7 +259,7 @@ async function checkDbschemeCompatibility(
|
|||
|
||||
if (query.dbItem.contents !== undefined && query.dbItem.contents.dbSchemeUri !== undefined) {
|
||||
const { scripts, finalDbscheme } = await cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath);
|
||||
async function hash(filename: string): Promise<string> {
|
||||
const hash = async function (filename: string): Promise<string> {
|
||||
return crypto.createHash('sha256').update(await fs.readFile(filename)).digest('hex');
|
||||
}
|
||||
|
||||
|
@ -289,7 +295,7 @@ async function checkDbschemeCompatibility(
|
|||
}
|
||||
|
||||
/** Prompts the user to save `document` if it has unsaved changes. */
|
||||
async function promptUserToSaveChanges(document: vscode.TextDocument) {
|
||||
async function promptUserToSaveChanges(document: vscode.TextDocument): Promise<void> {
|
||||
if (document.isDirty) {
|
||||
// TODO: add 'always save' button which records preference in configuration
|
||||
if (await helpers.showBinaryChoiceDialog('Query file has unsaved changes. Save now?')) {
|
||||
|
@ -299,8 +305,8 @@ async function promptUserToSaveChanges(document: vscode.TextDocument) {
|
|||
}
|
||||
|
||||
type SelectedQuery = {
|
||||
queryPath: string,
|
||||
quickEvalPosition?: messages.Position
|
||||
queryPath: string;
|
||||
quickEvalPosition?: messages.Position;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -457,7 +463,11 @@ export async function compileAndRunQueryAgainstDatabase(
|
|||
name: db.name,
|
||||
databaseUri: db.databaseUri.toString(true)
|
||||
},
|
||||
options: historyItemOptions
|
||||
options: historyItemOptions,
|
||||
logFileLocation: result.logFileLocation,
|
||||
dispose: () => {
|
||||
qs.logger.removeAdditionalLogLocation(result.logFileLocation);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Error dialogs are limited in size and scrollability,
|
||||
|
@ -466,7 +476,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
|||
// However we don't show quick eval errors there so we need to display them anyway.
|
||||
qs.logger.log(`Failed to compile query ${query.program.queryPath} against database scheme ${query.program.dbschemePath}:`);
|
||||
|
||||
let formattedMessages: string[] = [];
|
||||
const formattedMessages: string[] = [];
|
||||
|
||||
for (const error of errors) {
|
||||
const message = error.message || "[no error message available]";
|
||||
|
@ -493,7 +503,7 @@ function createSyntheticResult(
|
|||
historyItemOptions: QueryHistoryItemOptions,
|
||||
message: string,
|
||||
resultType: number
|
||||
) {
|
||||
): QueryWithResults {
|
||||
|
||||
return {
|
||||
query,
|
||||
|
@ -509,5 +519,6 @@ function createSyntheticResult(
|
|||
databaseUri: db.databaseUri.toString(true)
|
||||
},
|
||||
options: historyItemOptions,
|
||||
dispose: () => { /**/ },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ const MAX_UPGRADE_MESSAGE_LINES = 10;
|
|||
* Reports errors to both the user and the console.
|
||||
* @returns the `UpgradeParams` needed to start the upgrade, if the upgrade is possible and was confirmed by the user, or `undefined` otherwise.
|
||||
*/
|
||||
async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]):
|
||||
Promise<messages.UpgradeParams | undefined> {
|
||||
async function checkAndConfirmDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]
|
||||
): Promise<messages.UpgradeParams | undefined> {
|
||||
if (db.contents === undefined || db.contents.dbSchemeUri === undefined) {
|
||||
helpers.showAndLogErrorMessage("Database is invalid, and cannot be upgraded.");
|
||||
return;
|
||||
|
@ -80,7 +81,7 @@ async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db
|
|||
const showLogItem: vscode.MessageItem = { title: 'No, Show Changes', isCloseAffordance: true };
|
||||
const yesItem = { title: 'Yes', isCloseAffordance: false };
|
||||
const noItem = { title: 'No', isCloseAffordance: true }
|
||||
let dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
|
||||
const dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
|
||||
|
||||
let messageLines = descriptionMessage.split('\n');
|
||||
if (messageLines.length > MAX_UPGRADE_MESSAGE_LINES) {
|
||||
|
@ -110,8 +111,9 @@ async function checkAndConfirmDatabaseUpgrade(qs: qsClient.QueryServerClient, db
|
|||
* First performs a dry-run and prompts the user to confirm the upgrade.
|
||||
* Reports errors during compilation and evaluation of upgrades to the user.
|
||||
*/
|
||||
export async function upgradeDatabase(qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]):
|
||||
Promise<messages.RunUpgradeResult | undefined> {
|
||||
export async function upgradeDatabase(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, targetDbScheme: vscode.Uri, upgradesDirectories: vscode.Uri[]
|
||||
): Promise<messages.RunUpgradeResult | undefined> {
|
||||
const upgradeParams = await checkAndConfirmDatabaseUpgrade(qs, db, targetDbScheme, upgradesDirectories);
|
||||
|
||||
if (upgradeParams === undefined) {
|
||||
|
@ -150,8 +152,9 @@ export async function upgradeDatabase(qs: qsClient.QueryServerClient, db: Databa
|
|||
}
|
||||
}
|
||||
|
||||
async function checkDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams):
|
||||
Promise<messages.CheckUpgradeResult> {
|
||||
async function checkDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams
|
||||
): Promise<messages.CheckUpgradeResult> {
|
||||
return helpers.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Checking for database upgrades",
|
||||
|
@ -159,8 +162,9 @@ async function checkDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParam
|
|||
}, (progress, token) => qs.sendRequest(messages.checkUpgrade, upgradeParams, token, progress));
|
||||
}
|
||||
|
||||
async function compileDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams):
|
||||
Promise<messages.CompileUpgradeResult> {
|
||||
async function compileDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, upgradeParams: messages.UpgradeParams
|
||||
): Promise<messages.CompileUpgradeResult> {
|
||||
const params: messages.CompileUpgradeParams = {
|
||||
upgrade: upgradeParams,
|
||||
upgradeTempDir: upgradesTmpDir.name
|
||||
|
@ -173,8 +177,9 @@ async function compileDatabaseUpgrade(qs: qsClient.QueryServerClient, upgradePar
|
|||
}, (progress, token) => qs.sendRequest(messages.compileUpgrade, params, token, progress));
|
||||
}
|
||||
|
||||
async function runDatabaseUpgrade(qs: qsClient.QueryServerClient, db: DatabaseItem, upgrades: messages.CompiledUpgrades):
|
||||
Promise<messages.RunUpgradeResult> {
|
||||
async function runDatabaseUpgrade(
|
||||
qs: qsClient.QueryServerClient, db: DatabaseItem, upgrades: messages.CompiledUpgrades
|
||||
): Promise<messages.RunUpgradeResult> {
|
||||
|
||||
if (db.contents === undefined || db.contents.datasetUri === undefined) {
|
||||
throw new Error('Can\'t upgrade an invalid database.');
|
||||
|
|
|
@ -4,5 +4,5 @@ module.exports = {
|
|||
},
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
mocha: true
|
||||
}
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
import * as chai from 'chai';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp';
|
||||
import 'mocha';
|
||||
import * as sinonChai from 'sinon-chai';
|
||||
import * as sinon from 'sinon';
|
||||
import * as pq from 'proxyquire';
|
||||
|
||||
const proxyquire = pq.noPreserveCache().noCallThru();
|
||||
chai.should();
|
||||
chai.use(sinonChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('OutputChannelLogger tests', () => {
|
||||
let OutputChannelLogger;
|
||||
const tempFolders: Record<string, tmp.DirResult> = {};
|
||||
let logger: any;
|
||||
let mockOutputChannel: Record<string, sinon.SinonStub>;
|
||||
|
||||
beforeEach(async () => {
|
||||
OutputChannelLogger = createModule().OutputChannelLogger;
|
||||
tempFolders.globalStoragePath = tmp.dirSync({ prefix: 'logging-tests-global' });
|
||||
tempFolders.storagePath = tmp.dirSync({ prefix: 'logging-tests-workspace' });
|
||||
logger = new OutputChannelLogger('test-logger');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
tempFolders.globalStoragePath.removeCallback();
|
||||
tempFolders.storagePath.removeCallback();
|
||||
});
|
||||
|
||||
it('should log to the output channel', async () => {
|
||||
await logger.log('xxx');
|
||||
expect(mockOutputChannel.appendLine).to.have.been.calledWith('xxx');
|
||||
expect(mockOutputChannel.append).not.to.have.been.calledWith('xxx');
|
||||
|
||||
await logger.log('yyy', { trailingNewline: false });
|
||||
expect(mockOutputChannel.appendLine).not.to.have.been.calledWith('yyy');
|
||||
expect(mockOutputChannel.append).to.have.been.calledWith('yyy');
|
||||
|
||||
// additionalLogLocation ignored since not initialized
|
||||
await logger.log('zzz', { additionalLogLocation: 'hucairz' });
|
||||
|
||||
// should not have created any side logs
|
||||
expect(fs.readdirSync(tempFolders.globalStoragePath.name).length).to.equal(0);
|
||||
expect(fs.readdirSync(tempFolders.storagePath.name).length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should create a side log in the workspace area', async () => {
|
||||
await sideLogTest('storagePath', 'globalStoragePath');
|
||||
});
|
||||
|
||||
it('should create a side log in the global area', async () => {
|
||||
await sideLogTest('globalStoragePath', 'storagePath');
|
||||
});
|
||||
|
||||
async function sideLogTest(expectedArea: string, otherArea: string): Promise<void> {
|
||||
logger.init({
|
||||
[expectedArea]: tempFolders[expectedArea].name,
|
||||
[otherArea]: undefined
|
||||
});
|
||||
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
await logger.log('zzz', { additionalLogLocation: 'first', trailingNewline: false });
|
||||
await logger.log('aaa');
|
||||
|
||||
// expect 2 side logs
|
||||
const testLoggerFolder = path.join(tempFolders[expectedArea].name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
expect(fs.readdirSync(tempFolders[otherArea].name).length).to.equal(0);
|
||||
|
||||
// contents
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'first'), 'utf8')).to.equal('xxx\nzzz');
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'second'), 'utf8')).to.equal('yyy\n');
|
||||
}
|
||||
|
||||
it('should delete side logs on dispose', async () => {
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
|
||||
await logger.dispose();
|
||||
// need to wait for disposable-object to dispose
|
||||
await waitABit();
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(0);
|
||||
expect(mockOutputChannel.dispose).to.have.been.calledWith();
|
||||
});
|
||||
|
||||
it('should remove an additional log location', async () => {
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
await logger.log('xxx', { additionalLogLocation: 'first' });
|
||||
await logger.log('yyy', { additionalLogLocation: 'second' });
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(2);
|
||||
|
||||
await logger.removeAdditionalLogLocation('first');
|
||||
// need to wait for disposable-object to dispose
|
||||
await waitABit();
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(1);
|
||||
expect(fs.readFileSync(path.join(testLoggerFolder, 'second'), 'utf8')).to.equal('yyy\n');
|
||||
});
|
||||
|
||||
it('should delete an existing folder on init', async () => {
|
||||
fs.createFileSync(path.join(tempFolders.storagePath.name, 'test-logger', 'xxx'));
|
||||
logger.init({
|
||||
storagePath: tempFolders.storagePath.name
|
||||
});
|
||||
// should be empty dir
|
||||
|
||||
const testLoggerFolder = path.join(tempFolders.storagePath.name, 'test-logger');
|
||||
expect(fs.readdirSync(testLoggerFolder).length).to.equal(1);
|
||||
});
|
||||
|
||||
it('should show the output channel', () => {
|
||||
logger.show(true);
|
||||
expect(mockOutputChannel.show).to.have.been.calledWith(true);
|
||||
});
|
||||
|
||||
function createModule(): any {
|
||||
mockOutputChannel = {
|
||||
append: sinon.stub(),
|
||||
appendLine: sinon.stub(),
|
||||
show: sinon.stub(),
|
||||
dispose: sinon.stub(),
|
||||
};
|
||||
|
||||
return proxyquire('../../src/logging', {
|
||||
vscode: {
|
||||
window: {
|
||||
createOutputChannel: () => mockOutputChannel
|
||||
},
|
||||
Disposable: function() {
|
||||
/**/
|
||||
},
|
||||
'@noCallThru': true,
|
||||
'@global': true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function waitABit(ms = 50): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
});
|
|
@ -31,31 +31,31 @@ class Checkpoint<T> {
|
|||
private promise: Promise<T>;
|
||||
|
||||
constructor() {
|
||||
this.res = () => { };
|
||||
this.rej = () => { };
|
||||
this.res = () => { /**/ };
|
||||
this.rej = () => { /**/ };
|
||||
this.promise = new Promise((res, rej) => { this.res = res; this.rej = rej; })
|
||||
}
|
||||
|
||||
async done() {
|
||||
async done(): Promise<T> {
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
async resolve() {
|
||||
(this.res)();
|
||||
async resolve(): Promise<void> {
|
||||
await (this.res)();
|
||||
}
|
||||
|
||||
async reject(e: Error) {
|
||||
(this.rej)(e);
|
||||
async reject(e: Error): Promise<void> {
|
||||
await (this.rej)(e);
|
||||
}
|
||||
}
|
||||
|
||||
type ResultSets = {
|
||||
[name: string]: bqrs.ColumnValue[][]
|
||||
[name: string]: bqrs.ColumnValue[][];
|
||||
}
|
||||
|
||||
type QueryTestCase = {
|
||||
queryPath: string,
|
||||
expectedResultSets: ResultSets
|
||||
queryPath: string;
|
||||
expectedResultSets: ResultSets;
|
||||
}
|
||||
|
||||
// Test cases: queries to run and their expected results.
|
||||
|
@ -105,9 +105,10 @@ describe('using the query server', function() {
|
|||
report: (v: { message: string }) => console.log(`progress reporter says ${v.message}`)
|
||||
};
|
||||
const logger: Logger = {
|
||||
log: (s: string) => console.log('logger says', s),
|
||||
logWithoutTrailingNewline: (s: string) => console.log('logger says', s),
|
||||
show: () => { },
|
||||
log: async (s: string) => console.log('logger says', s),
|
||||
show: () => { /**/ },
|
||||
removeAdditionalLogLocation: async () => { /**/ },
|
||||
getBaseLocation: () => ''
|
||||
};
|
||||
cliServer = new cli.CodeQLCliServer({
|
||||
async getCodeQlPathWithoutVersionCheck(): Promise<string | undefined> {
|
||||
|
@ -161,7 +162,7 @@ describe('using the query server', function() {
|
|||
resultPath: COMPILED_QUERY_PATH,
|
||||
target: { query: {} }
|
||||
};
|
||||
const result = await qs.sendRequest(messages.compileQuery, params, token, () => { });
|
||||
const result = await qs.sendRequest(messages.compileQuery, params, token, () => { /**/ });
|
||||
expect(result.messages!.length).to.equal(0);
|
||||
compilationSucceeded.resolve();
|
||||
}
|
||||
|
@ -194,7 +195,7 @@ describe('using the query server', function() {
|
|||
stopOnError: false,
|
||||
useSequenceHint: false
|
||||
};
|
||||
await qs.sendRequest(messages.runQueries, params, token, () => { });
|
||||
await qs.sendRequest(messages.runQueries, params, token, () => { /**/ });
|
||||
}
|
||||
catch (e) {
|
||||
evaluationSucceeded.reject(e);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{}
|
Загрузка…
Ссылка в новой задаче