Skip to content

Improve performance of data frame absint tests #1490

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 28 additions & 18 deletions test/functionality/abstract-interpretation/data-frame/data-frame.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { assert, test } from 'vitest';
import { assert, beforeAll, test } from 'vitest';
import type { DataFrameDomain } from '../../../../src/abstract-interpretation/data-frame/domain';
import { leqColNames, leqInterval } from '../../../../src/abstract-interpretation/data-frame/domain';
import type { AbstractInterpretationInfo } from '../../../../src/abstract-interpretation/data-frame/absint-info';
import { PipelineExecutor } from '../../../../src/core/pipeline-executor';
import { DEFAULT_DATAFLOW_PIPELINE } from '../../../../src/core/steps/pipeline/default-pipelines';
import type { TREE_SITTER_DATAFLOW_PIPELINE } from '../../../../src/core/steps/pipeline/default-pipelines';
import { createDataflowPipeline, DEFAULT_DATAFLOW_PIPELINE } from '../../../../src/core/steps/pipeline/default-pipelines';
import { RType } from '../../../../src/r-bridge/lang-4.x/ast/model/type';
import { requestFromInput } from '../../../../src/r-bridge/retriever';
import type { RShell } from '../../../../src/r-bridge/shell';
import type { SingleSlicingCriterion, SlicingCriteria } from '../../../../src/slicing/criterion/parse';
import { slicingCriterionToId } from '../../../../src/slicing/criterion/parse';
import { assertUnreachable } from '../../../../src/util/assert';
import { assertUnreachable, guard, isNotUndefined } from '../../../../src/util/assert';
import { getRangeEnd } from '../../../../src/util/range';
import type { RSymbol } from '../../../../src/r-bridge/lang-4.x/ast/model/nodes/r-symbol';
import { resolveDataFrameValue } from '../../../../src/abstract-interpretation/data-frame/abstract-interpretation';
import { decorateLabelContext, type TestLabel } from '../../_helper/label';
import type { ParentInformation } from '../../../../src/r-bridge/lang-4.x/ast/model/processing/decorate';
import type { AbstractInterpretationInfo } from '../../../../src/abstract-interpretation/data-frame/absint-info';
import type { PipelineOutput } from '../../../../src/core/steps/pipeline/pipeline';
import type { KnownParser } from '../../../../src/r-bridge/parser';

export enum DomainMatchingType {
Exact = 'exact',
Expand Down Expand Up @@ -44,13 +47,20 @@ interface CriterionTestEntry {
}

export function assertDataFrameDomain(
shell: RShell,
parser: KnownParser,
code: string,
expected: [SingleSlicingCriterion, DataFrameDomain][],
name: string | TestLabel = code
) {
test.each(expected)( decorateLabelContext(name, ['absint']), async(criterion, expect) => {
const [value] = await getInferredDomainForCriterion(shell, code, criterion);
let result: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE | typeof TREE_SITTER_DATAFLOW_PIPELINE> | undefined;

beforeAll(async() => {
result = await createDataflowPipeline(parser, { request: requestFromInput(code) }).allRemainingSteps();
});

test.each(expected)(decorateLabelContext(name, ['absint']), (criterion, expect) => {
guard(isNotUndefined(result), 'Result cannot be undefined');
const [value] = getInferredDomainForCriterion(result, criterion);

assert.deepStrictEqual(value.colnames, expect.colnames, 'column names differ');
assert.deepStrictEqual(value.cols, expect.cols, 'column count differs');
Expand All @@ -67,11 +77,17 @@ export function testDataFrameDomainAgainstReal(
name: string | TestLabel = code
): void {
const effectiveOptions = { ...DataFrameTestExact, ...options };

test(decorateLabelContext(name, ['absint']), async()=> {
const result = await new PipelineExecutor(DEFAULT_DATAFLOW_PIPELINE, {
parser: shell,
request: requestFromInput(code)
}).allRemainingSteps();

const testEntries: CriterionTestEntry[] = [];

for(const criterion of criteria) {
const [value, node] = await getInferredDomainForCriterion(shell, code, criterion);
const [value, node] = getInferredDomainForCriterion(result, criterion);
const lineNumber = getRangeEnd(node.info.fullRange ?? node.location)?.[0];

if(lineNumber === undefined) {
Expand Down Expand Up @@ -141,16 +157,10 @@ function createCodeForOutput(
}
}

async function getInferredDomainForCriterion(
shell: RShell,
code: string,
function getInferredDomainForCriterion(
result: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>,
criterion: SingleSlicingCriterion
): Promise<[DataFrameDomain, RSymbol<ParentInformation & AbstractInterpretationInfo>]> {
const result = await new PipelineExecutor(DEFAULT_DATAFLOW_PIPELINE, {
parser: shell,
request: requestFromInput(code)
}).allRemainingSteps();

): [DataFrameDomain, RSymbol<ParentInformation & AbstractInterpretationInfo>] {
const idMap = result.dataflow.graph.idMap ?? result.normalize.idMap;
const nodeId = slicingCriterionToId(criterion, idMap);
const node = idMap.get(nodeId);
Expand All @@ -172,7 +182,7 @@ function getRealDomainFromOutput<K extends keyof DataFrameDomain>(
const line = output.find(line => line.startsWith(marker))?.replace(marker, '').trim();

if(line === undefined) {
throw new Error(`cannot parse output of instrumented code for ${type}`);
throw new Error(`cannot parse ${type} output of instrumented code for ${criterion}`);
}
switch(type) {
case 'colnames': {
Expand Down