Merge branch 'main' into robertbrignull/contait push origin robertbrignull/contains_pathns_path
This commit is contained in:
@@ -22,11 +22,11 @@ import {
|
||||
getErrorMessage,
|
||||
getErrorStack,
|
||||
} from "../pure/helpers-pure";
|
||||
import { walkDirectory } from "../pure/files";
|
||||
import { QueryMetadata, SortDirection } from "../pure/interface-types";
|
||||
import { BaseLogger, Logger, ProgressReporter } from "../common";
|
||||
import { CompilationMessage } from "../pure/legacy-messages";
|
||||
import { sarifParser } from "../common/sarif-parser";
|
||||
import { walkDirectory } from "../helpers";
|
||||
import { App } from "../common/app";
|
||||
import { QueryLanguage } from "../common/query-language";
|
||||
|
||||
|
||||
@@ -4,11 +4,6 @@ import { AppEventEmitter } from "./events";
|
||||
import { Logger } from "./logging";
|
||||
import { Memento } from "./memento";
|
||||
import { AppCommandManager } from "./commands";
|
||||
import type {
|
||||
WorkspaceFolder,
|
||||
Event,
|
||||
WorkspaceFoldersChangeEvent,
|
||||
} from "vscode";
|
||||
|
||||
export interface App {
|
||||
createEventEmitter<T>(): AppEventEmitter<T>;
|
||||
@@ -19,8 +14,6 @@ export interface App {
|
||||
readonly globalStoragePath: string;
|
||||
readonly workspaceStoragePath?: string;
|
||||
readonly workspaceState: Memento;
|
||||
readonly workspaceFolders: readonly WorkspaceFolder[] | undefined;
|
||||
readonly onDidChangeWorkspaceFolders: Event<WorkspaceFoldersChangeEvent>;
|
||||
readonly credentials: Credentials;
|
||||
readonly commands: AppCommandManager;
|
||||
readonly environment: EnvironmentContext;
|
||||
|
||||
@@ -35,3 +35,7 @@ export const dbSchemeToLanguage = {
|
||||
"ruby.dbscheme": "ruby",
|
||||
"swift.dbscheme": "swift",
|
||||
};
|
||||
|
||||
export function isQueryLanguage(language: string): language is QueryLanguage {
|
||||
return Object.values(QueryLanguage).includes(language as QueryLanguage);
|
||||
}
|
||||
|
||||
@@ -40,14 +40,6 @@ export class ExtensionApp implements App {
|
||||
return this.extensionContext.workspaceState;
|
||||
}
|
||||
|
||||
public get workspaceFolders(): readonly vscode.WorkspaceFolder[] | undefined {
|
||||
return vscode.workspace.workspaceFolders;
|
||||
}
|
||||
|
||||
public get onDidChangeWorkspaceFolders(): vscode.Event<vscode.WorkspaceFoldersChangeEvent> {
|
||||
return vscode.workspace.onDidChangeWorkspaceFolders;
|
||||
}
|
||||
|
||||
public get subscriptions(): Disposable[] {
|
||||
return this.extensionContext.subscriptions;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import { writeFile } from "fs-extra";
|
||||
import { dump as dumpYaml } from "js-yaml";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
isQueryLanguage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "../helpers";
|
||||
import { TeeLogger } from "../common";
|
||||
import { isQueryLanguage } from "../common/query-language";
|
||||
import { CancellationToken } from "vscode";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { DatabaseItem } from "../databases/local-databases";
|
||||
|
||||
@@ -29,11 +29,13 @@ import {
|
||||
withProgress,
|
||||
} from "../common/vscode/progress";
|
||||
import {
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "../helpers";
|
||||
import {
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
} from "./local-databases/db-contents-heuristics";
|
||||
import { extLogger } from "../common";
|
||||
import {
|
||||
importArchiveDatabase,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
zipArchiveScheme,
|
||||
} from "../../common/vscode/archive-filesystem-provider";
|
||||
import { DatabaseItem, PersistedDatabaseItem } from "./database-item";
|
||||
import { isLikelyDatabaseRoot } from "../../helpers";
|
||||
import { isLikelyDatabaseRoot } from "./db-contents-heuristics";
|
||||
import { stat } from "fs-extra";
|
||||
import { pathsEqual } from "../../pure/files";
|
||||
import { DatabaseContents } from "./database-contents";
|
||||
|
||||
@@ -16,10 +16,10 @@ import { DatabaseItemImpl } from "./database-item-impl";
|
||||
import {
|
||||
getFirstWorkspaceFolder,
|
||||
isFolderAlreadyInWorkspace,
|
||||
isQueryLanguage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showNeverAskAgainDialog,
|
||||
} from "../../helpers";
|
||||
import { isQueryLanguage } from "../../common/query-language";
|
||||
import { existsSync } from "fs";
|
||||
import { QlPackGenerator } from "../../qlpack-generator";
|
||||
import { asError, getErrorMessage } from "../../pure/helpers-pure";
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { pathExists } from "fs-extra";
|
||||
import { basename, join } from "path";
|
||||
import { glob } from "glob";
|
||||
|
||||
/**
|
||||
* The following functions al heuristically determine metadata about databases.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Heuristically determines if the directory passed in corresponds
|
||||
* to a database root. A database root is a directory that contains
|
||||
* a codeql-database.yml or (historically) a .dbinfo file. It also
|
||||
* contains a folder starting with `db-`.
|
||||
*/
|
||||
export async function isLikelyDatabaseRoot(maybeRoot: string) {
|
||||
const [a, b, c] = await Promise.all([
|
||||
// databases can have either .dbinfo or codeql-database.yml.
|
||||
pathExists(join(maybeRoot, ".dbinfo")),
|
||||
pathExists(join(maybeRoot, "codeql-database.yml")),
|
||||
|
||||
// they *must* have a db-{language} folder
|
||||
glob("db-*/", { cwd: maybeRoot }),
|
||||
]);
|
||||
|
||||
return (a || b) && c.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A language folder is any folder starting with `db-` that is itself not a database root.
|
||||
*/
|
||||
export async function isLikelyDbLanguageFolder(dbPath: string) {
|
||||
return (
|
||||
basename(dbPath).startsWith("db-") && !(await isLikelyDatabaseRoot(dbPath))
|
||||
);
|
||||
}
|
||||
130
extensions/ql-vscode/src/databases/qlpack.ts
Normal file
130
extensions/ql-vscode/src/databases/qlpack.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { window } from "vscode";
|
||||
import { glob } from "glob";
|
||||
import { basename } from "path";
|
||||
import { load } from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { getQlPackPath } from "../pure/ql";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli";
|
||||
import { extLogger } from "../common";
|
||||
import { getOnDiskWorkspaceFolders } from "../helpers";
|
||||
|
||||
export interface QlPacksForLanguage {
|
||||
/** The name of the pack containing the dbscheme. */
|
||||
dbschemePack: string;
|
||||
/** `true` if `dbschemePack` is a library pack. */
|
||||
dbschemePackIsLibraryPack: boolean;
|
||||
/**
|
||||
* The name of the corresponding standard query pack.
|
||||
* Only defined if `dbschemePack` is a library pack.
|
||||
*/
|
||||
queryPack?: string;
|
||||
}
|
||||
|
||||
interface QlPackWithPath {
|
||||
packName: string;
|
||||
packDir: string | undefined;
|
||||
}
|
||||
|
||||
async function findDbschemePack(
|
||||
packs: QlPackWithPath[],
|
||||
dbschemePath: string,
|
||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||
for (const { packDir, packName } of packs) {
|
||||
if (packDir !== undefined) {
|
||||
const qlpackPath = await getQlPackPath(packDir);
|
||||
|
||||
if (qlpackPath !== undefined) {
|
||||
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
||||
dbscheme?: string;
|
||||
library?: boolean;
|
||||
};
|
||||
if (
|
||||
qlpack.dbscheme !== undefined &&
|
||||
basename(qlpack.dbscheme) === basename(dbschemePath)
|
||||
) {
|
||||
return {
|
||||
name: packName,
|
||||
isLibraryPack: qlpack.library === true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
|
||||
}
|
||||
|
||||
function findStandardQueryPack(
|
||||
qlpacks: QlpacksInfo,
|
||||
dbschemePackName: string,
|
||||
): string | undefined {
|
||||
const matches = dbschemePackName.match(/^codeql\/(?<language>[a-z]+)-all$/);
|
||||
if (matches) {
|
||||
const queryPackName = `codeql/${matches.groups!.language}-queries`;
|
||||
if (qlpacks[queryPackName] !== undefined) {
|
||||
return queryPackName;
|
||||
}
|
||||
}
|
||||
|
||||
// Either the dbscheme pack didn't look like one where the queries might be in the query pack, or
|
||||
// no query pack was found in the search path. Either is OK.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function getQlPackForDbscheme(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">,
|
||||
dbschemePath: string,
|
||||
): Promise<QlPacksForLanguage> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
|
||||
const packs: QlPackWithPath[] = Object.entries(qlpacks).map(
|
||||
([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`,
|
||||
);
|
||||
return { packName, packDir: undefined };
|
||||
}
|
||||
if (dirs.length > 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
packName,
|
||||
packDir: dirs[0],
|
||||
};
|
||||
},
|
||||
);
|
||||
const dbschemePack = await findDbschemePack(packs, dbschemePath);
|
||||
const queryPack = dbschemePack.isLibraryPack
|
||||
? findStandardQueryPack(qlpacks, dbschemePack.name)
|
||||
: undefined;
|
||||
return {
|
||||
dbschemePack: dbschemePack.name,
|
||||
dbschemePackIsLibraryPack: dbschemePack.isLibraryPack,
|
||||
queryPack,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPrimaryDbscheme(
|
||||
datasetFolder: string,
|
||||
): Promise<string> {
|
||||
const dbschemes = await glob("*.dbscheme", {
|
||||
cwd: datasetFolder,
|
||||
});
|
||||
|
||||
if (dbschemes.length < 1) {
|
||||
throw new Error(
|
||||
`Can't find dbscheme for current database in ${datasetFolder}`,
|
||||
);
|
||||
}
|
||||
|
||||
dbschemes.sort();
|
||||
const dbscheme = dbschemes[0];
|
||||
|
||||
if (dbschemes.length > 1) {
|
||||
void window.showErrorMessage(
|
||||
`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`,
|
||||
);
|
||||
}
|
||||
return dbscheme;
|
||||
}
|
||||
@@ -1,24 +1,14 @@
|
||||
import {
|
||||
ensureDirSync,
|
||||
readFile,
|
||||
pathExists,
|
||||
ensureDir,
|
||||
writeFile,
|
||||
opendir,
|
||||
} from "fs-extra";
|
||||
import { glob } from "glob";
|
||||
import { load } from "js-yaml";
|
||||
import { join, basename, dirname } from "path";
|
||||
import { ensureDirSync, pathExists, ensureDir, writeFile } from "fs-extra";
|
||||
import { join, dirname } from "path";
|
||||
import { dirSync } from "tmp-promise";
|
||||
import { Uri, window as Window, workspace, env, WorkspaceFolder } from "vscode";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "./codeql-cli/cli";
|
||||
import { CodeQLCliServer } from "./codeql-cli/cli";
|
||||
import { UserCancellationException } from "./common/vscode/progress";
|
||||
import { extLogger, OutputChannelLogger } from "./common";
|
||||
import { QueryMetadata } from "./pure/interface-types";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
import { RedactableError } from "./pure/errors";
|
||||
import { getQlPackPath } from "./pure/ql";
|
||||
import { dbSchemeToLanguage, QueryLanguage } from "./common/query-language";
|
||||
import { isQueryLanguage, QueryLanguage } from "./common/query-language";
|
||||
import { isCodespacesTemplate } from "./config";
|
||||
import { AppCommandManager } from "./common/commands";
|
||||
|
||||
@@ -356,202 +346,6 @@ export async function prepareCodeTour(
|
||||
}
|
||||
}
|
||||
|
||||
export interface QlPacksForLanguage {
|
||||
/** The name of the pack containing the dbscheme. */
|
||||
dbschemePack: string;
|
||||
/** `true` if `dbschemePack` is a library pack. */
|
||||
dbschemePackIsLibraryPack: boolean;
|
||||
/**
|
||||
* The name of the corresponding standard query pack.
|
||||
* Only defined if `dbschemePack` is a library pack.
|
||||
*/
|
||||
queryPack?: string;
|
||||
}
|
||||
|
||||
interface QlPackWithPath {
|
||||
packName: string;
|
||||
packDir: string | undefined;
|
||||
}
|
||||
|
||||
async function findDbschemePack(
|
||||
packs: QlPackWithPath[],
|
||||
dbschemePath: string,
|
||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||
for (const { packDir, packName } of packs) {
|
||||
if (packDir !== undefined) {
|
||||
const qlpackPath = await getQlPackPath(packDir);
|
||||
|
||||
if (qlpackPath !== undefined) {
|
||||
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
||||
dbscheme?: string;
|
||||
library?: boolean;
|
||||
};
|
||||
if (
|
||||
qlpack.dbscheme !== undefined &&
|
||||
basename(qlpack.dbscheme) === basename(dbschemePath)
|
||||
) {
|
||||
return {
|
||||
name: packName,
|
||||
isLibraryPack: qlpack.library === true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
|
||||
}
|
||||
|
||||
function findStandardQueryPack(
|
||||
qlpacks: QlpacksInfo,
|
||||
dbschemePackName: string,
|
||||
): string | undefined {
|
||||
const matches = dbschemePackName.match(/^codeql\/(?<language>[a-z]+)-all$/);
|
||||
if (matches) {
|
||||
const queryPackName = `codeql/${matches.groups!.language}-queries`;
|
||||
if (qlpacks[queryPackName] !== undefined) {
|
||||
return queryPackName;
|
||||
}
|
||||
}
|
||||
|
||||
// Either the dbscheme pack didn't look like one where the queries might be in the query pack, or
|
||||
// no query pack was found in the search path. Either is OK.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function getQlPackForDbscheme(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">,
|
||||
dbschemePath: string,
|
||||
): Promise<QlPacksForLanguage> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
|
||||
const packs: QlPackWithPath[] = Object.entries(qlpacks).map(
|
||||
([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`,
|
||||
);
|
||||
return { packName, packDir: undefined };
|
||||
}
|
||||
if (dirs.length > 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
packName,
|
||||
packDir: dirs[0],
|
||||
};
|
||||
},
|
||||
);
|
||||
const dbschemePack = await findDbschemePack(packs, dbschemePath);
|
||||
const queryPack = dbschemePack.isLibraryPack
|
||||
? findStandardQueryPack(qlpacks, dbschemePack.name)
|
||||
: undefined;
|
||||
return {
|
||||
dbschemePack: dbschemePack.name,
|
||||
dbschemePackIsLibraryPack: dbschemePack.isLibraryPack,
|
||||
queryPack,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPrimaryDbscheme(
|
||||
datasetFolder: string,
|
||||
): Promise<string> {
|
||||
const dbschemes = await glob("*.dbscheme", {
|
||||
cwd: datasetFolder,
|
||||
});
|
||||
|
||||
if (dbschemes.length < 1) {
|
||||
throw new Error(
|
||||
`Can't find dbscheme for current database in ${datasetFolder}`,
|
||||
);
|
||||
}
|
||||
|
||||
dbschemes.sort();
|
||||
const dbscheme = dbschemes[0];
|
||||
|
||||
if (dbschemes.length > 1) {
|
||||
void Window.showErrorMessage(
|
||||
`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`,
|
||||
);
|
||||
}
|
||||
return dbscheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following functions al heuristically determine metadata about databases.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Note that this heuristic is only being used for backwards compatibility with
|
||||
* CLI versions before the langauge name was introduced to dbInfo. Features
|
||||
* that do not require backwards compatibility should call
|
||||
* `cli.CodeQLCliServer.resolveDatabase` and use the first entry in the
|
||||
* `languages` property.
|
||||
*
|
||||
* @see cli.CodeQLCliServer.resolveDatabase
|
||||
*/
|
||||
|
||||
export const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce(
|
||||
(acc, [k, v]) => {
|
||||
acc[v] = k;
|
||||
return acc;
|
||||
},
|
||||
{} as { [k: string]: string },
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the initial contents for an empty query, based on the language of the selected
|
||||
* databse.
|
||||
*
|
||||
* First try to use the given language name. If that doesn't exist, try to infer it based on
|
||||
* dbscheme. Otherwise return no import statement.
|
||||
*
|
||||
* @param language the database language or empty string if unknown
|
||||
* @param dbscheme path to the dbscheme file
|
||||
*
|
||||
* @returns an import and empty select statement appropriate for the selected language
|
||||
*/
|
||||
export function getInitialQueryContents(language: string, dbscheme: string) {
|
||||
if (!language) {
|
||||
const dbschemeBase = basename(dbscheme) as keyof typeof dbSchemeToLanguage;
|
||||
language = dbSchemeToLanguage[dbschemeBase];
|
||||
}
|
||||
|
||||
return language ? `import ${language}\n\nselect ""` : 'select ""';
|
||||
}
|
||||
|
||||
/**
|
||||
* Heuristically determines if the directory passed in corresponds
|
||||
* to a database root. A database root is a directory that contains
|
||||
* a codeql-database.yml or (historically) a .dbinfo file. It also
|
||||
* contains a folder starting with `db-`.
|
||||
*/
|
||||
export async function isLikelyDatabaseRoot(maybeRoot: string) {
|
||||
const [a, b, c] = await Promise.all([
|
||||
// databases can have either .dbinfo or codeql-database.yml.
|
||||
pathExists(join(maybeRoot, ".dbinfo")),
|
||||
pathExists(join(maybeRoot, "codeql-database.yml")),
|
||||
|
||||
// they *must* have a db-{language} folder
|
||||
glob("db-*/", { cwd: maybeRoot }),
|
||||
]);
|
||||
|
||||
return (a || b) && c.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A language folder is any folder starting with `db-` that is itself not a database root.
|
||||
*/
|
||||
export async function isLikelyDbLanguageFolder(dbPath: string) {
|
||||
return (
|
||||
basename(dbPath).startsWith("db-") && !(await isLikelyDatabaseRoot(dbPath))
|
||||
);
|
||||
}
|
||||
|
||||
export function isQueryLanguage(language: string): language is QueryLanguage {
|
||||
return Object.values(QueryLanguage).includes(language as QueryLanguage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the language that a query targets.
|
||||
* If it can't be autodetected, prompt the user to specify the language manually.
|
||||
@@ -655,29 +449,6 @@ export async function createTimestampFile(storagePath: string) {
|
||||
await writeFile(timestampPath, Date.now().toString(), "utf8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walk a directory and return the full path to all files found.
|
||||
* Symbolic links are ignored.
|
||||
*
|
||||
* @param dir the directory to walk
|
||||
*
|
||||
* @return An iterator of the full path to all files recursively found in the directory.
|
||||
*/
|
||||
export async function* walkDirectory(
|
||||
dir: string,
|
||||
): AsyncIterableIterator<string> {
|
||||
const seenFiles = new Set<string>();
|
||||
for await (const d of await opendir(dir)) {
|
||||
const entry = join(dir, d.name);
|
||||
seenFiles.add(entry);
|
||||
if (d.isDirectory()) {
|
||||
yield* walkDirectory(entry);
|
||||
} else if (d.isFile()) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the first folder in the workspace.
|
||||
* This is used to decide where to create skeleton QL packs.
|
||||
|
||||
@@ -4,12 +4,14 @@ import { file } from "tmp-promise";
|
||||
import { basename, dirname, resolve } from "path";
|
||||
|
||||
import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
getOnDiskWorkspaceFolders,
|
||||
QlPacksForLanguage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "../../helpers";
|
||||
import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
QlPacksForLanguage,
|
||||
} from "../../databases/qlpack";
|
||||
import {
|
||||
KeyType,
|
||||
kindOfKeyType,
|
||||
|
||||
23
extensions/ql-vscode/src/local-queries/query-contents.ts
Normal file
23
extensions/ql-vscode/src/local-queries/query-contents.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { basename } from "path";
|
||||
import { dbSchemeToLanguage } from "../common/query-language";
|
||||
|
||||
/**
|
||||
* Returns the initial contents for an empty query, based on the language of the selected
|
||||
* databse.
|
||||
*
|
||||
* First try to use the given language name. If that doesn't exist, try to infer it based on
|
||||
* dbscheme. Otherwise return no import statement.
|
||||
*
|
||||
* @param language the database language or empty string if unknown
|
||||
* @param dbscheme path to the dbscheme file
|
||||
*
|
||||
* @returns an import and empty select statement appropriate for the selected language
|
||||
*/
|
||||
export function getInitialQueryContents(language: string, dbscheme: string) {
|
||||
if (!language) {
|
||||
const dbschemeBase = basename(dbscheme) as keyof typeof dbSchemeToLanguage;
|
||||
language = dbSchemeToLanguage[dbschemeBase];
|
||||
}
|
||||
|
||||
return language ? `import ${language}\n\nselect ""` : 'select ""';
|
||||
}
|
||||
@@ -5,12 +5,9 @@ import { CancellationToken, window as Window, workspace, Uri } from "vscode";
|
||||
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { DatabaseUI } from "../databases/local-databases-ui";
|
||||
import {
|
||||
getInitialQueryContents,
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
showBinaryChoiceDialog,
|
||||
} from "../helpers";
|
||||
import { showBinaryChoiceDialog } from "../helpers";
|
||||
import { getInitialQueryContents } from "./query-contents";
|
||||
import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
|
||||
import {
|
||||
ProgressCallback,
|
||||
UserCancellationException,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { pathExists, stat, readdir } from "fs-extra";
|
||||
import { pathExists, stat, readdir, opendir } from "fs-extra";
|
||||
import { isAbsolute, join, relative, resolve } from "path";
|
||||
|
||||
/**
|
||||
@@ -84,3 +84,26 @@ export async function readDirFullPaths(path: string): Promise<string[]> {
|
||||
const baseNames = await readdir(path);
|
||||
return baseNames.map((baseName) => join(path, baseName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walk a directory and return the full path to all files found.
|
||||
* Symbolic links are ignored.
|
||||
*
|
||||
* @param dir the directory to walk
|
||||
*
|
||||
* @return An iterator of the full path to all files recursively found in the directory.
|
||||
*/
|
||||
export async function* walkDirectory(
|
||||
dir: string,
|
||||
): AsyncIterableIterator<string> {
|
||||
const seenFiles = new Set<string>();
|
||||
for await (const d of await opendir(dir)) {
|
||||
const entry = join(dir, d.name);
|
||||
seenFiles.add(entry);
|
||||
if (d.isDirectory()) {
|
||||
yield* walkDirectory(entry);
|
||||
} else if (d.isFile()) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class QueriesModule extends DisposableObject {
|
||||
}
|
||||
void extLogger.log("Initializing queries panel.");
|
||||
|
||||
const queryDiscovery = new QueryDiscovery(app, cliServer);
|
||||
const queryDiscovery = new QueryDiscovery(app.environment, cliServer);
|
||||
this.push(queryDiscovery);
|
||||
void queryDiscovery.refresh();
|
||||
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { dirname, basename, normalize, relative } from "path";
|
||||
import { Discovery } from "../common/discovery";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { Event, RelativePattern, Uri, WorkspaceFolder } from "vscode";
|
||||
import {
|
||||
Event,
|
||||
EventEmitter,
|
||||
RelativePattern,
|
||||
Uri,
|
||||
WorkspaceFolder,
|
||||
workspace,
|
||||
} from "vscode";
|
||||
import { MultiFileSystemWatcher } from "../common/vscode/multi-file-system-watcher";
|
||||
import { App } from "../common/app";
|
||||
import { EnvironmentContext } from "../common/app";
|
||||
import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes";
|
||||
import { getOnDiskWorkspaceFoldersObjects } from "../helpers";
|
||||
import { AppEventEmitter } from "../common/events";
|
||||
@@ -42,13 +49,13 @@ export class QueryDiscovery
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
private readonly env: EnvironmentContext,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
) {
|
||||
super("Query Discovery", extLogger);
|
||||
|
||||
this.onDidChangeQueriesEmitter = this.push(app.createEventEmitter<void>());
|
||||
this.push(app.onDidChangeWorkspaceFolders(this.refresh.bind(this)));
|
||||
this.onDidChangeQueriesEmitter = this.push(new EventEmitter<void>());
|
||||
this.push(workspace.onDidChangeWorkspaceFolders(this.refresh.bind(this)));
|
||||
this.push(this.watcher.onDidChange(this.refresh.bind(this)));
|
||||
}
|
||||
|
||||
@@ -130,7 +137,7 @@ export class QueryDiscovery
|
||||
const rootDirectory = new FileTreeDirectory<string>(
|
||||
fullPath,
|
||||
name,
|
||||
this.app.environment,
|
||||
this.env,
|
||||
);
|
||||
for (const queryPath of resolvedQueries) {
|
||||
const relativePath = normalize(relative(fullPath, queryPath));
|
||||
|
||||
@@ -8,11 +8,6 @@ import { testCredentialsWithStub } from "../factories/authentication";
|
||||
import { Credentials } from "../../src/common/authentication";
|
||||
import { AppCommandManager } from "../../src/common/commands";
|
||||
import { createMockCommandManager } from "./commandsMock";
|
||||
import type {
|
||||
Event,
|
||||
WorkspaceFolder,
|
||||
WorkspaceFoldersChangeEvent,
|
||||
} from "vscode";
|
||||
|
||||
export function createMockApp({
|
||||
extensionPath = "/mock/extension/path",
|
||||
@@ -20,8 +15,6 @@ export function createMockApp({
|
||||
globalStoragePath = "/mock/global/storage/path",
|
||||
createEventEmitter = <T>() => new MockAppEventEmitter<T>(),
|
||||
workspaceState = createMockMemento(),
|
||||
workspaceFolders = [],
|
||||
onDidChangeWorkspaceFolders = jest.fn(),
|
||||
credentials = testCredentialsWithStub(),
|
||||
commands = createMockCommandManager(),
|
||||
environment = createMockEnvironmentContext(),
|
||||
@@ -31,8 +24,6 @@ export function createMockApp({
|
||||
globalStoragePath?: string;
|
||||
createEventEmitter?: <T>() => AppEventEmitter<T>;
|
||||
workspaceState?: Memento;
|
||||
workspaceFolders?: readonly WorkspaceFolder[] | undefined;
|
||||
onDidChangeWorkspaceFolders?: Event<WorkspaceFoldersChangeEvent>;
|
||||
credentials?: Credentials;
|
||||
commands?: AppCommandManager;
|
||||
environment?: EnvironmentContext;
|
||||
@@ -45,8 +36,6 @@ export function createMockApp({
|
||||
workspaceStoragePath,
|
||||
globalStoragePath,
|
||||
workspaceState,
|
||||
workspaceFolders,
|
||||
onDidChangeWorkspaceFolders,
|
||||
createEventEmitter,
|
||||
credentials,
|
||||
commands,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import * as tmp from "tmp";
|
||||
import { join } from "path";
|
||||
import { mkdirSync, writeFileSync } from "fs-extra";
|
||||
import {
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
} from "../../../../src/databases/local-databases/db-contents-heuristics";
|
||||
|
||||
describe("isLikelyDatabaseRoot", () => {
|
||||
let dir: tmp.DirResult;
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
it("should likely be a database root: codeql-database.yml", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely be a database root: .dbinfo", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: empty dir", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: no db language folder", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isLikelyDbLanguageFolder", () => {
|
||||
let dir: tmp.DirResult;
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
it("should find likely db language folder", async () => {
|
||||
const dbFolder = join(dir.name, "db-python");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
// not a db folder since there is a db-python folder inside this one
|
||||
expect(await isLikelyDbLanguageFolder(dbFolder)).toBe(false);
|
||||
|
||||
const nestedDbPythonFolder = join(dbFolder, "db-python");
|
||||
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as tmp from "tmp";
|
||||
import { dump } from "js-yaml";
|
||||
import { writeFileSync } from "fs-extra";
|
||||
import { join } from "path";
|
||||
import { QueryLanguage } from "../../../src/common/query-language";
|
||||
import { getInitialQueryContents } from "../../../src/local-queries/query-contents";
|
||||
|
||||
describe("getInitialQueryContents", () => {
|
||||
let dir: tmp.DirResult;
|
||||
let language: QueryLanguage;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
language = QueryLanguage.Cpp;
|
||||
|
||||
const contents = dump({
|
||||
primaryLanguage: language,
|
||||
});
|
||||
writeFileSync(join(dir.name, "codeql-database.yml"), contents, "utf8");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
it("should get initial query contents when language is known", () => {
|
||||
expect(getInitialQueryContents(language, "hucairz")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when dbscheme is known", () => {
|
||||
expect(getInitialQueryContents("", "semmlecode.cpp.dbscheme")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when nothing is known", () => {
|
||||
expect(getInitialQueryContents("", "hucairz")).toBe('select ""');
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,11 @@ import {
|
||||
getDirectoryNamesInsidePath,
|
||||
pathsEqual,
|
||||
readDirFullPaths,
|
||||
walkDirectory,
|
||||
} from "../../../src/pure/files";
|
||||
import { DirResult } from "tmp";
|
||||
import * as tmp from "tmp";
|
||||
import { ensureDirSync, symlinkSync, writeFileSync } from "fs-extra";
|
||||
|
||||
describe("files", () => {
|
||||
const dataDir = join(__dirname, "../../data");
|
||||
@@ -339,3 +343,67 @@ describe("containsPath", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("walkDirectory", () => {
|
||||
let tmpDir: DirResult;
|
||||
let dir: string;
|
||||
let dir2: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||
dir = join(tmpDir.name, "dir");
|
||||
ensureDirSync(dir);
|
||||
dir2 = join(tmpDir.name, "dir2");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
tmpDir.removeCallback();
|
||||
});
|
||||
|
||||
it("should walk a directory", async () => {
|
||||
const file1 = join(dir, "file1");
|
||||
const file2 = join(dir, "file2");
|
||||
const file3 = join(dir, "file3");
|
||||
const dir3 = join(dir, "dir3");
|
||||
const file4 = join(dir, "file4");
|
||||
const file5 = join(dir, "file5");
|
||||
const file6 = join(dir, "file6");
|
||||
|
||||
// These symlinks link back to paths that are already existing, so ignore.
|
||||
const symLinkFile7 = join(dir, "symlink0");
|
||||
const symlinkDir = join(dir2, "symlink1");
|
||||
|
||||
// some symlinks that point outside of the base dir.
|
||||
const file8 = join(tmpDir.name, "file8");
|
||||
const file9 = join(dir2, "file8");
|
||||
const symlinkDir2 = join(dir2, "symlink2");
|
||||
const symlinkFile2 = join(dir2, "symlinkFile3");
|
||||
|
||||
ensureDirSync(dir2);
|
||||
ensureDirSync(dir3);
|
||||
|
||||
writeFileSync(file1, "file1");
|
||||
writeFileSync(file2, "file2");
|
||||
writeFileSync(file3, "file3");
|
||||
writeFileSync(file4, "file4");
|
||||
writeFileSync(file5, "file5");
|
||||
writeFileSync(file6, "file6");
|
||||
writeFileSync(file8, "file8");
|
||||
writeFileSync(file9, "file9");
|
||||
|
||||
// We don't really need to be testing all of these variants of symlinks,
|
||||
// but it doesn't hurt, and will help us if we ever do decide to support them.
|
||||
symlinkSync(file6, symLinkFile7, "file");
|
||||
symlinkSync(dir3, symlinkDir, "dir");
|
||||
symlinkSync(file8, symlinkFile2, "file");
|
||||
symlinkSync(dir2, symlinkDir2, "dir");
|
||||
|
||||
const files = [];
|
||||
for await (const file of walkDirectory(dir)) {
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
// Only real files should be returned.
|
||||
expect(files.sort()).toEqual([file1, file2, file3, file4, file5, file6]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,15 +7,13 @@ import {
|
||||
QueryInfoByLanguage,
|
||||
} from "../../../src/codeql-cli/cli";
|
||||
import { itWithCodeQL } from "../cli";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
getQlPackForDbscheme,
|
||||
languageToDbScheme,
|
||||
} from "../../../src/helpers";
|
||||
import { getOnDiskWorkspaceFolders } from "../../../src/helpers";
|
||||
import { KeyType, resolveQueries } from "../../../src/language-support";
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { getActivatedExtension } from "../global.helper";
|
||||
import { BaseLogger } from "../../../src/common";
|
||||
import { getQlPackForDbscheme } from "../../../src/databases/qlpack";
|
||||
import { dbSchemeToLanguage } from "../../../src/common/query-language";
|
||||
|
||||
/**
|
||||
* Perform proper integration tests by running the CLI
|
||||
@@ -26,6 +24,14 @@ describe("Use cli", () => {
|
||||
|
||||
let logSpy: jest.SpiedFunction<BaseLogger["log"]>;
|
||||
|
||||
const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce(
|
||||
(acc, [k, v]) => {
|
||||
acc[v] = k;
|
||||
return acc;
|
||||
},
|
||||
{} as { [k: string]: string },
|
||||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
const extension = await getActivatedExtension();
|
||||
cli = extension.cliServer;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "vscode";
|
||||
import { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
||||
import { QueryDiscovery } from "../../../../src/queries-panel/query-discovery";
|
||||
import { createMockApp } from "../../../__mocks__/appMock";
|
||||
import { createMockEnvironmentContext } from "../../../__mocks__/appMock";
|
||||
import { mockedObject } from "../../utils/mocking.helpers";
|
||||
import { basename, join, sep } from "path";
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("QueryDiscovery", () => {
|
||||
resolveQueries,
|
||||
});
|
||||
|
||||
const discovery = new QueryDiscovery(createMockApp({}), cli);
|
||||
const discovery = new QueryDiscovery(createMockEnvironmentContext(), cli);
|
||||
await discovery.refresh();
|
||||
const queries = discovery.queries;
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("QueryDiscovery", () => {
|
||||
]),
|
||||
});
|
||||
|
||||
const discovery = new QueryDiscovery(createMockApp({}), cli);
|
||||
const discovery = new QueryDiscovery(createMockEnvironmentContext(), cli);
|
||||
await discovery.refresh();
|
||||
const queries = discovery.queries;
|
||||
expect(queries).toBeDefined();
|
||||
@@ -69,7 +69,7 @@ describe("QueryDiscovery", () => {
|
||||
]),
|
||||
});
|
||||
|
||||
const discovery = new QueryDiscovery(createMockApp({}), cli);
|
||||
const discovery = new QueryDiscovery(createMockEnvironmentContext(), cli);
|
||||
await discovery.refresh();
|
||||
const queries = discovery.queries;
|
||||
expect(queries).toBeDefined();
|
||||
@@ -114,7 +114,7 @@ describe("QueryDiscovery", () => {
|
||||
resolveQueries,
|
||||
});
|
||||
|
||||
const discovery = new QueryDiscovery(createMockApp({}), cli);
|
||||
const discovery = new QueryDiscovery(createMockEnvironmentContext(), cli);
|
||||
await discovery.refresh();
|
||||
const queries = discovery.queries;
|
||||
expect(queries).toBeDefined();
|
||||
@@ -153,12 +153,7 @@ describe("QueryDiscovery", () => {
|
||||
.mockResolvedValue([join(workspaceRoot, "query1.ql")]),
|
||||
});
|
||||
|
||||
const discovery = new QueryDiscovery(
|
||||
createMockApp({
|
||||
createEventEmitter: () => new EventEmitter(),
|
||||
}),
|
||||
cli,
|
||||
);
|
||||
const discovery = new QueryDiscovery(createMockEnvironmentContext(), cli);
|
||||
|
||||
const onDidChangeQueriesSpy = jest.fn();
|
||||
discovery.onDidChangeQueries(onDidChangeQueriesSpy);
|
||||
@@ -180,12 +175,12 @@ describe("QueryDiscovery", () => {
|
||||
it("should refresh when workspace folders change", async () => {
|
||||
const onDidChangeWorkspaceFoldersEvent =
|
||||
new EventEmitter<WorkspaceFoldersChangeEvent>();
|
||||
jest
|
||||
.spyOn(workspace, "onDidChangeWorkspaceFolders")
|
||||
.mockImplementation(onDidChangeWorkspaceFoldersEvent.event);
|
||||
|
||||
const discovery = new QueryDiscovery(
|
||||
createMockApp({
|
||||
createEventEmitter: () => new EventEmitter(),
|
||||
onDidChangeWorkspaceFolders: onDidChangeWorkspaceFoldersEvent.event,
|
||||
}),
|
||||
createMockEnvironmentContext(),
|
||||
mockedObject<CodeQLCliServer>({
|
||||
resolveQueries: jest.fn().mockResolvedValue([]),
|
||||
}),
|
||||
|
||||
@@ -1,129 +1,22 @@
|
||||
import { Uri, window, workspace, WorkspaceFolder } from "vscode";
|
||||
import { dump } from "js-yaml";
|
||||
import * as tmp from "tmp";
|
||||
import { join } from "path";
|
||||
import {
|
||||
writeFileSync,
|
||||
mkdirSync,
|
||||
ensureDirSync,
|
||||
symlinkSync,
|
||||
writeFile,
|
||||
mkdir,
|
||||
} from "fs-extra";
|
||||
import { DirResult } from "tmp";
|
||||
import { writeFile, mkdir } from "fs-extra";
|
||||
|
||||
import {
|
||||
getFirstWorkspaceFolder,
|
||||
getInitialQueryContents,
|
||||
isFolderAlreadyInWorkspace,
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
prepareCodeTour,
|
||||
showBinaryChoiceDialog,
|
||||
showBinaryChoiceWithUrlDialog,
|
||||
showInformationMessageWithAction,
|
||||
showNeverAskAgainDialog,
|
||||
walkDirectory,
|
||||
} from "../../../src/helpers";
|
||||
import { reportStreamProgress } from "../../../src/common/vscode/progress";
|
||||
import { QueryLanguage } from "../../../src/common/query-language";
|
||||
import { Setting } from "../../../src/config";
|
||||
import { createMockCommandManager } from "../../__mocks__/commandsMock";
|
||||
|
||||
describe("helpers", () => {
|
||||
describe("codeql-database.yml tests", () => {
|
||||
let dir: tmp.DirResult;
|
||||
let language: QueryLanguage;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
language = QueryLanguage.Cpp;
|
||||
|
||||
const contents = dump({
|
||||
primaryLanguage: language,
|
||||
});
|
||||
writeFileSync(join(dir.name, "codeql-database.yml"), contents, "utf8");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
it("should get initial query contents when language is known", () => {
|
||||
expect(getInitialQueryContents(language, "hucairz")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when dbscheme is known", () => {
|
||||
expect(getInitialQueryContents("", "semmlecode.cpp.dbscheme")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when nothing is known", () => {
|
||||
expect(getInitialQueryContents("", "hucairz")).toBe('select ""');
|
||||
});
|
||||
});
|
||||
|
||||
describe("likely database tests", () => {
|
||||
let dir: tmp.DirResult;
|
||||
beforeEach(() => {
|
||||
dir = tmp.dirSync();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dir.removeCallback();
|
||||
});
|
||||
|
||||
it("should likely be a database root: codeql-database.yml", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely be a database root: .dbinfo", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: empty dir", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: no db language folder", async () => {
|
||||
const dbFolder = join(dir.name, "db");
|
||||
mkdirSync(dbFolder);
|
||||
writeFileSync(join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
|
||||
it("should find likely db language folder", async () => {
|
||||
const dbFolder = join(dir.name, "db-python");
|
||||
mkdirSync(dbFolder);
|
||||
mkdirSync(join(dbFolder, "db-python"));
|
||||
writeFileSync(join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
// not a db folder since there is a db-python folder inside this one
|
||||
expect(await isLikelyDbLanguageFolder(dbFolder)).toBe(false);
|
||||
|
||||
const nestedDbPythonFolder = join(dbFolder, "db-python");
|
||||
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should report stream progress", () => {
|
||||
const progressSpy = jest.fn();
|
||||
const mockReadable = {
|
||||
@@ -342,70 +235,6 @@ describe("helpers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("walkDirectory", () => {
|
||||
let tmpDir: DirResult;
|
||||
let dir: string;
|
||||
let dir2: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||
dir = join(tmpDir.name, "dir");
|
||||
ensureDirSync(dir);
|
||||
dir2 = join(tmpDir.name, "dir2");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
tmpDir.removeCallback();
|
||||
});
|
||||
|
||||
it("should walk a directory", async () => {
|
||||
const file1 = join(dir, "file1");
|
||||
const file2 = join(dir, "file2");
|
||||
const file3 = join(dir, "file3");
|
||||
const dir3 = join(dir, "dir3");
|
||||
const file4 = join(dir, "file4");
|
||||
const file5 = join(dir, "file5");
|
||||
const file6 = join(dir, "file6");
|
||||
|
||||
// These symlinks link back to paths that are already existing, so ignore.
|
||||
const symLinkFile7 = join(dir, "symlink0");
|
||||
const symlinkDir = join(dir2, "symlink1");
|
||||
|
||||
// some symlinks that point outside of the base dir.
|
||||
const file8 = join(tmpDir.name, "file8");
|
||||
const file9 = join(dir2, "file8");
|
||||
const symlinkDir2 = join(dir2, "symlink2");
|
||||
const symlinkFile2 = join(dir2, "symlinkFile3");
|
||||
|
||||
ensureDirSync(dir2);
|
||||
ensureDirSync(dir3);
|
||||
|
||||
writeFileSync(file1, "file1");
|
||||
writeFileSync(file2, "file2");
|
||||
writeFileSync(file3, "file3");
|
||||
writeFileSync(file4, "file4");
|
||||
writeFileSync(file5, "file5");
|
||||
writeFileSync(file6, "file6");
|
||||
writeFileSync(file8, "file8");
|
||||
writeFileSync(file9, "file9");
|
||||
|
||||
// We don't really need to be testing all of these variants of symlinks,
|
||||
// but it doesn't hurt, and will help us if we ever do decide to support them.
|
||||
symlinkSync(file6, symLinkFile7, "file");
|
||||
symlinkSync(dir3, symlinkDir, "dir");
|
||||
symlinkSync(file8, symlinkFile2, "file");
|
||||
symlinkSync(dir2, symlinkDir2, "dir");
|
||||
|
||||
const files = [];
|
||||
for await (const file of walkDirectory(dir)) {
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
// Only real files should be returned.
|
||||
expect(files.sort()).toEqual([file1, file2, file3, file4, file5, file6]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isFolderAlreadyInWorkspace", () => {
|
||||
beforeEach(() => {
|
||||
const folders = [
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as fs from "fs-extra";
|
||||
import { getErrorMessage } from "../../../../../src/pure/helpers-pure";
|
||||
|
||||
import * as helpers from "../../../../../src/helpers";
|
||||
import * as qlpack from "../../../../../src/databases/qlpack";
|
||||
import {
|
||||
KeyType,
|
||||
qlpackOfDatabase,
|
||||
@@ -14,10 +15,10 @@ import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers";
|
||||
|
||||
describe("queryResolver", () => {
|
||||
let getQlPackForDbschemeSpy: jest.SpiedFunction<
|
||||
typeof helpers.getQlPackForDbscheme
|
||||
typeof qlpack.getQlPackForDbscheme
|
||||
>;
|
||||
let getPrimaryDbschemeSpy: jest.SpiedFunction<
|
||||
typeof helpers.getPrimaryDbscheme
|
||||
typeof qlpack.getPrimaryDbscheme
|
||||
>;
|
||||
|
||||
const resolveQueriesInSuite = jest.fn();
|
||||
@@ -28,13 +29,13 @@ describe("queryResolver", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
getQlPackForDbschemeSpy = jest
|
||||
.spyOn(helpers, "getQlPackForDbscheme")
|
||||
.spyOn(qlpack, "getQlPackForDbscheme")
|
||||
.mockResolvedValue({
|
||||
dbschemePack: "dbschemePack",
|
||||
dbschemePackIsLibraryPack: false,
|
||||
});
|
||||
getPrimaryDbschemeSpy = jest
|
||||
.spyOn(helpers, "getPrimaryDbscheme")
|
||||
.spyOn(qlpack, "getPrimaryDbscheme")
|
||||
.mockResolvedValue("primaryDbscheme");
|
||||
|
||||
jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]);
|
||||
|
||||
@@ -10,7 +10,8 @@ import { join } from "path";
|
||||
|
||||
import { ExtensionContext, Uri } from "vscode";
|
||||
import { DatabaseManager } from "../../../../src/databases/local-databases";
|
||||
import { tmpDir, walkDirectory } from "../../../../src/helpers";
|
||||
import { tmpDir } from "../../../../src/helpers";
|
||||
import { walkDirectory } from "../../../../src/pure/files";
|
||||
import { DisposableBucket } from "../../disposable-bucket";
|
||||
import { testDisposeHandler } from "../../test-dispose-handler";
|
||||
import { HistoryItemLabelProvider } from "../../../../src/query-history/history-item-label-provider";
|
||||
|
||||
Reference in New Issue
Block a user