Skip to content
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

feat: implement path objectives #203

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions libraries/search-javascript/lib/criterion/BranchDistance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ import { transformSync, traverse } from "@babel/core";
import { defaultBabelOptions } from "@syntest/analysis-javascript";
import { getLogger, Logger } from "@syntest/logging";
import {
BranchDistance as FrameworkBranchDistance,
BranchDistanceCalculator as AbstractBranchDistanceCalculator,
shouldNeverHappen,
} from "@syntest/search";

import { BranchDistanceVisitor } from "./BranchDistanceVisitor";

export class BranchDistance extends FrameworkBranchDistance {
export class BranchDistanceCalculator extends AbstractBranchDistanceCalculator {
protected static LOGGER: Logger;
protected syntaxForgiving: boolean;
protected stringAlphabet: string;

constructor(syntaxForgiving: boolean, stringAlphabet: string) {
super();
this.syntaxForgiving = syntaxForgiving;
BranchDistance.LOGGER = getLogger("BranchDistance");
BranchDistanceCalculator.LOGGER = getLogger("BranchDistance");
this.stringAlphabet = stringAlphabet;
}

Expand Down Expand Up @@ -84,7 +84,7 @@ export class BranchDistance extends FrameworkBranchDistance {
const variables_ = Object.entries(variables)
.map(([key, value]) => `${key}=${String(value)}`)
.join(", ");
BranchDistance.LOGGER.warn(
BranchDistanceCalculator.LOGGER.warn(
`Calculated distance for condition '${condition}' -> ${String(
trueOrFalse
)}, is zero. Variables: ${variables_}`
Expand Down
157 changes: 3 additions & 154 deletions libraries/search-javascript/lib/search/JavaScriptSubject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,168 +17,17 @@
*/
import { TargetType } from "@syntest/analysis";
import { RootContext, SubTarget, Target } from "@syntest/analysis-javascript";
import { ControlFlowGraph, Edge, EdgeType } from "@syntest/cfg";
import {
ApproachLevel,
BranchObjectiveFunction,
FunctionObjectiveFunction,
ObjectiveFunction,
SearchSubject,
shouldNeverHappen,
} from "@syntest/search";
import { ObjectiveFunction, SearchSubject } from "@syntest/search";

import { BranchDistance } from "../criterion/BranchDistance";
import { JavaScriptTestCase } from "../testcase/JavaScriptTestCase";

export class JavaScriptSubject extends SearchSubject<JavaScriptTestCase> {
protected syntaxForgiving: boolean;
protected stringAlphabet: string;
constructor(
target: Target,
rootContext: RootContext,
syntaxForgiving: boolean,
stringAlphabet: string
objectives: ObjectiveFunction<JavaScriptTestCase>[]
) {
super(target, rootContext);
this.syntaxForgiving = syntaxForgiving;
this.stringAlphabet = stringAlphabet;

this._extractObjectives();
}

protected _extractObjectives(): void {
this._objectives = new Map<
ObjectiveFunction<JavaScriptTestCase>,
ObjectiveFunction<JavaScriptTestCase>[]
>();

const functions = this._rootContext.getControlFlowProgram(
this._target.path
).functions;

// FUNCTION objectives
for (const function_ of functions) {
const graph = function_.graph;
// Branch objectives
// Find all control nodes
// I.E. nodes that have more than one outgoing edge
const controlNodeIds = [...graph.nodes.keys()].filter(
(node) => graph.getOutgoingEdges(node).length > 1
);

for (const controlNodeId of controlNodeIds) {
const outGoingEdges = graph.getOutgoingEdges(controlNodeId);

for (const edge of outGoingEdges) {
if (["ENTRY", "SUCCESS_EXIT", "ERROR_EXIT"].includes(edge.target)) {
throw new Error(
`Function ${function_.name} in ${function_.id} ends in entry/exit node`
);
}
// Add objective function
this._objectives.set(
new BranchObjectiveFunction(
new ApproachLevel(),
new BranchDistance(this.syntaxForgiving, this.stringAlphabet),
this,
edge.target
),
[]
);
}
}

for (const objective of this._objectives.keys()) {
const childrenObject = this.findChildren(graph, objective);
this._objectives.get(objective).push(...childrenObject);
}

const entry = function_.graph.entry;

const children = function_.graph.getChildren(entry.id);

if (children.length !== 1) {
throw new Error(shouldNeverHappen("JavaScriptSubject")); //, "entry node has more than one child"))
}

// Add objective
const functionObjective = new FunctionObjectiveFunction(
this,
function_.id
);

// find first control node in function
let firstControlNodeInFunction = children[0];
while (
function_.graph.getChildren(firstControlNodeInFunction.id).length === 1
) {
firstControlNodeInFunction = function_.graph.getChildren(
firstControlNodeInFunction.id
)[0];
}

// there are control nodes in the function
if (
function_.graph.getChildren(firstControlNodeInFunction.id).length === 2
) {
const firstObjectives = function_.graph
.getChildren(firstControlNodeInFunction.id)
.map((child) => {
return [...this._objectives.keys()].find(
(objective) => objective.getIdentifier() === child.id
);
});

if (!firstObjectives[0] || !firstObjectives[1]) {
throw new Error(
`Cannot find objective with id: ${firstControlNodeInFunction.id}`
);
}

this._objectives.set(functionObjective, [...firstObjectives]);
} else {
// no control nodes so no sub objectives
this._objectives.set(functionObjective, []);
}
}
}

findChildren(
graph: ControlFlowGraph,
object: ObjectiveFunction<JavaScriptTestCase>
): ObjectiveFunction<JavaScriptTestCase>[] {
let childObjectives: ObjectiveFunction<JavaScriptTestCase>[] = [];

let edges2Visit = [...graph.getOutgoingEdges(object.getIdentifier())];

const visitedEdges: Edge[] = [];

while (edges2Visit.length > 0) {
const edge = edges2Visit.pop();

if (visitedEdges.includes(edge)) {
// this condition is made to avoid infinite loops
continue;
}

if (edge.type === EdgeType.BACK_EDGE) {
continue;
}

visitedEdges.push(edge);

const found = this.getObjectives().filter(
(child) => child.getIdentifier() === edge.target
);
if (found.length === 0) {
const additionalEdges = graph.getOutgoingEdges(edge.target);
edges2Visit = [...edges2Visit, ...additionalEdges];
} else {
childObjectives = [...childObjectives, ...found];
}
}

return childObjectives;
super(target, rootContext, objectives);
}

getActionableTargets(): SubTarget[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
import { expect } from "chai";

import { BranchDistance } from "../../lib/criterion/BranchDistance";
import { BranchDistanceCalculator } from "../../lib/criterion/BranchDistance";

describe("BranchDistance a == b test", () => {
// number
Expand All @@ -26,7 +26,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -41,7 +41,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -54,7 +54,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -67,7 +67,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -82,7 +82,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -97,7 +97,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -111,7 +111,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -124,7 +124,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -139,7 +139,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -154,7 +154,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -168,7 +168,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = true;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand All @@ -181,7 +181,7 @@ describe("BranchDistance a == b test", () => {
const variables = {};
const trueOrFalse = false;

const calculator = new BranchDistance(
const calculator = new BranchDistanceCalculator(
false,
"0123456789abcdefghijklmnopqrstuvxyz"
);
Expand Down
Loading
Loading