UNPKG

@syntest/search

Version:

The common core of the SynTest Framework

157 lines 7.65 kB
"use strict"; /* * Copyright 2020-2021 Delft University of Technology and SynTest contributors * * This file is part of SynTest Framework - SynTest Core. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BranchObjectiveFunction = void 0; const cfg_1 = require("@syntest/cfg"); const logging_1 = require("@syntest/logging"); const diagnostics_1 = require("../util/diagnostics"); const ControlFlowBasedObjectiveFunction_1 = require("./ControlFlowBasedObjectiveFunction"); /** * Objective function for the branch criterion. * * @author Mitchell Olsthoorn * @author Annibale Panichella * @author Dimitri Stallenberg */ class BranchObjectiveFunction extends ControlFlowBasedObjectiveFunction_1.ControlFlowBasedObjectiveFunction { constructor(approachLevel, branchDistance, subject, id) { super(id, subject, approachLevel, branchDistance); BranchObjectiveFunction.LOGGER = (0, logging_1.getLogger)("BranchObjectiveFunction"); } /** * Calculating the distance for a single encoding * * This returns a number structured as follows: XX.YYZZZZ * Where: * - XX is the approach level * - YY is the fraction of uncovered statements within the block * - ZZZZ is the branch distance from the objective * * @param encoding * @returns */ // eslint-disable-next-line sonarjs/cognitive-complexity calculateDistance(encoding) { const executionResult = encoding.getExecutionResult(); if (executionResult === undefined || executionResult.getTraces().length === 0) { return Number.MAX_VALUE; } // check if the branch is covered if (executionResult.coversId(this._id)) { return 0; } else if (this.shallow) { return Number.MAX_VALUE; } else { return this._calculateControlFlowDistance(executionResult); } } // eslint-disable-next-line sonarjs/cognitive-complexity _calculateControlFlowDistance(executionResult) { // find the corresponding node inside the cfg const functions_ = this._subject.cfg.functions.filter((function_) => function_.graph.getNodeById(this._id) !== undefined); if (functions_.length !== 1) { throw new Error((0, diagnostics_1.shouldNeverHappen)("BranchObjectiveFunction")); } const function_ = functions_[0]; const targetNode = function_.graph.getNodeById(this._id); if (!targetNode) { throw new Error((0, diagnostics_1.shouldNeverHappen)("BranchObjectiveFunction")); } // Find approach level and ancestor based on node and covered nodes const { approachLevel, closestCoveredNode, closestCoveredBranchTrace, lastEdgeType, statementFraction, } = this.approachLevel.calculate(function_.graph, targetNode, executionResult.getTraces()); if (closestCoveredNode === undefined) { // if closest node is not found, we return the distance to the root branch // this happens when the function is not entered at all return Number.MAX_VALUE; } const outgoingEdges = function_.graph.getOutgoingEdges(closestCoveredNode.id); if (statementFraction !== -1 && statementFraction !== 1) { // Here we use the fractions of unreached statements to generate a number between 0.01 and 0.99 // It must be larger than 0 otherwise it seems like the node is actually covered // It must be smaller than 1 otherwise it would be the same as one further node in the approach level // This represents the YY part in the distance (XX.YYZZZZ) let distance = (1 - statementFraction) * 0.98 + 0.01; distance = Math.round(distance * 100) / 100; return approachLevel + distance; } if (outgoingEdges.length < 2) { // end of block problem // when a crash happens at the last line of a block the statement fraction becomes 1 since we do not record the last one if (statementFraction === 1) { return approachLevel + 0.01; } throw new Error((0, diagnostics_1.shouldNeverHappen)("Statement fraction should not be zero because that means it rashed on the conditional instead of the first statement of a blok, could be that the traces are wrong")); // return approachLevel + 0.48 * statementFraction + 0.01; } if (outgoingEdges.length > 2) { // weird throw new Error((0, diagnostics_1.moreThanTwoOutgoingEdges)(closestCoveredNode.id, this._id)); } const trueEdge = outgoingEdges.find((edge) => edge.type === cfg_1.EdgeType.CONDITIONAL_TRUE); const falseEdge = outgoingEdges.find((edge) => edge.type === cfg_1.EdgeType.CONDITIONAL_FALSE); if (!trueEdge || !falseEdge) { // weird throw new Error((0, diagnostics_1.shouldNeverHappen)("BranchObjectiveFunction")); } // if closest covered node is not found, we return the distance to the root branch if (!closestCoveredBranchTrace) { throw new Error((0, diagnostics_1.shouldNeverHappen)("BranchObjectiveFunction")); } let trace; if (lastEdgeType) { const trueNode = trueEdge.target; trace = executionResult .getTraces() .find((trace) => trace.id === trueNode && trace.type === "branch"); } else { const falseNode = falseEdge.target; trace = executionResult .getTraces() .find((trace) => trace.id === falseNode && trace.type === "branch"); } if (trace === undefined) { throw new TypeError((0, diagnostics_1.shouldNeverHappen)("ObjectiveManager")); } let branchDistance = this.branchDistance.calculate(trace.condition, trace.variables, lastEdgeType); if (Number.isNaN(approachLevel)) { throw new TypeError((0, diagnostics_1.shouldNeverHappen)("ObjectiveManager")); } if (Number.isNaN(branchDistance)) { throw new TypeError((0, diagnostics_1.shouldNeverHappen)("ObjectiveManager")); } if (Number.isNaN(approachLevel + branchDistance)) { throw new TypeError((0, diagnostics_1.shouldNeverHappen)("ObjectiveManager")); } if (branchDistance === 0) { // TODO there can still be a crash inside of the if statement giving this result BranchObjectiveFunction.LOGGER.warn("branch distance is zero"); branchDistance += 0.999; } // add the distances // We divide the branch distance by 100 to "free up" the YY part of the distance metric // The branch distance represents the ZZZZ part in the distance (XX.YYZZZZ) return approachLevel + branchDistance / 100; } } exports.BranchObjectiveFunction = BranchObjectiveFunction; //# sourceMappingURL=BranchObjectiveFunction.js.map