Merge branch 'main' into robertbrignull/automodel-sort-order
This commit is contained in:
@@ -5,57 +5,6 @@ import type { AutoModelQueriesResult } from "./auto-model-codeml-queries";
|
|||||||
import { assertNever } from "../common/helpers-pure";
|
import { assertNever } from "../common/helpers-pure";
|
||||||
import type { Log } from "sarif";
|
import type { Log } from "sarif";
|
||||||
import { gzipEncode } from "../common/zlib";
|
import { gzipEncode } from "../common/zlib";
|
||||||
import type { Method, MethodSignature } from "./method";
|
|
||||||
import type { ModeledMethod } from "./modeled-method";
|
|
||||||
import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the candidates that the model should be run on. This includes limiting the number of
|
|
||||||
* candidates to the candidate limit and filtering out anything that is already modeled and respecting
|
|
||||||
* the order in the UI.
|
|
||||||
* @param mode Whether it is application or framework mode.
|
|
||||||
* @param methods all methods.
|
|
||||||
* @param modeledMethodsBySignature the currently modeled methods.
|
|
||||||
* @returns list of modeled methods that are candidates for modeling.
|
|
||||||
*/
|
|
||||||
export function getCandidates(
|
|
||||||
mode: Mode,
|
|
||||||
methods: readonly Method[],
|
|
||||||
modeledMethodsBySignature: Record<string, readonly ModeledMethod[]>,
|
|
||||||
processedByAutoModelMethods: Set<string>,
|
|
||||||
): MethodSignature[] {
|
|
||||||
const candidateMethods = methods.filter((method) => {
|
|
||||||
// Filter out any methods already processed by auto-model
|
|
||||||
if (processedByAutoModelMethods.has(method.signature)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modeledMethods: ModeledMethod[] = [
|
|
||||||
...(modeledMethodsBySignature[method.signature] ?? []),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Anything that is modeled is not a candidate
|
|
||||||
if (modeledMethods.some((m) => m.type !== "none")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A method that is supported is modeled outside of the model file, so it is not a candidate.
|
|
||||||
if (method.supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort the same way as the UI so we send the first ones listed in the UI first
|
|
||||||
const grouped = groupMethods(candidateMethods, mode);
|
|
||||||
const sortedGroupNames = sortGroupNames(grouped);
|
|
||||||
return sortedGroupNames.flatMap((name) =>
|
|
||||||
// We can safely pass empty sets for `modifiedSignatures` and `processedByAutoModelMethods`
|
|
||||||
// because we've filtered out all methods that are already modeled or have already been processed by auto-model.
|
|
||||||
sortMethods(grouped[name], modeledMethodsBySignature, new Set(), new Set()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode a SARIF log to the format expected by the server: JSON, GZIP-compressed, base64-encoded
|
* Encode a SARIF log to the format expected by the server: JSON, GZIP-compressed, base64-encoded
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type { ModeledMethod } from "./modeled-method";
|
|||||||
import { load as loadYaml } from "js-yaml";
|
import { load as loadYaml } from "js-yaml";
|
||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
import type { ProgressCallback } from "../common/vscode/progress";
|
||||||
import { withProgress } from "../common/vscode/progress";
|
import { withProgress } from "../common/vscode/progress";
|
||||||
import { createAutoModelRequest, getCandidates } from "./auto-model";
|
import { createAutoModelRequest } from "./auto-model";
|
||||||
|
import { getCandidates } from "./shared/auto-model-candidates";
|
||||||
import { runAutoModelQueries } from "./auto-model-codeml-queries";
|
import { runAutoModelQueries } from "./auto-model-codeml-queries";
|
||||||
import { loadDataExtensionYaml } from "./yaml";
|
import { loadDataExtensionYaml } from "./yaml";
|
||||||
import type { ModelRequest, ModelResponse } from "./auto-model-api";
|
import type { ModelRequest, ModelResponse } from "./auto-model-api";
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import type { Method, MethodSignature } from "../method";
|
||||||
|
import type { ModeledMethod } from "../modeled-method";
|
||||||
|
import type { Mode } from "./mode";
|
||||||
|
import { groupMethods, sortGroupNames, sortMethods } from "./sorting";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the candidates that the model should be run on. This includes limiting the number of
|
||||||
|
* candidates to the candidate limit and filtering out anything that is already modeled and respecting
|
||||||
|
* the order in the UI.
|
||||||
|
* @param mode Whether it is application or framework mode.
|
||||||
|
* @param methods all methods.
|
||||||
|
* @param modeledMethodsBySignature the currently modeled methods.
|
||||||
|
* @returns list of modeled methods that are candidates for modeling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getCandidates(
|
||||||
|
mode: Mode,
|
||||||
|
methods: readonly Method[],
|
||||||
|
modeledMethodsBySignature: Record<string, readonly ModeledMethod[]>,
|
||||||
|
processedByAutoModelMethods: Set<string>,
|
||||||
|
): MethodSignature[] {
|
||||||
|
const candidateMethods = methods.filter((method) => {
|
||||||
|
// Filter out any methods already processed by auto-model
|
||||||
|
if (processedByAutoModelMethods.has(method.signature)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modeledMethods: ModeledMethod[] = [
|
||||||
|
...(modeledMethodsBySignature[method.signature] ?? []),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Anything that is modeled is not a candidate
|
||||||
|
if (modeledMethods.some((m) => m.type !== "none")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A method that is supported is modeled outside of the model file, so it is not a candidate.
|
||||||
|
if (method.supported) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort the same way as the UI so we send the first ones listed in the UI first
|
||||||
|
const grouped = groupMethods(candidateMethods, mode);
|
||||||
|
const sortedGroupNames = sortGroupNames(grouped);
|
||||||
|
return sortedGroupNames.flatMap((name) =>
|
||||||
|
// We can safely pass empty sets for `modifiedSignatures` and `processedByAutoModelMethods`
|
||||||
|
// because we've filtered out all methods that are already modeled or have already been processed by auto-model.
|
||||||
|
sortMethods(grouped[name], modeledMethodsBySignature, new Set(), new Set()),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { join } from "path";
|
||||||
|
import type { BaseLogger } from "../common/logging";
|
||||||
|
import type { QueryLanguage } from "../common/query-language";
|
||||||
|
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
|
import type { QlPackDetails } from "./ql-pack-details";
|
||||||
|
import { getQlPackFilePath } from "../common/ql";
|
||||||
|
|
||||||
|
export async function resolveCodeScanningQueryPack(
|
||||||
|
logger: BaseLogger,
|
||||||
|
cliServer: CodeQLCliServer,
|
||||||
|
language: QueryLanguage,
|
||||||
|
): Promise<QlPackDetails> {
|
||||||
|
// Get pack
|
||||||
|
void logger.log(`Downloading pack for language: ${language}`);
|
||||||
|
const packName = `codeql/${language}-queries`;
|
||||||
|
const packDownloadResult = await cliServer.packDownload([packName]);
|
||||||
|
const downloadedPack = packDownloadResult.packs[0];
|
||||||
|
|
||||||
|
const packDir = join(
|
||||||
|
packDownloadResult.packDir,
|
||||||
|
downloadedPack.name,
|
||||||
|
downloadedPack.version,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Resolve queries
|
||||||
|
void logger.log(`Resolving queries for pack: ${packName}`);
|
||||||
|
const suitePath = join(
|
||||||
|
packDir,
|
||||||
|
"codeql-suites",
|
||||||
|
`${language}-code-scanning.qls`,
|
||||||
|
);
|
||||||
|
const resolvedQueries = await cliServer.resolveQueries(suitePath);
|
||||||
|
|
||||||
|
const problemQueries = await filterToOnlyProblemQueries(
|
||||||
|
logger,
|
||||||
|
cliServer,
|
||||||
|
resolvedQueries,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (problemQueries.length === 0) {
|
||||||
|
throw Error(
|
||||||
|
`No problem queries found in published query pack: ${packName}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return pack details
|
||||||
|
const qlPackFilePath = await getQlPackFilePath(packDir);
|
||||||
|
|
||||||
|
const qlPackDetails: QlPackDetails = {
|
||||||
|
queryFiles: problemQueries,
|
||||||
|
qlPackRootPath: packDir,
|
||||||
|
qlPackFilePath,
|
||||||
|
language,
|
||||||
|
};
|
||||||
|
|
||||||
|
return qlPackDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function filterToOnlyProblemQueries(
|
||||||
|
logger: BaseLogger,
|
||||||
|
cliServer: CodeQLCliServer,
|
||||||
|
queries: string[],
|
||||||
|
): Promise<string[]> {
|
||||||
|
const problemQueries: string[] = [];
|
||||||
|
for (const query of queries) {
|
||||||
|
const queryMetadata = await cliServer.resolveMetadata(query);
|
||||||
|
if (
|
||||||
|
queryMetadata.kind === "problem" ||
|
||||||
|
queryMetadata.kind === "path-problem"
|
||||||
|
) {
|
||||||
|
problemQueries.push(query);
|
||||||
|
} else {
|
||||||
|
void logger.log(`Skipping non-problem query ${query}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return problemQueries;
|
||||||
|
}
|
||||||
@@ -94,6 +94,7 @@ import { getQlPackFilePath } from "../common/ql";
|
|||||||
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
|
||||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||||
import { findVariantAnalysisQlPackRoot } from "./ql";
|
import { findVariantAnalysisQlPackRoot } from "./ql";
|
||||||
|
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
|
||||||
|
|
||||||
const maxRetryCount = 3;
|
const maxRetryCount = 3;
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ export class VariantAnalysisManager
|
|||||||
public async runVariantAnalysisFromPublishedPack(): Promise<void> {
|
public async runVariantAnalysisFromPublishedPack(): Promise<void> {
|
||||||
return withProgress(async (progress, token) => {
|
return withProgress(async (progress, token) => {
|
||||||
progress({
|
progress({
|
||||||
maxStep: 8,
|
maxStep: 7,
|
||||||
step: 0,
|
step: 0,
|
||||||
message: "Determining query language",
|
message: "Determining query language",
|
||||||
});
|
});
|
||||||
@@ -230,53 +231,17 @@ export class VariantAnalysisManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress({
|
progress({
|
||||||
maxStep: 8,
|
maxStep: 7,
|
||||||
step: 1,
|
|
||||||
message: "Downloading query pack",
|
|
||||||
});
|
|
||||||
|
|
||||||
const packName = `codeql/${language}-queries`;
|
|
||||||
const packDownloadResult = await this.cliServer.packDownload([packName]);
|
|
||||||
const downloadedPack = packDownloadResult.packs[0];
|
|
||||||
|
|
||||||
const packDir = join(
|
|
||||||
packDownloadResult.packDir,
|
|
||||||
downloadedPack.name,
|
|
||||||
downloadedPack.version,
|
|
||||||
);
|
|
||||||
|
|
||||||
progress({
|
|
||||||
maxStep: 8,
|
|
||||||
step: 2,
|
step: 2,
|
||||||
message: "Resolving queries in pack",
|
message: "Downloading query pack and resolving queries",
|
||||||
});
|
});
|
||||||
|
|
||||||
const suitePath = join(
|
|
||||||
packDir,
|
|
||||||
"codeql-suites",
|
|
||||||
`${language}-code-scanning.qls`,
|
|
||||||
);
|
|
||||||
const resolvedQueries = await this.cliServer.resolveQueries(suitePath);
|
|
||||||
|
|
||||||
const problemQueries =
|
|
||||||
await this.filterToOnlyProblemQueries(resolvedQueries);
|
|
||||||
|
|
||||||
if (problemQueries.length === 0) {
|
|
||||||
void this.app.logger.showErrorMessage(
|
|
||||||
`Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const qlPackFilePath = await getQlPackFilePath(packDir);
|
|
||||||
|
|
||||||
// Build up details to pass to the functions that run the variant analysis.
|
// Build up details to pass to the functions that run the variant analysis.
|
||||||
const qlPackDetails: QlPackDetails = {
|
const qlPackDetails = await resolveCodeScanningQueryPack(
|
||||||
queryFiles: problemQueries,
|
this.app.logger,
|
||||||
qlPackRootPath: packDir,
|
this.cliServer,
|
||||||
qlPackFilePath,
|
|
||||||
language,
|
language,
|
||||||
};
|
);
|
||||||
|
|
||||||
await this.runVariantAnalysis(
|
await this.runVariantAnalysis(
|
||||||
qlPackDetails,
|
qlPackDetails,
|
||||||
@@ -291,24 +256,6 @@ export class VariantAnalysisManager
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async filterToOnlyProblemQueries(
|
|
||||||
queries: string[],
|
|
||||||
): Promise<string[]> {
|
|
||||||
const problemQueries: string[] = [];
|
|
||||||
for (const query of queries) {
|
|
||||||
const queryMetadata = await this.cliServer.resolveMetadata(query);
|
|
||||||
if (
|
|
||||||
queryMetadata.kind === "problem" ||
|
|
||||||
queryMetadata.kind === "path-problem"
|
|
||||||
) {
|
|
||||||
problemQueries.push(query);
|
|
||||||
} else {
|
|
||||||
void this.app.logger.log(`Skipping non-problem query ${query}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return problemQueries;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async runVariantAnalysisCommand(queryFiles: Uri[]): Promise<void> {
|
private async runVariantAnalysisCommand(queryFiles: Uri[]): Promise<void> {
|
||||||
if (queryFiles.length === 0) {
|
if (queryFiles.length === 0) {
|
||||||
throw new Error("Please select a .ql file to run as a variant analysis");
|
throw new Error("Please select a .ql file to run as a variant analysis");
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from "@vscode/webview-ui-toolkit/react";
|
} from "@vscode/webview-ui-toolkit/react";
|
||||||
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||||
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
|
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
|
||||||
|
import { getCandidates } from "../../model-editor/shared/auto-model-candidates";
|
||||||
|
|
||||||
const LibraryContainer = styled.div`
|
const LibraryContainer = styled.div`
|
||||||
background-color: var(--vscode-peekViewResult-background);
|
background-color: var(--vscode-peekViewResult-background);
|
||||||
@@ -186,6 +187,17 @@ export const LibraryRow = ({
|
|||||||
return methods.some((method) => inProgressMethods.has(method.signature));
|
return methods.some((method) => inProgressMethods.has(method.signature));
|
||||||
}, [methods, inProgressMethods]);
|
}, [methods, inProgressMethods]);
|
||||||
|
|
||||||
|
const modelWithAIDisabled = useMemo(() => {
|
||||||
|
return (
|
||||||
|
getCandidates(
|
||||||
|
viewState.mode,
|
||||||
|
methods,
|
||||||
|
modeledMethodsMap,
|
||||||
|
processedByAutoModelMethods,
|
||||||
|
).length === 0
|
||||||
|
);
|
||||||
|
}, [methods, modeledMethodsMap, processedByAutoModelMethods, viewState.mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LibraryContainer>
|
<LibraryContainer>
|
||||||
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
|
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
|
||||||
@@ -205,7 +217,11 @@ export const LibraryRow = ({
|
|||||||
{hasUnsavedChanges ? <VSCodeTag>UNSAVED</VSCodeTag> : null}
|
{hasUnsavedChanges ? <VSCodeTag>UNSAVED</VSCodeTag> : null}
|
||||||
</NameContainer>
|
</NameContainer>
|
||||||
{viewState.showLlmButton && !canStopAutoModeling && (
|
{viewState.showLlmButton && !canStopAutoModeling && (
|
||||||
<VSCodeButton appearance="icon" onClick={handleModelWithAI}>
|
<VSCodeButton
|
||||||
|
appearance="icon"
|
||||||
|
disabled={modelWithAIDisabled}
|
||||||
|
onClick={handleModelWithAI}
|
||||||
|
>
|
||||||
<Codicon name="lightbulb-autofix" label="Model with AI" />
|
<Codicon name="lightbulb-autofix" label="Model with AI" />
|
||||||
Model with AI
|
Model with AI
|
||||||
</VSCodeButton>
|
</VSCodeButton>
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
createAutoModelRequest,
|
createAutoModelRequest,
|
||||||
encodeSarif,
|
encodeSarif,
|
||||||
getCandidates,
|
|
||||||
} from "../../../src/model-editor/auto-model";
|
} from "../../../src/model-editor/auto-model";
|
||||||
import { Mode } from "../../../src/model-editor/shared/mode";
|
import { Mode } from "../../../src/model-editor/shared/mode";
|
||||||
import { AutomodelMode } from "../../../src/model-editor/auto-model-api";
|
import { AutomodelMode } from "../../../src/model-editor/auto-model-api";
|
||||||
import type { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries";
|
import type { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries";
|
||||||
import type { Log } from "sarif";
|
import type { Log } from "sarif";
|
||||||
import { gzipDecode } from "../../../src/common/zlib";
|
import { gzipDecode } from "../../../src/common/zlib";
|
||||||
import type { Method } from "../../../src/model-editor/method";
|
|
||||||
import { EndpointType } from "../../../src/model-editor/method";
|
|
||||||
import type { ModeledMethod } from "../../../src/model-editor/modeled-method";
|
|
||||||
|
|
||||||
describe("createAutoModelRequest", () => {
|
describe("createAutoModelRequest", () => {
|
||||||
const createSarifLog = (queryId: string): Log => {
|
const createSarifLog = (queryId: string): Log => {
|
||||||
@@ -84,118 +80,3 @@ describe("createAutoModelRequest", () => {
|
|||||||
expect(parsed).toEqual(result.candidates);
|
expect(parsed).toEqual(result.candidates);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getCandidates", () => {
|
|
||||||
it("doesn't return methods that are already modelled", () => {
|
|
||||||
const methods: Method[] = [
|
|
||||||
{
|
|
||||||
library: "my.jar",
|
|
||||||
signature: "org.my.A#x()",
|
|
||||||
endpointType: EndpointType.Method,
|
|
||||||
packageName: "org.my",
|
|
||||||
typeName: "A",
|
|
||||||
methodName: "x",
|
|
||||||
methodParameters: "()",
|
|
||||||
supported: false,
|
|
||||||
supportedType: "none",
|
|
||||||
usages: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const modeledMethods: Record<string, ModeledMethod[]> = {
|
|
||||||
"org.my.A#x()": [
|
|
||||||
{
|
|
||||||
type: "neutral",
|
|
||||||
kind: "sink",
|
|
||||||
provenance: "manual",
|
|
||||||
signature: "org.my.A#x()",
|
|
||||||
endpointType: EndpointType.Method,
|
|
||||||
packageName: "org.my",
|
|
||||||
typeName: "A",
|
|
||||||
methodName: "x",
|
|
||||||
methodParameters: "()",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const candidates = getCandidates(
|
|
||||||
Mode.Application,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
new Set(),
|
|
||||||
);
|
|
||||||
expect(candidates.length).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't return methods that are supported from other sources", () => {
|
|
||||||
const methods: Method[] = [
|
|
||||||
{
|
|
||||||
library: "my.jar",
|
|
||||||
signature: "org.my.A#x()",
|
|
||||||
endpointType: EndpointType.Method,
|
|
||||||
packageName: "org.my",
|
|
||||||
typeName: "A",
|
|
||||||
methodName: "x",
|
|
||||||
methodParameters: "()",
|
|
||||||
supported: true,
|
|
||||||
supportedType: "none",
|
|
||||||
usages: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const modeledMethods = {};
|
|
||||||
const candidates = getCandidates(
|
|
||||||
Mode.Application,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
new Set(),
|
|
||||||
);
|
|
||||||
expect(candidates.length).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't return methods that are already processed by auto model", () => {
|
|
||||||
const methods: Method[] = [
|
|
||||||
{
|
|
||||||
library: "my.jar",
|
|
||||||
signature: "org.my.A#x()",
|
|
||||||
endpointType: EndpointType.Method,
|
|
||||||
packageName: "org.my",
|
|
||||||
typeName: "A",
|
|
||||||
methodName: "x",
|
|
||||||
methodParameters: "()",
|
|
||||||
supported: false,
|
|
||||||
supportedType: "none",
|
|
||||||
usages: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const modeledMethods = {};
|
|
||||||
const candidates = getCandidates(
|
|
||||||
Mode.Application,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
new Set(["org.my.A#x()"]),
|
|
||||||
);
|
|
||||||
expect(candidates.length).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns methods that are neither modeled nor supported from other sources", () => {
|
|
||||||
const methods: Method[] = [];
|
|
||||||
methods.push({
|
|
||||||
library: "my.jar",
|
|
||||||
signature: "org.my.A#x()",
|
|
||||||
endpointType: EndpointType.Method,
|
|
||||||
packageName: "org.my",
|
|
||||||
typeName: "A",
|
|
||||||
methodName: "x",
|
|
||||||
methodParameters: "()",
|
|
||||||
supported: false,
|
|
||||||
supportedType: "none",
|
|
||||||
usages: [],
|
|
||||||
});
|
|
||||||
const modeledMethods = {};
|
|
||||||
const candidates = getCandidates(
|
|
||||||
Mode.Application,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
new Set(),
|
|
||||||
);
|
|
||||||
expect(candidates.length).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import type { Method } from "../../../../src/model-editor/method";
|
||||||
|
import { EndpointType } from "../../../../src/model-editor/method";
|
||||||
|
import type { ModeledMethod } from "../../../../src/model-editor/modeled-method";
|
||||||
|
import { getCandidates } from "../../../../src/model-editor/shared/auto-model-candidates";
|
||||||
|
import { Mode } from "../../../../src/model-editor/shared/mode";
|
||||||
|
|
||||||
|
describe("getCandidates", () => {
|
||||||
|
it("doesn't return methods that are already modelled", () => {
|
||||||
|
const methods: Method[] = [
|
||||||
|
{
|
||||||
|
library: "my.jar",
|
||||||
|
signature: "org.my.A#x()",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
packageName: "org.my",
|
||||||
|
typeName: "A",
|
||||||
|
methodName: "x",
|
||||||
|
methodParameters: "()",
|
||||||
|
supported: false,
|
||||||
|
supportedType: "none",
|
||||||
|
usages: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const modeledMethods: Record<string, ModeledMethod[]> = {
|
||||||
|
"org.my.A#x()": [
|
||||||
|
{
|
||||||
|
type: "neutral",
|
||||||
|
kind: "sink",
|
||||||
|
provenance: "manual",
|
||||||
|
signature: "org.my.A#x()",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
packageName: "org.my",
|
||||||
|
typeName: "A",
|
||||||
|
methodName: "x",
|
||||||
|
methodParameters: "()",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const candidates = getCandidates(
|
||||||
|
Mode.Application,
|
||||||
|
methods,
|
||||||
|
modeledMethods,
|
||||||
|
new Set(),
|
||||||
|
);
|
||||||
|
expect(candidates.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't return methods that are supported from other sources", () => {
|
||||||
|
const methods: Method[] = [
|
||||||
|
{
|
||||||
|
library: "my.jar",
|
||||||
|
signature: "org.my.A#x()",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
packageName: "org.my",
|
||||||
|
typeName: "A",
|
||||||
|
methodName: "x",
|
||||||
|
methodParameters: "()",
|
||||||
|
supported: true,
|
||||||
|
supportedType: "none",
|
||||||
|
usages: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const modeledMethods = {};
|
||||||
|
const candidates = getCandidates(
|
||||||
|
Mode.Application,
|
||||||
|
methods,
|
||||||
|
modeledMethods,
|
||||||
|
new Set(),
|
||||||
|
);
|
||||||
|
expect(candidates.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't return methods that are already processed by auto model", () => {
|
||||||
|
const methods: Method[] = [
|
||||||
|
{
|
||||||
|
library: "my.jar",
|
||||||
|
signature: "org.my.A#x()",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
packageName: "org.my",
|
||||||
|
typeName: "A",
|
||||||
|
methodName: "x",
|
||||||
|
methodParameters: "()",
|
||||||
|
supported: false,
|
||||||
|
supportedType: "none",
|
||||||
|
usages: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const modeledMethods = {};
|
||||||
|
const candidates = getCandidates(
|
||||||
|
Mode.Application,
|
||||||
|
methods,
|
||||||
|
modeledMethods,
|
||||||
|
new Set(["org.my.A#x()"]),
|
||||||
|
);
|
||||||
|
expect(candidates.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns methods that are neither modeled nor supported from other sources", () => {
|
||||||
|
const methods: Method[] = [];
|
||||||
|
methods.push({
|
||||||
|
library: "my.jar",
|
||||||
|
signature: "org.my.A#x()",
|
||||||
|
endpointType: EndpointType.Method,
|
||||||
|
packageName: "org.my",
|
||||||
|
typeName: "A",
|
||||||
|
methodName: "x",
|
||||||
|
methodParameters: "()",
|
||||||
|
supported: false,
|
||||||
|
supportedType: "none",
|
||||||
|
usages: [],
|
||||||
|
});
|
||||||
|
const modeledMethods = {};
|
||||||
|
const candidates = getCandidates(
|
||||||
|
Mode.Application,
|
||||||
|
methods,
|
||||||
|
modeledMethods,
|
||||||
|
new Set(),
|
||||||
|
);
|
||||||
|
expect(candidates.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
|
||||||
|
import type { App } from "../../../../src/common/app";
|
||||||
|
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||||
|
import { ExtensionApp } from "../../../../src/common/vscode/vscode-app";
|
||||||
|
import { resolveCodeScanningQueryPack } from "../../../../src/variant-analysis/code-scanning-pack";
|
||||||
|
import { getActivatedExtension } from "../../global.helper";
|
||||||
|
|
||||||
|
describe("Code Scanning pack", () => {
|
||||||
|
let cli: CodeQLCliServer;
|
||||||
|
let app: App;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const extension = await getActivatedExtension();
|
||||||
|
cli = extension.cliServer;
|
||||||
|
app = new ExtensionApp(extension.ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download pack for correct language and identify problem queries", async () => {
|
||||||
|
const pack = await resolveCodeScanningQueryPack(
|
||||||
|
app.logger,
|
||||||
|
cli,
|
||||||
|
QueryLanguage.Javascript,
|
||||||
|
);
|
||||||
|
// Should include queries. Just check that at least one known query exists.
|
||||||
|
// It doesn't particularly matter which query we check for.
|
||||||
|
expect(
|
||||||
|
pack.queryFiles.some((q) => q.includes("PostMessageStar.ql")),
|
||||||
|
).toBeTruthy();
|
||||||
|
// Should not include non-problem queries.
|
||||||
|
expect(
|
||||||
|
pack.queryFiles.some((q) => q.includes("LinesOfCode.ql")),
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -477,41 +477,4 @@ describe("Variant Analysis Manager", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("runVariantAnalysisFromPublishedPack", () => {
|
|
||||||
// Temporarily disabling this until we add a way to receive multiple queries in the
|
|
||||||
// runVariantAnalysis function.
|
|
||||||
it("should download pack for correct language and identify problem queries", async () => {
|
|
||||||
const showQuickPickSpy = jest
|
|
||||||
.spyOn(window, "showQuickPick")
|
|
||||||
.mockResolvedValue(
|
|
||||||
mockedQuickPickItem({
|
|
||||||
label: "JavaScript",
|
|
||||||
description: "javascript",
|
|
||||||
language: "javascript",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const runVariantAnalysisMock = jest.fn();
|
|
||||||
variantAnalysisManager.runVariantAnalysis = runVariantAnalysisMock;
|
|
||||||
|
|
||||||
await variantAnalysisManager.runVariantAnalysisFromPublishedPack();
|
|
||||||
|
|
||||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(runVariantAnalysisMock).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
console.log(runVariantAnalysisMock.mock.calls[0][0]);
|
|
||||||
const queries: string[] =
|
|
||||||
runVariantAnalysisMock.mock.calls[0][0].queryFiles;
|
|
||||||
// Should include queries. Just check that at least one known query exists.
|
|
||||||
// It doesn't particularly matter which query we check for.
|
|
||||||
expect(
|
|
||||||
queries.find((q) => q.includes("PostMessageStar.ql")),
|
|
||||||
).toBeDefined();
|
|
||||||
// Should not include non-problem queries.
|
|
||||||
expect(
|
|
||||||
queries.find((q) => q.includes("LinesOfCode.ql")),
|
|
||||||
).not.toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user