@syntest/search
Version:
The common core of the SynTest Framework
164 lines • 7.32 kB
JavaScript
"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.SearchAlgorithm = void 0;
const path = require("node:path");
const logging_1 = require("@syntest/logging");
/**
* Abstract search algorithm to search for an optimal solution within the search space.
*
* The search algorithm is dependent on the encoding of the search space.
*
* @author Mitchell Olsthoorn
*/
class SearchAlgorithm {
/**
* Abstract constructor.
*
* @param eventManager The event manager
* @param objectiveManager The objective manager
* @protected
*/
constructor(objectiveManager) {
SearchAlgorithm.LOGGER = (0, logging_1.getLogger)(SearchAlgorithm.name);
this._population = [];
this._objectiveManager = objectiveManager;
}
/**
* Search the search space for an optimal solution until one of the termination conditions are met.
*
* @param subject The subject of the search
* @param budgetManager The budget manager to track budget progress
* @param terminationManager The termination trigger manager
*/
async search(subject, budgetManager, terminationManager) {
SearchAlgorithm.LOGGER.info("Starting search");
// Load search subject into the objective manager
this._objectiveManager.load(subject);
// Start initialization budget tracking
budgetManager.initializationStarted();
process.emit("searchInitializationStart", this, subject, budgetManager, terminationManager);
// Initialize search process
await this._initialize(budgetManager, terminationManager);
// Stop initialization budget tracking, inform the listeners, and start search budget tracking
budgetManager.initializationStopped();
process.emit("searchInitializationComplete", this, subject, budgetManager, terminationManager);
budgetManager.searchStarted();
process.emit("searchStart", this, subject, budgetManager, terminationManager);
// Start search until the budget has expired, a termination trigger has been triggered, or there are no more objectives
while (this._objectiveManager.hasObjectives() &&
budgetManager.hasBudgetLeft() &&
!terminationManager.isTriggered()) {
process.emit("searchIterationStart", this, subject, budgetManager, terminationManager);
// Start next iteration of the search process
await this._iterate(budgetManager, terminationManager);
// Inform the budget manager and listeners that an iteration happened
budgetManager.iteration(this);
process.emit("searchIterationComplete", this, subject, budgetManager, terminationManager);
}
// Stop search budget tracking
budgetManager.searchStopped();
process.emit("searchComplete", this, subject, budgetManager, terminationManager);
// Finalize the population
this._objectiveManager.finalize(this._population);
// Return the archive of covered objectives
return this._objectiveManager.getArchive();
}
// eslint-disable-next-line sonarjs/cognitive-complexity
calculateObjectivePerformance(objectives) {
const objectivePerformace = new Map();
for (const encoding of this._population) {
if (encoding.getExecutionResult() === undefined) {
continue;
}
for (const objective of objectives) {
const distance = encoding.getDistance(objective);
if (distance === undefined) {
continue;
}
if (objectivePerformace.has(objective)) {
const smallestDistance = objectivePerformace.get(objective);
if (distance < smallestDistance) {
objectivePerformace.set(objective, distance);
}
}
else {
objectivePerformace.set(objective, distance);
}
}
}
return objectivePerformace;
}
/**
* Return the objective manager.
*/
getObjectiveManager() {
return this._objectiveManager;
}
getCovered(objectiveType = "mixed") {
const covered = new Set();
for (const key of this._objectiveManager.getArchive().getObjectives()) {
const test = this._objectiveManager.getArchive().getEncoding(key);
const result = test.getExecutionResult();
// TODO this does not work when there are files with the same name in different directories!!
const fileName = path.basename(key.getSubject().path);
for (const current of result
.getTraces()
.filter((element) => element.type.includes(objectiveType) || objectiveType === "mixed")
.filter((element) => element.path.includes(fileName))) {
if (current.hits > 0)
covered.add(current.id);
}
}
return covered.size;
}
getUncovered(objectiveType = "mixed") {
const total = new Set();
const covered = new Set();
for (const key of this._objectiveManager.getArchive().getObjectives()) {
const test = this._objectiveManager.getArchive().getEncoding(key);
const result = test.getExecutionResult();
// TODO this does not work when there are files with the same name in different directories!!
const fileName = path.basename(key.getSubject().path);
for (const current of result
.getTraces()
.filter((element) => element.type.includes(objectiveType) || objectiveType === "mixed")
.filter((element) => element.path.includes(fileName))) {
total.add(current.id);
if (current.hits > 0)
covered.add(current.id);
}
}
return total.size - covered.size;
}
/**
* The progress of the search process.
*/
progress(objectiveType = "mixed") {
const numberOfCoveredObjectives = this.getCovered(objectiveType);
const numberOfUncoveredObjectives = this.getUncovered(objectiveType);
const progress = (numberOfCoveredObjectives /
(numberOfCoveredObjectives + numberOfUncoveredObjectives)) *
100;
const factor = 10 ** 2;
return Math.round(progress * factor) / factor;
}
}
exports.SearchAlgorithm = SearchAlgorithm;
//# sourceMappingURL=SearchAlgorithm.js.map