diff --git a/.gitignore b/.gitignore index 08124dc..b7dfe09 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,7 @@ __pycache__ */latex/*.out */latex/*.synctex.gz -/src/test/resources/variantgeneration/gen/ \ No newline at end of file +/src/test/resources/variantgeneration/gen/ +/log.txt +.classpath +.project diff --git a/LICENSE.LGPL3 b/LICENSE.LGPL3 new file mode 100644 index 0000000..153d416 --- /dev/null +++ b/LICENSE.LGPL3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/README.md b/README.md index 4cd5317..ff9e13f 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,19 @@ VEVOS is a tool suite for the simulation of the evolution of clone-and-own projects and consists of two main components: The ground truth extraction, called VEVOS/Extraction and the variant simulation called VEVOS/Simulation. This repository contains VEVOS/Simulation and thus the second part of the replication package for the paper _Simulating the Evolution of Clone-and-Own Projects with VEVOS_ published at the International Conference on Evaluation and Assessment in Software Engineering (EASE) 2022 ([doi](https://doi.org/10.1145/3530019.3534084)). -VEVOS/Simulation is a java library for generating variants with ground truth from an input software product line and dataset extracted with VEVOS/Extraction. +VEVOS/Simulation is a java library for generating variants with ground truth from an input software product line and dataset extracted with [VEVOS/Extraction](https://github.com/VariantSync/VEVOS_Extraction). ![Simulation Overview](docs/generation.png) +## Version 2.0.0 Update +With the latest version, VEVOS - Variant Simulation ensures compatability with ground truths extracted by VEVOS Extraction v2.0.0. It can therefore no longer be used for ground truths extracted with older VEVOS Extraction versions. + +There were no major interface changes besides the compatability with the new ground truth format. + ## Example Usage and Main Features VEVOS/Simulation is supposed to be used by your research prototype on clone-and-own or variability in software systems. -In the following we give a step by step example in how the library can be used to +In the following we give a step-by-step example in how the library can be used to - parse the ground truth dataset extracted by VEVOS/Extraction, - traverse the datasets' evolution history, - sample variants randomly, or use a predefined set of variants for simulation, @@ -74,6 +79,7 @@ In particular, the `VariabilityDataset` provides: - _success commits_ for which the extraction of feature mappings and feature model succeeded, - _partial success_ commits for which part of the extraction failed; Usually, a partial success commit has feature mappings but no file presence condition and no feature model, - _error commits_ for which the extraction failed. +- _empty commits_ for which there were no files for which an extraction was performed To generate variants, we have to specify which variants should be generated. Therefore, a `Sampler` is used that returns the set of variants to use for a certain feature model. @@ -112,6 +118,8 @@ final SPLRepository splRepository = new SPLRepository(splRepositoryPath.path()); /// for Busybox: final SPLRepository splRepository = new BusyboxRepository(splRepositoryPath.path()); ``` + +> VEVOS Extraction 1.x.x: Note that Busybox has a special subclass called `BusyboxRepository` that performs some necessary pre- and postprocessing on the product line's source code. We are now ready to traverse the evolution history to generate variants: diff --git a/pom.xml b/pom.xml index b447096..051f825 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.variantsync.vevos simulation - 1.1.2 + 2.0.0 @@ -105,7 +105,7 @@ org.eclipse.jgit org.eclipse.jgit - 6.2.0.202206071550-r + 6.6.0.202305301015-r @@ -120,5 +120,10 @@ zip4j 2.11.3 + + org.tinylog + tinylog-impl + 2.6.1 + - \ No newline at end of file + diff --git a/src/main/java/org/variantsync/vevos/simulation/VEVOS.java b/src/main/java/org/variantsync/vevos/simulation/VEVOS.java index 5b8862b..b576412 100644 --- a/src/main/java/org/variantsync/vevos/simulation/VEVOS.java +++ b/src/main/java/org/variantsync/vevos/simulation/VEVOS.java @@ -4,7 +4,7 @@ import de.ovgu.featureide.fm.core.configuration.*; import de.ovgu.featureide.fm.core.io.sxfm.SXFMFormat; import de.ovgu.featureide.fm.core.io.xml.XmlFeatureModelFormat; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; public final class VEVOS { private static boolean initialized = false; @@ -31,7 +31,6 @@ private static void InitFeatureIDE() { public static void Initialize() { if (!initialized) { - Logger.initConsoleLogger(); InitFeatureIDE(); initialized = true; Logger.debug("Finished initialization"); diff --git a/src/main/java/org/variantsync/vevos/simulation/examples/GenerationExample.java b/src/main/java/org/variantsync/vevos/simulation/examples/GenerationExample.java index 62564c8..5f74027 100644 --- a/src/main/java/org/variantsync/vevos/simulation/examples/GenerationExample.java +++ b/src/main/java/org/variantsync/vevos/simulation/examples/GenerationExample.java @@ -15,7 +15,7 @@ import org.variantsync.vevos.simulation.io.Resources; import org.variantsync.vevos.simulation.repository.BusyboxRepository; import org.variantsync.vevos.simulation.repository.SPLRepository; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.variability.EvolutionStep; import org.variantsync.vevos.simulation.variability.SPLCommit; @@ -39,27 +39,38 @@ public class GenerationExample { public static void main(final String[] args) throws Resources.ResourceIOException { VEVOS.Initialize(); - final CaseSensitivePath splRepositoryPath = CaseSensitivePath.of("path", "to", "SPL", "git", "repository"); - final CaseSensitivePath groundTruthDatasetPath = CaseSensitivePath.of("path", "to", "datasets"); - final CaseSensitivePath variantsGenerationDir = CaseSensitivePath.of("directory", "to", "put", "generated", "variants"); + final CaseSensitivePath splRepositoryPath = + CaseSensitivePath.of("path", "to", "SPL", "git", "repository"); + final CaseSensitivePath groundTruthDatasetPath = + CaseSensitivePath.of("path", "to", "datasets"); + final CaseSensitivePath variantsGenerationDir = + CaseSensitivePath.of("directory", "to", "put", "generated", "variants"); /// Load the ground truth dataset extracted with the variability extraction of VEVOS - final VariabilityDataset dataset = - Resources.Instance().load(VariabilityDataset.class, groundTruthDatasetPath.path()); + final VariabilityDataset dataset = Resources.Instance().load(VariabilityDataset.class, + groundTruthDatasetPath.path()); /// Inspect all evolution steps (i.e., commits where the extraction succeeded). final Set> evolutionSteps = dataset.getEvolutionSteps(); - Logger.info("The dataset contains " + dataset.getSuccessCommits().size() + " commits for which the variability extraction succeeded."); - Logger.info("The dataset contains " + dataset.getErrorCommits().size() + " commits for which the variability extraction failed."); - Logger.info("The dataset contains " + dataset.getPartialSuccessCommits().size() + " commits that for which the file presence conditions are missing."); + Logger.info("The dataset contains " + dataset.getSuccessCommits().size() + + " commits for which the variability extraction succeeded."); + Logger.info("The dataset contains " + dataset.getErrorCommits().size() + + " commits for which the variability extraction failed."); + Logger.info("The dataset contains " + dataset.getEmptyCommits().size() + + " commits for which there is no ground truth."); + Logger.info("The dataset contains " + dataset.getPartialSuccessCommits().size() + + " commits that for which the file presence conditions are missing."); Logger.info("The dataset contains " + evolutionSteps.size() + " usable pairs of commits."); /// Organize all evolution steps into a history for the clone-and-own project. - final VariabilityHistory history = dataset.getVariabilityHistory(new LongestNonOverlappingSequences()); + final VariabilityHistory history = + dataset.getVariabilityHistory(new LongestNonOverlappingSequences()); /// This yields a list of continuous sub-histories. - /// The history is divided into sub-histories because for some commits in the SPL, the commit extraction might have failed. - /// If the extraction fails for a commit c, then we have to exclude c from the variant generation. + /// The history is divided into sub-histories because for some commits in the SPL, the + /// commit extraction might have failed. + /// If the extraction fails for a commit c, then we have to exclude c from the variant + /// generation. /// This cuts the evolution history into two pieces. /// Thus, we divide the history into sub-histories at each failed commit. final NonEmptyList> sequencesInHistory = history.commitSequences(); @@ -69,12 +80,14 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio } Logger.info(""); - /// In case the sequence extraction fails, you might either use the evolutionSteps directly or + /// In case the sequence extraction fails, you might either use the evolutionSteps directly + /// or /// iterate over all commits in isolation: -// final List successCommits = dataset.getSuccessCommits(); + // final List successCommits = dataset.getSuccessCommits(); /// Now lets use the variant generator to generate variants. - /// First, create a sampler that determines which variants to generate at each evolution step. + /// First, create a sampler that determines which variants to generate at each evolution + /// step. Sampler variantsSampler; { /// Either random sample the set of variants at each evolution step, @@ -84,25 +97,22 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio { /// Or use a predefined set of variants for each evolution step. final Sample variantsToGenerate = new Sample(List.of( - new Variant("Bernard", new SimpleConfiguration(List.of( - /// Features selected in variant Bernhard. - "A", "B", "D", "E", "N", "R" - ))), - new Variant("Bianca", new SimpleConfiguration(List.of( - /// Features selected in variant Bianca. - "A", "B", "C", "I", "N" - ))) - )); + new Variant("Bernard", new SimpleConfiguration(List.of( + /// Features selected in variant Bernhard. + "A", "B", "D", "E", "N", "R"))), + new Variant("Bianca", new SimpleConfiguration(List.of( + /// Features selected in variant Bianca. + "A", "B", "C", "I", "N"))))); variantsSampler = new ConstSampler(variantsToGenerate); } /// Access the input software-product line's repository. /// In case you use Busybox as input SPL, use the BusyboxRepository class. - /// Busybox requires special pre- and postprocessing steps upon commit checkout, that are handled by the + /// Busybox requires special pre- and postprocessing steps upon commit checkout, that are + /// handled by the /// BusyboxRepository class. - final SPLRepository splRepository = - new SPLRepository(splRepositoryPath.path()); -// new BusyboxRepository(splRepositoryPath.path()); + final SPLRepository splRepository = new SPLRepository(splRepositoryPath.path()); + // new BusyboxRepository(splRepositoryPath.path()); /// For the entire history @@ -110,62 +120,76 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio for (final SPLCommit splCommit : subhistory) { /// The spl commit holds the ground truth data of the SPL at the given commit: final Lazy> loadFeatureModel = splCommit.featureModel(); - final Lazy> loadPresenceConditions = splCommit.presenceConditions(); + final Lazy> loadPresenceConditions = + splCommit.presenceConditionsFallback(); - /// To generate the variants, we have to load the presence conditions from the ground truth dataset. + /// To generate the variants, we have to load the presence conditions from the + /// ground truth dataset. /// Invoking run does exactly that. /// We use Lazy, to defer the loading of data until it is required such that /// no unwanted data is loaded. - /// As the extraction of presence condition might have failed, we get an Optional. + /// As the extraction of presence condition might have failed, we get an + /// Optional. /// Let's assume the extraction succeeded by just invoking orElseThrow here. final Artefact pcs = loadPresenceConditions.run().orElseThrow(); final IFeatureModel featureModel = loadFeatureModel.run().orElseThrow(); - /// Let's sample the set of variants we want to generate for the feature model at this commit. - /// In case the variantsSampler is actually a ConstSampler, it will ignore the feature model + /// Let's sample the set of variants we want to generate for the feature model at + /// this commit. + /// In case the variantsSampler is actually a ConstSampler, it will ignore the + /// feature model /// and will just always return the same set of variants to generate. final Sample variants = variantsSampler.sample(featureModel); /// Optionally, we might want to filter which files of a variant to generate. - /// For example, a study could be interested only in generating the changed files in a commit. + /// For example, a study could be interested only in generating the changed files in + /// a commit. /// In our case, let's just generate all variants. - /// Moreover, VariantGenerationOptions allow to configure some parameters for the variant generation. - //Here, we just instruct the generation to exit in case an error happens but we could for example also instruct it to ignore errors and proceed. + /// Moreover, VariantGenerationOptions allow to configure some parameters for the + /// variant generation. + // Here, we just instruct the generation to exit in case an error happens but we + /// could for example also instruct it to ignore errors and proceed. final ArtefactFilter artefactFilter = ArtefactFilter.KeepAll(); - final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(false, artefactFilter); + final VariantGenerationOptions generationOptions = + VariantGenerationOptions.ExitOnError(false, artefactFilter); /// Checkout the considered commit of the input SPL to access its source code. try { splRepository.checkoutCommit(splCommit); } catch (final GitAPIException | IOException e) { - Logger.error("Failed to checkout commit " + splCommit.id() + " of " + splRepository.getPath() + "!", e); + Logger.error("Failed to checkout commit " + splCommit.id() + " of " + + splRepository.getPath() + "!", e); return; } for (final Variant variant : variants) { - /// Let's put the variant into our target directory but indexed by commit hash and its name. - final CaseSensitivePath variantDir = variantsGenerationDir.resolve(splCommit.id(), variant.getName()); + /// Let's put the variant into our target directory but indexed by commit hash + /// and its name. + final CaseSensitivePath variantDir = variantsGenerationDir + .resolve(splCommit.id(), variant.getName()); /// We can now generate the variant. /// As a result, we get either the ground truth for the generated variant, /// or an exception telling us that something went wrong. - final Result result = pcs.generateVariant(variant, splRepositoryPath, variantDir, generationOptions); + final Result result = pcs.generateVariant(variant, + splRepositoryPath, variantDir, generationOptions); if (result.isSuccess()) { /// The ground truth of a variant contains: - final GroundTruth groundTruth = result.getSuccess();/// 1. the presence conditions. + final GroundTruth groundTruth = result.getSuccess();/// 1. the presence + /// conditions. final Artefact presenceConditionsOfVariant = groundTruth.variant(); /// 2. a map that stores matchings of blocks for each source code file - final Map fileMatches = groundTruth.fileMatches(); + final Map fileMatches = + groundTruth.fileMatches(); /// We can also export the ground truth PCs of the variant. - Resources.Instance().write(Artefact.class, presenceConditionsOfVariant, variantDir.resolve("pcs.variant.csv").path()); + Resources.Instance().write(Artefact.class, presenceConditionsOfVariant, + variantDir.resolve("pcs.variant.csv").path()); } else { - Logger.error( - "Error upon generation of variant " - + variant.getName() + Logger.error("Error upon generation of variant " + variant.getName() + " at SPL commit " + splCommit.id() + "!", - result.getFailure()); + result.getFailure()); } } @@ -173,7 +197,8 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio try { b.postprocess(); } catch (final GitAPIException | IOException e) { - Logger.error("Busybox postprocessing failed, please clean up manually (e.g., git stash, git stash drop) at " + splRepository.getPath(), e); + Logger.error("Busybox postprocessing failed, please clean up manually (e.g., git stash, git stash drop) at " + + splRepository.getPath(), e); } } } diff --git a/src/main/java/org/variantsync/vevos/simulation/examples/Main.java b/src/main/java/org/variantsync/vevos/simulation/examples/Main.java index 28c3e1a..0023273 100644 --- a/src/main/java/org/variantsync/vevos/simulation/examples/Main.java +++ b/src/main/java/org/variantsync/vevos/simulation/examples/Main.java @@ -11,7 +11,7 @@ import org.variantsync.vevos.simulation.io.Resources; import org.variantsync.vevos.simulation.io.data.VariabilityDatasetLoader; import org.variantsync.vevos.simulation.repository.AbstractSPLRepository; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.EvolutionStep; import org.variantsync.vevos.simulation.variability.SPLCommit; import org.variantsync.vevos.simulation.variability.VariabilityDataset; @@ -74,6 +74,7 @@ public static void main(final String[] args) throws IOException, Resources.Resou final Set> evolutionSteps = variabilityDataset.getEvolutionSteps(); Logger.info("The dataset contains " + variabilityDataset.getSuccessCommits().size() + " commits for which the variability extraction succeeded."); Logger.info("The dataset contains " + variabilityDataset.getErrorCommits().size() + " commits for which the variability extraction failed."); + Logger.info("The dataset contains " + variabilityDataset.getEmptyCommits().size() + " commits without ground truth, because there were no changes of interest."); Logger.info("The dataset contains " + variabilityDataset.getPartialSuccessCommits().size() + " commits that for which the file presence conditions are missing."); Logger.info("The dataset contains " + evolutionSteps.size() + " usable pairs."); for (final EvolutionStep pair : evolutionSteps) { diff --git a/src/main/java/org/variantsync/vevos/simulation/examples/VEVOSBenchmark.java b/src/main/java/org/variantsync/vevos/simulation/examples/VEVOSBenchmark.java index 5366191..58b401b 100644 --- a/src/main/java/org/variantsync/vevos/simulation/examples/VEVOSBenchmark.java +++ b/src/main/java/org/variantsync/vevos/simulation/examples/VEVOSBenchmark.java @@ -14,7 +14,7 @@ import org.variantsync.vevos.simulation.repository.BusyboxRepository; import org.variantsync.vevos.simulation.repository.SPLRepository; import org.variantsync.vevos.simulation.util.Clock; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.variability.EvolutionStep; import org.variantsync.vevos.simulation.variability.SPLCommit; @@ -32,39 +32,34 @@ import java.util.function.Function; public class VEVOSBenchmark { - private record Repo( - CaseSensitivePath splRepositoryPath, - CaseSensitivePath groundTruthDatasetPath, - CaseSensitivePath variantsGenerationDir, - Function createRepo, - Consumer cleanup) {} + private record Repo(CaseSensitivePath splRepositoryPath, + CaseSensitivePath groundTruthDatasetPath, + CaseSensitivePath variantsGenerationDir, + Function createRepo, + Consumer cleanup) { + } /// TODO: Specify your paths here. - private static final CaseSensitivePath SPL_REPOS_DIR = CaseSensitivePath.of("path/to/SPL/repos"); + private static final CaseSensitivePath SPL_REPOS_DIR = + CaseSensitivePath.of("path/to/SPL/repos"); private static final CaseSensitivePath DATASETS_DIR = CaseSensitivePath.of("path/to/datasets/"); - private static final CaseSensitivePath VARIANT_GENERATION_DIR = CaseSensitivePath.of("path/to/datasets/"); - - private static final Repo LINUX = new Repo( - SPL_REPOS_DIR.resolve("linux"), - DATASETS_DIR.resolve("linux"), - VARIANT_GENERATION_DIR.resolve("linux"), - path -> new SPLRepository(path.path()), - s -> {} - ); - private static final Repo BUSYBOX = new Repo( - SPL_REPOS_DIR.resolve("busybox"), - DATASETS_DIR.resolve("busybox"), - VARIANT_GENERATION_DIR.resolve("busybox"), - path -> new BusyboxRepository(path.path()), - s -> { - BusyboxRepository b = Cast.unchecked(s); - try { - b.postprocess(); - } catch (Exception e) { - Logger.error("Error in Busybox cleanup", e); - } - } - ); + private static final CaseSensitivePath VARIANT_GENERATION_DIR = + CaseSensitivePath.of("path/to/datasets/"); + + private static final Repo LINUX = new Repo(SPL_REPOS_DIR.resolve("linux"), + DATASETS_DIR.resolve("linux"), VARIANT_GENERATION_DIR.resolve("linux"), + path -> new SPLRepository(path.path()), s -> { + }); + private static final Repo BUSYBOX = new Repo(SPL_REPOS_DIR.resolve("busybox"), + DATASETS_DIR.resolve("busybox"), VARIANT_GENERATION_DIR.resolve("busybox"), + path -> new BusyboxRepository(path.path()), s -> { + BusyboxRepository b = Cast.unchecked(s); + try { + b.postprocess(); + } catch (Exception e) { + Logger.error("Error in Busybox cleanup", e); + } + }); private final static int NUMBER_OF_VARIANTS_TO_GENERATE = 5; private final static int MAX_COMMITS_TO_ANALYZE = 20; @@ -80,7 +75,7 @@ private static void resultEntry(final StringBuilder builder, final String entry) public static void benchmark(final Repo repo) throws Exception { VEVOS.Initialize(); -// Logger.setLogLevel(LogLevel.INFO); + // Logger.setLogLevel(LogLevel.INFO); final StringBuilder timeData = new StringBuilder(); @@ -88,8 +83,8 @@ public static void benchmark(final Repo repo) throws Exception { final Clock clock = new Clock(); clock.start(); - final VariabilityDataset dataset = - Resources.Instance().load(VariabilityDataset.class, repo.groundTruthDatasetPath().path()); + final VariabilityDataset dataset = Resources.Instance().load(VariabilityDataset.class, + repo.groundTruthDatasetPath().path()); final double timeDatasetLoading = clock.getPassedSeconds(); resultEntry(timeData, logTime("Loading dataset", timeDatasetLoading)); @@ -98,16 +93,22 @@ public static void benchmark(final Repo repo) throws Exception { final double timeEvolutionStepCreation = clock.getPassedSeconds(); resultEntry(timeData, logTime("Creating evolution steps", timeEvolutionStepCreation)); - Logger.info("The dataset contains " + dataset.getSuccessCommits().size() + " commits for which the variability extraction succeeded."); - Logger.info("The dataset contains " + dataset.getErrorCommits().size() + " commits for which the variability extraction failed."); - Logger.info("The dataset contains " + dataset.getPartialSuccessCommits().size() + " commits that for which the file presence conditions are missing."); + Logger.info("The dataset contains " + dataset.getSuccessCommits().size() + + " commits for which the variability extraction succeeded."); + Logger.info("The dataset contains " + dataset.getErrorCommits().size() + + " commits for which the variability extraction failed."); + Logger.info("The dataset contains " + dataset.getEmptyCommits().size() + + " commits for which there is no ground truth."); + Logger.info("The dataset contains " + dataset.getPartialSuccessCommits().size() + + " commits that for which the file presence conditions are missing."); Logger.info("The dataset contains " + evolutionSteps.size() + " usable pairs of commits."); final List subhistory = dataset.getSuccessCommits(); final Sampler variantsSampler = - FeatureIDESampler.CreateRandomSampler(NUMBER_OF_VARIANTS_TO_GENERATE); -// new FeatureIDESampler(NUMBER_OF_VARIANTS_TO_GENERATE, cnf -> new VEVOSRandomSampling(cnf, NUMBER_OF_VARIANTS_TO_GENERATE)); + FeatureIDESampler.CreateRandomSampler(NUMBER_OF_VARIANTS_TO_GENERATE); + // new FeatureIDESampler(NUMBER_OF_VARIANTS_TO_GENERATE, cnf -> new VEVOSRandomSampling(cnf, + // NUMBER_OF_VARIANTS_TO_GENERATE)); double timeLoadPCsAverage = 0; double timeSampleAverage = 0; double timeGenVariantsAverage = 0; @@ -119,7 +120,8 @@ public static void benchmark(final Repo repo) throws Exception { clock.start(); final Lazy> loadFeatureModel = splCommit.featureModel(); - final Lazy> loadPresenceConditions = splCommit.presenceConditions(); + final Lazy> loadPresenceConditions = + splCommit.presenceConditionsFallback(); if (loadPresenceConditions.run().isEmpty()) { Logger.info("has no PCs"); @@ -136,8 +138,8 @@ public static void benchmark(final Repo repo) throws Exception { timeLoadPCsAverage += timeLoadPCs; logTime("Loading PCs and FM", timeLoadPCs); -// Logger.info("#features = " + featureModel.getFeatures().size()); -// Logger.info("Feature model is valid = " + FeatureModelUtils.isValid(featureModel)); + // Logger.info("#features = " + featureModel.getFeatures().size()); + // Logger.info("Feature model is valid = " + FeatureModelUtils.isValid(featureModel)); clock.start(); final Sample variants = variantsSampler.sample(featureModel); @@ -146,20 +148,26 @@ public static void benchmark(final Repo repo) throws Exception { logTime("Sampling " + variants.size() + " variants", timeSample); final ArtefactFilter artefactFilter = ArtefactFilter.KeepAll(); - final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(false, artefactFilter); + final VariantGenerationOptions generationOptions = + VariantGenerationOptions.ExitOnError(false, artefactFilter); clock.start(); for (final Variant variant : variants) { - final CaseSensitivePath variantDir = repo.variantsGenerationDir().resolve(splCommit.id(), "_", analyzedCommits + "" , "_", variant.getName()); - final Result result = pcs.generateVariant(variant, repo.splRepositoryPath(), variantDir, generationOptions); + final CaseSensitivePath variantDir = repo.variantsGenerationDir().resolve( + splCommit.id(), "_", analyzedCommits + "", "_", variant.getName()); + final Result result = pcs.generateVariant(variant, + repo.splRepositoryPath(), variantDir, generationOptions); -// final FeatureIDEConfiguration config = (FeatureIDEConfiguration) variant.getConfiguration(); -// Logger.info("Generating variant with configuration:\r\n" + config.toAssignment()); + // final FeatureIDEConfiguration config = (FeatureIDEConfiguration) + // variant.getConfiguration(); + // Logger.info("Generating variant with configuration:\r\n" + + // config.toAssignment()); if (result.isSuccess()) { final GroundTruth groundTruth = result.getSuccess(); final Artefact presenceConditionsOfVariant = groundTruth.variant(); - Resources.Instance().write(Artefact.class, presenceConditionsOfVariant, variantDir.resolve("pcs.variant.csv").path()); + Resources.Instance().write(Artefact.class, presenceConditionsOfVariant, + variantDir.resolve("pcs.variant.csv").path()); } else { throw result.getFailure(); } @@ -176,13 +184,18 @@ public static void benchmark(final Repo repo) throws Exception { } } - timeLoadPCsAverage = timeLoadPCsAverage / ((double)analyzedCommits); - timeSampleAverage = timeSampleAverage / ((double)analyzedCommits); - timeGenVariantsAverage = timeGenVariantsAverage / (analyzedCommits * NUMBER_OF_VARIANTS_TO_GENERATE); + timeLoadPCsAverage = timeLoadPCsAverage / ((double) analyzedCommits); + timeSampleAverage = timeSampleAverage / ((double) analyzedCommits); + timeGenVariantsAverage = + timeGenVariantsAverage / (analyzedCommits * NUMBER_OF_VARIANTS_TO_GENERATE); resultEntry(timeData, logTime("Loading PCs Average", timeLoadPCsAverage)); - resultEntry(timeData, logTime("Sampling " + NUMBER_OF_VARIANTS_TO_GENERATE + " variants Average", timeSampleAverage)); - resultEntry(timeData, logTime("Generating " + NUMBER_OF_VARIANTS_TO_GENERATE + " variants Average", timeGenVariantsAverage)); + resultEntry(timeData, + logTime("Sampling " + NUMBER_OF_VARIANTS_TO_GENERATE + " variants Average", + timeSampleAverage)); + resultEntry(timeData, logTime( + "Generating " + NUMBER_OF_VARIANTS_TO_GENERATE + " variants Average", + timeGenVariantsAverage)); resultEntry(timeData, "Commits: " + analyzedCommits); final String result = timeData.toString(); diff --git a/src/main/java/org/variantsync/vevos/simulation/feature/config/SimpleConfiguration.java b/src/main/java/org/variantsync/vevos/simulation/feature/config/SimpleConfiguration.java index e66de45..186d81c 100644 --- a/src/main/java/org/variantsync/vevos/simulation/feature/config/SimpleConfiguration.java +++ b/src/main/java/org/variantsync/vevos/simulation/feature/config/SimpleConfiguration.java @@ -1,7 +1,7 @@ package org.variantsync.vevos.simulation.feature.config; import org.prop4j.Node; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import java.util.HashMap; import java.util.List; @@ -13,6 +13,8 @@ public class SimpleConfiguration implements IConfiguration { public SimpleConfiguration(final List activeFeatures) { this.assignment = new HashMap<>(); activeFeatures.forEach(f -> this.assignment.put(f, true)); + this.assignment.put("True", true); + this.assignment.put("False", false); } @Override diff --git a/src/main/java/org/variantsync/vevos/simulation/io/Resources.java b/src/main/java/org/variantsync/vevos/simulation/io/Resources.java index a269e09..0c0d218 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/Resources.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/Resources.java @@ -15,7 +15,7 @@ import org.variantsync.vevos.simulation.io.kernelhaven.KernelHavenSPLPCIO; import org.variantsync.vevos.simulation.io.kernelhaven.KernelHavenVariantPCIO; import org.variantsync.vevos.simulation.io.kernelhaven.VariabilityModelLoader; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.VariabilityDataset; import org.variantsync.vevos.simulation.variability.pc.Artefact; @@ -134,6 +134,7 @@ public T load(final Class type, final Path p) throws ResourceIOException if (loader.canLoad(p)) { final Result result = loader.load(p); if (result.isSuccess()) { + Logger.debug("Successfully loaded resource under " + p); return result.getSuccess(); } else { Logger.error("ResourceLoader " + loader + " failed: ", result.getFailure()); diff --git a/src/main/java/org/variantsync/vevos/simulation/io/TextIO.java b/src/main/java/org/variantsync/vevos/simulation/io/TextIO.java index 1ba8cf8..77dfb5c 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/TextIO.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/TextIO.java @@ -1,7 +1,7 @@ package org.variantsync.vevos.simulation.io; import org.variantsync.functjonal.Result; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import java.io.BufferedReader; import java.io.File; diff --git a/src/main/java/org/variantsync/vevos/simulation/io/data/CSVIO.java b/src/main/java/org/variantsync/vevos/simulation/io/data/CSVIO.java index 4e1e8b6..cff489a 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/data/CSVIO.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/data/CSVIO.java @@ -1,5 +1,6 @@ package org.variantsync.vevos.simulation.io.data; +import org.tinylog.Logger; import org.variantsync.functjonal.Result; import org.variantsync.functjonal.Unit; import org.variantsync.vevos.simulation.io.ResourceLoader; @@ -56,7 +57,8 @@ public Result load(final Path p) { final List rows = reader.lines().map(line -> line.trim().split(separatorWithWhiteSpace)).collect(Collectors.toList()); return Result.Success(new CSV(rows)); - } catch (final IOException e) { + } catch (final Exception e) { + Logger.error(e); return Result.Failure(e); } } diff --git a/src/main/java/org/variantsync/vevos/simulation/io/data/VariabilityDatasetLoader.java b/src/main/java/org/variantsync/vevos/simulation/io/data/VariabilityDatasetLoader.java index 2558361..1731f9b 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/data/VariabilityDatasetLoader.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/data/VariabilityDatasetLoader.java @@ -1,11 +1,10 @@ package org.variantsync.vevos.simulation.io.data; import net.lingala.zip4j.ZipFile; -import net.lingala.zip4j.exception.ZipException; import org.variantsync.functjonal.Result; import org.variantsync.vevos.simulation.io.ResourceLoader; import org.variantsync.vevos.simulation.io.TextIO; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.SPLCommit; import org.variantsync.vevos.simulation.variability.VariabilityDataset; @@ -18,11 +17,16 @@ ; public class VariabilityDatasetLoader implements ResourceLoader { - private final static String SUCCESS_COMMIT_FILE = "SUCCESS_COMMITS.txt"; - private final static String ERROR_COMMIT_FILE = "ERROR_COMMITS.txt"; - private final static String PARTIAL_SUCCESS_COMMIT_FILE = "PARTIAL_SUCCESS_COMMITS.txt"; + private final static String SUCCESS_COMMITS_FILE = "SUCCESS_COMMITS.txt"; + private final static String ERROR_COMMITS_FILE = "ERROR_COMMITS.txt"; + private final static String EMPTY_COMMITS_FILE = "EMPTY_COMMITS.txt"; + private final static String PARTIAL_SUCCESS_COMMITS_FILE = "PARTIAL_SUCCESS_COMMITS.txt"; private final static String FEATURE_MODEL_FILE = "variability-model.json"; - private final static String PRESENCE_CONDITIONS_FILE = "code-variability.spl.csv"; + private final static String PRESENCE_CONDITIONS_BEFORE_FILE = "code-variability.before.spl.csv"; + private final static String PRESENCE_CONDITIONS_AFTER_FILE = "code-variability.after.spl.csv"; + private final static String PRESENCE_CONDITIONS_FALLBACK_FILE = "code-variability.spl.csv"; + private final static String MATCHING_BEFORE_FILE = "code-matching.before.spl.csv"; + private final static String MATCHING_AFTER_FILE = "code-matching.after.spl.csv"; private final static String PARENTS_FILE = "PARENTS.txt"; private final static String MESSAGE_FILE = "MESSAGE.txt"; private final static String VARIABLES_FILE = "VARIABLES.txt"; @@ -33,12 +37,11 @@ public class VariabilityDatasetLoader implements ResourceLoader { - final String name = f.getName(); - return name.equals(SUCCESS_COMMIT_FILE) || name.equals(ERROR_COMMIT_FILE) || name.equals(PARTIAL_SUCCESS_COMMIT_FILE); - }); + return Files.list(p).map(Path::toFile).anyMatch(f -> { + final String name = f.getName(); + return name.equals(SUCCESS_COMMITS_FILE) || name.equals(ERROR_COMMITS_FILE) || name.equals(EMPTY_COMMITS_FILE) + || name.equals(PARTIAL_SUCCESS_COMMITS_FILE); + }); } catch (final IOException e) { Logger.error("Was not able to check the file(s) under " + p, e); return false; @@ -48,13 +51,21 @@ public boolean canLoad(final Path p) { /** * Load a dataset containing the extracted variability information of a SPL. *

- * The given path should point to the root of the dataset's directory. Assume that the given path to the dataset is - * `/home/alice/data/extraction-results`. Then, the structure of `extraction-results` should look as follows: - *

+ * The given path should point to the root of the dataset's directory. Assume that the given + * path to the dataset is `/home/alice/data/extraction-results`. Then, the structure of + * `extraction-results` should look as follows: + *

+ *

* extraction-results/ - *

|- log/

- *

|- data/

- *

|- SUCCESS_COMMITS.txt

+ *

+ * |- log/ + *

+ *

+ * |- data/ + *

+ *

+ * |- SUCCESS_COMMITS.txt + *

* * @param p path to the root directory of the dataset * @return The fully-loaded dataset if loading is successful, otherwise an Exception. @@ -62,32 +73,43 @@ public boolean canLoad(final Path p) { @Override public Result load(final Path p) { // Read the metadata - List successIds = new ArrayList<>(); - List errorIds = new ArrayList<>(); - List partialSuccessIds = new ArrayList<>(); - - Logger.status("Started loading of dataset under " + p); - final Path successFile = p.resolve(SUCCESS_COMMIT_FILE); - if (Files.exists(successFile)) { - successIds = TextIO.readLinesTrimmed(successFile).expect("Success-commit file exists but could not be loaded."); + List successCommitIds = new ArrayList<>(); + List errorCommitIds = new ArrayList<>(); + List emptyCommitIds = new ArrayList<>(); + List partialSuccessCommitIds = new ArrayList<>(); + + Logger.info("Started loading of dataset under " + p); + final Path successCommitFile = p.resolve(SUCCESS_COMMITS_FILE); + if (Files.exists(successCommitFile)) { + successCommitIds = TextIO.readLinesTrimmed(successCommitFile) + .expect("Success-commit file exists but could not be loaded."); } - final Path errorFile = p.resolve(ERROR_COMMIT_FILE); - if (Files.exists(errorFile)) { - errorIds = TextIO.readLinesTrimmed(errorFile).expect("Error-commit file exists but could not be loaded."); + final Path errorCommitFile = p.resolve(ERROR_COMMITS_FILE); + if (Files.exists(errorCommitFile)) { + errorCommitIds = TextIO.readLinesTrimmed(errorCommitFile) + .expect("Error-commit file exists but could not be loaded."); } - final Path partialSuccessFile = p.resolve(PARTIAL_SUCCESS_COMMIT_FILE); - if (Files.exists(partialSuccessFile)) { - partialSuccessIds = TextIO.readLinesTrimmed(partialSuccessFile).expect("Partial-success-commit file exists but could not be loaded."); + final Path emptyFile = p.resolve(EMPTY_COMMITS_FILE); + if (Files.exists(emptyFile)) { + emptyCommitIds = TextIO.readLinesTrimmed(emptyFile) + .expect("Success-commit file exists but could not be loaded."); + } + + final Path partialSuccessCommitFile = p.resolve(PARTIAL_SUCCESS_COMMITS_FILE); + if (Files.exists(partialSuccessCommitFile)) { + partialSuccessCommitIds = TextIO.readLinesTrimmed(partialSuccessCommitFile) + .expect("Partial-success-commit file exists but could not be loaded."); } Logger.info("Read commit ids."); // Create SPLCommit objects for each commit - final List successCommits = initializeSPLCommits(p, successIds); - final List errorCommits = initializeSPLCommits(p, errorIds); - final List partialSuccessCommits = initializeSPLCommits(p, partialSuccessIds); + final List successCommits = initializeSPLCommits(p, successCommitIds); + final List errorCommits = initializeSPLCommits(p, errorCommitIds); + final List emptyCommits = initializeSPLCommits(p, emptyCommitIds); + final List partialSuccessCommits = initializeSPLCommits(p, partialSuccessCommitIds); Logger.info("Initialized SPL commits."); // Retrieve the SPLCommit objects for the parents of each commit @@ -96,6 +118,8 @@ public Result load(final Path p) { Logger.info("Mapped success commits."); errorCommits.forEach(c -> idToCommitMap.put(c.id(), c)); Logger.info("Mapped error commits."); + emptyCommits.forEach(c -> idToCommitMap.put(c.id(), c)); + Logger.info("Mapped empty commits."); partialSuccessCommits.forEach(c -> idToCommitMap.put(c.id(), c)); Logger.info("Mapped partial success commits."); @@ -105,48 +129,92 @@ public Result load(final Path p) { if (parentIds == null || parentIds.length == 0) { entry.getValue().setParents(null); } else { - entry.getValue().setParents(Arrays.stream(parentIds).map(idToCommitMap::get).toArray(SPLCommit[]::new)); + entry.getValue().setParents(Arrays.stream(parentIds).map(id -> { + var commit = idToCommitMap.get(id); + if (commit == null) { + commit = new SPLCommit(id); + } + return commit; + }).toArray(SPLCommit[]::new)); } } Logger.info("Done."); Logger.info("Found a total of " + idToCommitMap.size() + " commits."); // Return the fully-loaded dataset - return Result.Success(new VariabilityDataset(successCommits, errorCommits, partialSuccessCommits)); + return Result.Success(new VariabilityDataset(successCommits, errorCommits, emptyCommits, + partialSuccessCommits)); } private List initializeSPLCommits(final Path p, final List commitIds) { final List splCommits = new ArrayList<>(commitIds.size()); for (final String id : commitIds) { - // Initialize a SPLCommit object for each commit id by resolving all paths to files with data about the commit - final SPLCommit splCommit = new SPLCommit( - id, - resolvePathToCommitOutputDir(p, id), - resolvePathToLogFile(p, id), - resolvePathToFeatureModel(p, id), - resolvePathToPresenceConditions(p, id), - resolvePathToMessageFile(p, id), - resolvePathToFilterCountsFile(p, id)); + // Initialize a SPLCommit object for each commit id by resolving all paths to files with + // data about the commit + final SPLCommit splCommit = new SPLCommit(id, resolvePathToCommitOutputDir(p, id), + resolvePathToLogFile(p, id), resolvePathToFeatureModel(p, id), + resolvePathToPresenceConditionsBefore(p, id), + resolvePathToPresenceConditionsAfter(p, id), + resolvePathToPresenceConditionsFallback(p, id), + resolvePathToMatchingBefore(p, id), + resolvePathToMatchingAfter(p, id), + resolvePathToMessageFile(p, id), resolvePathToFilterCountsFile(p, id)); splCommits.add(splCommit); } return splCommits; } + private SPLCommit.CodeMatchingPath resolvePathToMatchingBefore(final Path rootDir, String commitId) { + final Path p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(MATCHING_BEFORE_FILE); + return new SPLCommit.CodeMatchingPath(p); + } + + private SPLCommit.CodeMatchingPath resolvePathToMatchingAfter(final Path rootDir, String commitId) { + final Path p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(MATCHING_AFTER_FILE); + return new SPLCommit.CodeMatchingPath(p); + } + private Path resolvePathToCommitOutputDir(final Path rootDir, final String commitId) { return rootDir.resolve(DATA_DIR_NAME).resolve(commitId); } - private SPLCommit.FeatureModelPath resolvePathToFeatureModel(final Path rootDir, final String commitId) { + private SPLCommit.FeatureModelPath resolvePathToFeatureModel(final Path rootDir, + final String commitId) { Path p = resolvePathToCommitOutputDir(rootDir, commitId).resolve(FEATURE_MODEL_FILE); if (!Files.exists(p)) { // If no feature model is found, we instead set the variables file, as feature model - // TODO: Move this logic to VEVOS_extraction, if we convert a feature model a FeatureIDE format? + // TODO: Move this logic to VEVOS_extraction, if we convert a feature model a FeatureIDE + // format? p = resolvePathToCommitOutputDir(rootDir, commitId).resolve(VARIABLES_FILE); } return new SPLCommit.FeatureModelPath(p); } - private SPLCommit.PresenceConditionPath resolvePathToPresenceConditions(final Path rootDir, final String commitId) { - final Path p = resolvePathToCommitOutputDir(rootDir, commitId).resolve(PRESENCE_CONDITIONS_FILE); + private SPLCommit.PresenceConditionPath resolvePathToPresenceConditionsBefore( + final Path rootDir, final String commitId) { + final Path p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(PRESENCE_CONDITIONS_BEFORE_FILE); + return new SPLCommit.PresenceConditionPath(p); + } + + private SPLCommit.PresenceConditionPath resolvePathToPresenceConditionsAfter(final Path rootDir, + final String commitId) { + final Path p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(PRESENCE_CONDITIONS_AFTER_FILE); + return new SPLCommit.PresenceConditionPath(p); + } + + private SPLCommit.PresenceConditionPath resolvePathToPresenceConditionsFallback( + final Path rootDir, final String commitId) { + // For the fallback file, we first try the 'after' version of the PCS. If it does not exist, + // we use the fallback + Path p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(PRESENCE_CONDITIONS_AFTER_FILE); + if (!Files.exists(p)) { + p = resolvePathToCommitOutputDir(rootDir, commitId) + .resolve(PRESENCE_CONDITIONS_FALLBACK_FILE); + } return new SPLCommit.PresenceConditionPath(p); } @@ -154,17 +222,20 @@ private Path resolvePathToParentsFile(final Path rootDir, final String commitId) return resolvePathToCommitOutputDir(rootDir, commitId).resolve(PARENTS_FILE); } - private SPLCommit.CommitMessagePath resolvePathToMessageFile(final Path rootDir, final String commitId) { + private SPLCommit.CommitMessagePath resolvePathToMessageFile(final Path rootDir, + final String commitId) { final Path p = resolvePathToCommitOutputDir(rootDir, commitId).resolve(MESSAGE_FILE); return new SPLCommit.CommitMessagePath(p); } - private SPLCommit.KernelHavenLogPath resolvePathToLogFile(final Path rootDir, final String commitId) { + private SPLCommit.KernelHavenLogPath resolvePathToLogFile(final Path rootDir, + final String commitId) { final Path p = rootDir.resolve(LOG_DIR_NAME).resolve(commitId + ".log"); return new SPLCommit.KernelHavenLogPath(p); } - private SPLCommit.FilterCountsPath resolvePathToFilterCountsFile(final Path rootDir, final String commitId) { + private SPLCommit.FilterCountsPath resolvePathToFilterCountsFile(final Path rootDir, + final String commitId) { final Path p = resolvePathToCommitOutputDir(rootDir, commitId).resolve(FILTER_COUNTS_FILE); return new SPLCommit.FilterCountsPath(p); } @@ -176,11 +247,13 @@ private String[] loadParentIds(final Path p, final String commitId) { final File zipFile = new File(parentsFile.getParent() + ".zip"); Logger.debug("Checking ZIP file " + zipFile); if (zipFile.exists()) { - try { + try (var zip = new ZipFile(zipFile)) { Logger.debug("Unzipping PARENTS.txt"); - new ZipFile(zipFile).extractFile(commitId + "/PARENTS.txt", String.valueOf(resolvePathToCommitOutputDir(p, commitId).getParent())); - } catch (final ZipException e) { - // Not all commits have a ZIP file and not all commits with a ZIP file have a PARENTS.txt. So this is + zip.extractFile(commitId + "/PARENTS.txt", String.valueOf( + resolvePathToCommitOutputDir(p, commitId).getParent())); + } catch (final IOException e) { + // Not all commits have a ZIP file and not all commits with a ZIP file have a + // PARENTS.txt. So this is // an expected exception Logger.debug("Was not able to unzip commit data." + e.getMessage()); } @@ -190,10 +263,12 @@ private String[] loadParentIds(final Path p, final String commitId) { try { return Files.readString(parentsFile).split("\\s"); } catch (final IOException e) { - Logger.error("Was not able to load PARENTS.txt " + parentsFile + " even though it exists:", e); + Logger.error("Was not able to load PARENTS.txt " + parentsFile + + " even though it exists:", e); return null; } } return null; } -} \ No newline at end of file +} + diff --git a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/ArtefactCSVExporter.java b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/ArtefactCSVExporter.java index e0f1f11..06ba8a8 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/ArtefactCSVExporter.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/ArtefactCSVExporter.java @@ -21,7 +21,7 @@ * Path;File Condition;Block Condition;Presence Condition;start;end */ public class ArtefactCSVExporter implements ArtefactVisitor { - private final static int COLUMN_COUNT = 6; + private final static int COLUMN_COUNT = 7; private final List csv = new ArrayList<>(); private SourceCodeFile currentFile = null; @@ -39,8 +39,9 @@ public ArtefactCSVExporter() { header[1] = "File Condition"; header[2] = "Block Condition"; header[3] = "Presence Condition"; - header[4] = "start"; - header[5] = "end"; + header[4] = "Line Type"; + header[5] = "start"; + header[6] = "end"; csv.add(header); } @@ -62,9 +63,9 @@ private String[] toRow(final LineBasedAnnotation annotation) { row[1] = FormulaUtils.toFormulaString(currentFile.getPresenceCondition(), NodeWriter.javaSymbols); row[2] = FormulaUtils.toFormulaString(annotation.getFeatureMapping(), NodeWriter.javaSymbols); row[3] = FormulaUtils.toFormulaString(annotation.getPresenceCondition(), NodeWriter.javaSymbols); - row[4] = "" + annotation.getLineFrom(); - // -1 because Kernelhaven stores annotations as [#if, #endif) intervals, so we have to point one line before the annotation end (#endif). - row[5] = "" + (annotation.getLineTo() - (annotation.isMacro() ? 1 : 0)); + row[4] = annotation.getLineType().name; + row[5] = "" + annotation.getLineFrom(); + row[6] = "" + annotation.getLineTo(); return row; } diff --git a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenPCIO.java b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenPCIO.java index ead4860..cc8b939 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenPCIO.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenPCIO.java @@ -2,6 +2,7 @@ import org.prop4j.Node; import org.prop4j.NodeReader; +import org.tinylog.Logger; import org.variantsync.functjonal.Result; import org.variantsync.functjonal.Unit; import org.variantsync.functjonal.list.ListHeadTailView; @@ -16,6 +17,7 @@ import org.variantsync.vevos.simulation.variability.pc.LineBasedAnnotation; import org.variantsync.vevos.simulation.variability.pc.SourceCodeFile; import org.variantsync.vevos.simulation.variability.pc.SyntheticArtefactTreeNode; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; import java.nio.file.Path; import java.util.*; @@ -44,45 +46,72 @@ public boolean canWrite(final Path p) { public Result load(final Path csvPath) { final Map files = new HashMap<>(); final CSV csv; + Logger.debug("Loading csv file: " + csvPath); try { csv = Resources.Instance().load(CSV.class, csvPath); } catch (final Resources.ResourceIOException resourceFailure) { + Logger.error("Was not able to load csv file: " + resourceFailure); return Result.Failure(resourceFailure); } - // parser for propositional formulas - final NodeReader nodeReader = new NodeReader(); - nodeReader.activateJavaSymbols(); // select the symbols used for parsing conjunction (&&), disjunction (||), ... - - // skip first entry as it is the csv header - final ListHeadTailView rows = new ListHeadTailView<>(csv.rows()).tail(); - for (final String[] row : rows) { - final CaseSensitivePath pathOfSourceFile = CaseSensitivePath.of(row[0]); - final Node fileCondition = FixTrueFalse.EliminateTrueAndFalseInplace(nodeReader.stringToNode(row[1])); - final Node blockCondition = FixTrueFalse.EliminateTrueAndFalseInplace(nodeReader.stringToNode(row[2])); - // We don't need the actual presenceCondition (lol) as it is a value computed from row[1] and row[2] - // final Node presenceCondition = nodeReader.stringToNode(row[3]); - final int startLine = Integer.parseInt(row[4]); - final int endLine = Integer.parseInt(row[5]); + Logger.debug("Parsing..."); + + try { + // parser for propositional formulas + final NodeReader nodeReader = new NodeReader(); + nodeReader.activateJavaSymbols(); // select the symbols used for parsing conjunction (&&), disjunction (||), ... + + // skip first entry as it is the csv header + final ListHeadTailView rows = new ListHeadTailView<>(csv.rows()).tail(); + for (final String[] row : rows) { + final CaseSensitivePath pathOfSourceFile = CaseSensitivePath.of(row[0]); + Node fileConditionNode = nodeReader.stringToNode(row[1]); + if (fileConditionNode == null) { + Logger.warn("Was not able to parse the file condition for " + pathOfSourceFile + " in " + csvPath); + Logger.warn("GT entry: " + row[1]); + Logger.warn(nodeReader.getErrorMessage()); + fileConditionNode = nodeReader.stringToNode("1"); + } + Node blockConditionNode = nodeReader.stringToNode(row[2]); + if (blockConditionNode == null) { + Logger.warn("Was not able to parse the block condition for " + pathOfSourceFile + " in " + csvPath); + Logger.warn("GT entry: " + row[2]); + Logger.warn(nodeReader.getErrorMessage()); + blockConditionNode = nodeReader.stringToNode("1"); + } + Node presenceConditionNode = nodeReader.stringToNode(row[3]); + if (presenceConditionNode == null) { + Logger.warn("Was not able to parse the presence condition for " + pathOfSourceFile + " in " + csvPath); + Logger.warn("GT entry: " + row[3]); + Logger.warn(nodeReader.getErrorMessage()); + presenceConditionNode = nodeReader.stringToNode("1"); + } + final Node fileCondition = FixTrueFalse.EliminateTrueAndFalseInplace(fileConditionNode); + final Node blockCondition = FixTrueFalse.EliminateTrueAndFalseInplace(blockConditionNode); + // We don't need the actual presenceCondition (lol) as it is a value computed from row[1] and row[2] + final Node presenceCondition = FixTrueFalse.EliminateTrueAndFalseInplace(presenceConditionNode); + final LineType lineType = LineType.fromName(row[4]); + final int startLine = Integer.parseInt(row[5]); + final int endLine = Integer.parseInt(row[6]); /* Add the file to our map if not already present and add the PreprocessorBlock to it that was described in the parsed row. */ - try { files.computeIfAbsent( - pathOfSourceFile, - p -> new SourceCodeFile(fileCondition, p)) - .addTrace(createAnnotation(blockCondition, startLine, endLine)); - } catch (final Exception e) { - return Result.Failure(e); + pathOfSourceFile, + p -> new SourceCodeFile(fileCondition, fileCondition, p)) + .addTrace(createAnnotation(blockCondition, presenceCondition, lineType, startLine, endLine)); } + // sort and return all files as list + final List allFiles = new ArrayList<>(files.values()); + allFiles.sort(Comparator.comparing(SourceCodeFile::getFile)); + Logger.debug("Parsed a ground truth for " + allFiles.size() + " files."); + return Result.Success(new SyntheticArtefactTreeNode<>(allFiles)); + } catch (final Exception e) { + Logger.error("Was not able to parse csv file: " + e); + return Result.Failure(e); } - - // sort and return all files as list - final List allFiles = new ArrayList<>(files.values()); - allFiles.sort(Comparator.comparing(SourceCodeFile::getFile)); - return Result.Success(new SyntheticArtefactTreeNode<>(allFiles)); } @Override @@ -108,5 +137,9 @@ public Result load(final Path csvPath) { return Result.Try(() -> Resources.Instance().write(CSV.class, csv, p)); } - protected abstract LineBasedAnnotation createAnnotation(final Node blockCondition, final int startLine, final int endLine); + protected abstract LineBasedAnnotation createAnnotation(final Node blockCondition, + final Node presenceCondition, + final LineType lineType, + final int startLine, + final int endLine); } diff --git a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenSPLPCIO.java b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenSPLPCIO.java index 284dbb9..f8f22a3 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenSPLPCIO.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenSPLPCIO.java @@ -3,6 +3,7 @@ import org.prop4j.Node; import org.variantsync.vevos.simulation.variability.pc.AnnotationStyle; import org.variantsync.vevos.simulation.variability.pc.LineBasedAnnotation; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; /** * IO for presence condition of source code of software product lines. @@ -18,33 +19,20 @@ public KernelHavenSPLPCIO() { } @Override - protected LineBasedAnnotation createAnnotation(final Node blockCondition, final int startLine, int endLine) { + protected LineBasedAnnotation createAnnotation(final Node blockCondition, final Node presenceCondition, + final LineType lineType, final int startLine, int endLine) { /// If a block starts at 1 in KernelHaven files, it does not denote an #if but the entire file. /// Thus, there is no #if at line 1 but LineBasedAnnotation expects a macro at startLine. final boolean isVirtualSurroundingTrue = startLine == 1; - /* - Line numbers for the all surrounding true are given in [1, last line of file + 1] format. - Line numbers for macros are given in [#if, #endif) format. + Line numbers for the all surrounding true are given in [1, last line of file] format. + Line numbers for macros are given in [firstCodeLine, lastCodeLine] format. */ - if (isVirtualSurroundingTrue) { - /* - If we find the all-surrounding "true" macro: - We know that it always points behind one line behind the last line. - Thus, we would need to do - endLine -= 1; - here. However, this would be inconsistent with the export of presence condition. - When exporting presence conditions, we cannot recover the information if an annotation - was the all surrounding virtual true macro. So to be consistent, we just leave the line - as is although it now points to the (non-existent) line after the file's last line. - This might cause warnings on exporting PCs. - */ - } else { - endLine += 1 /* to include #endif */; - } return new LineBasedAnnotation( blockCondition, + presenceCondition, + lineType, startLine, endLine, isVirtualSurroundingTrue ? AnnotationStyle.External : AnnotationStyle.Internal); diff --git a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenVariantPCIO.java b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenVariantPCIO.java index 60755c4..22d599d 100644 --- a/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenVariantPCIO.java +++ b/src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/KernelHavenVariantPCIO.java @@ -3,6 +3,7 @@ import org.prop4j.Node; import org.variantsync.vevos.simulation.variability.pc.AnnotationStyle; import org.variantsync.vevos.simulation.variability.pc.LineBasedAnnotation; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; /** * IO for presence condition of source code of variants of a software product lines. @@ -14,7 +15,8 @@ public KernelHavenVariantPCIO() { } @Override - protected LineBasedAnnotation createAnnotation(final Node blockCondition, final int startLine, final int endLine) { - return new LineBasedAnnotation(blockCondition, startLine, endLine, AnnotationStyle.External); + protected LineBasedAnnotation createAnnotation(final Node blockCondition, final Node presenceCondition, + final LineType lineType, final int startLine, final int endLine) { + return new LineBasedAnnotation(blockCondition, presenceCondition, lineType, startLine, endLine, AnnotationStyle.External); } } diff --git a/src/main/java/org/variantsync/vevos/simulation/repository/BusyboxRepository.java b/src/main/java/org/variantsync/vevos/simulation/repository/BusyboxRepository.java index 911734f..5452255 100644 --- a/src/main/java/org/variantsync/vevos/simulation/repository/BusyboxRepository.java +++ b/src/main/java/org/variantsync/vevos/simulation/repository/BusyboxRepository.java @@ -2,7 +2,7 @@ import net.ssehub.kernel_haven.util.null_checks.NonNull; import org.eclipse.jgit.api.errors.GitAPIException; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.SPLCommit; import java.io.*; diff --git a/src/main/java/org/variantsync/vevos/simulation/repository/Repository.java b/src/main/java/org/variantsync/vevos/simulation/repository/Repository.java index f89756d..43f77ab 100644 --- a/src/main/java/org/variantsync/vevos/simulation/repository/Repository.java +++ b/src/main/java/org/variantsync/vevos/simulation/repository/Repository.java @@ -6,7 +6,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevCommit; import org.variantsync.vevos.simulation.util.GitUtil; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import java.io.IOException; import java.nio.file.Path; diff --git a/src/main/java/org/variantsync/vevos/simulation/util/GitUtil.java b/src/main/java/org/variantsync/vevos/simulation/util/GitUtil.java index 1f01905..6201301 100644 --- a/src/main/java/org/variantsync/vevos/simulation/util/GitUtil.java +++ b/src/main/java/org/variantsync/vevos/simulation/util/GitUtil.java @@ -4,6 +4,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.tinylog.Logger; import java.io.File; import java.io.IOException; diff --git a/src/main/java/org/variantsync/vevos/simulation/util/LogLevel.java b/src/main/java/org/variantsync/vevos/simulation/util/LogLevel.java deleted file mode 100644 index 9765373..0000000 --- a/src/main/java/org/variantsync/vevos/simulation/util/LogLevel.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.variantsync.vevos.simulation.util; - -public enum LogLevel { - DEBUG, - INFO, - STATUS, - WARNING, - ERROR -} diff --git a/src/main/java/org/variantsync/vevos/simulation/util/Logger.java b/src/main/java/org/variantsync/vevos/simulation/util/Logger.java deleted file mode 100644 index d7dc2ea..0000000 --- a/src/main/java/org/variantsync/vevos/simulation/util/Logger.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.variantsync.vevos.simulation.util; - -import org.variantsync.functjonal.Result; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class Logger { - protected static LogLevel logLevel = LogLevel.INFO; - private static Logger INSTANCE; - private final Map streamMap; - - private Logger(final Map streamMap) { - this.streamMap = streamMap; - } - - public static void init(final Map streamMap) { - if (Logger.INSTANCE != null) { - Logger.warning("Logger already initialized"); - return; - } - for (final LogLevel level : LogLevel.values()) { - if (!streamMap.containsKey(level)) { - throw new IllegalArgumentException("No Output stream for " + logLevel + " defined."); - } - } - Logger.INSTANCE = new Logger(streamMap); - } - - public static void initConsoleLogger() { - final Map streamMap = new HashMap<>(); - streamMap.put(LogLevel.INFO, System.out); - streamMap.put(LogLevel.DEBUG, System.out); - streamMap.put(LogLevel.STATUS, System.out); - streamMap.put(LogLevel.WARNING, System.out); - streamMap.put(LogLevel.ERROR, System.err); - init(streamMap); - } - - public static void debug(final String message) { - INSTANCE.log(message, LogLevel.DEBUG); - } - - public static void info(final String message) { - INSTANCE.log(message, LogLevel.INFO); - } - - public static void info(final Collection collection) { - info(collectionToString(collection)); - } - - public static void status(final String message) { - INSTANCE.log(message, LogLevel.STATUS); - } - - public static void warning(final String message) { - INSTANCE.log(message, LogLevel.WARNING); - } - - public static void error(final String message) { - INSTANCE.log(message, LogLevel.ERROR); - } - - public static void error(final Collection collection) { - error(collectionToString(collection)); - } - - public static void error(final String message, final Exception e) { - INSTANCE.log(String.format("%s%n%s%n%s%n", message, e.getMessage(), Arrays.toString(e.getStackTrace())), LogLevel.ERROR); - } - - public static void log(final Result result) { - if (result.isSuccess()) { - info(result.getSuccess().toString()); // printing in green would be cool! :D - } else { - error(result.getFailure().toString()); - } - } - - public static void setLogLevel(final LogLevel level) { - Logger.logLevel = level; - } - - public static LogLevel logLevel() { - return Logger.logLevel; - } - - private static String collectionToString(final Collection collection) { - final StringBuilder sb = new StringBuilder("["); - int count = 0; - for (final var item : collection) { - sb.append(item); - if (count < collection.size() - 1) { - sb.append(" , "); - } - count++; - } - sb.append("]"); - return sb.toString(); - } - - protected void log(final String message, final LogLevel targetLevel) { - if (logLevel.ordinal() <= targetLevel.ordinal()) { - try { - final var stream = streamMap.get(targetLevel); - stream.write(format(message, targetLevel).getBytes(StandardCharsets.UTF_8)); - stream.write('\n'); - stream.flush(); - } catch (final IOException e) { - throw new UncheckedIOException(e); - } - } - } - - // https://stackoverflow.com/questions/11306811/how-to-get-the-caller-class-in-java - public static String getCallerClassName() { - final StackTraceElement[] stElements = Thread.currentThread().getStackTrace(); - String callerClassName = null; - for (int i = 1; i < stElements.length; i++) { - final StackTraceElement ste = stElements[i]; - if (!ste.getClassName().startsWith("java.lang.Thread")) { - if (callerClassName == null) { - callerClassName = ste.getClassName(); - } else if (!callerClassName.equals(ste.getClassName())) { - final String[] name = ste.getClassName().split("\\."); - return name[name.length - 1]; - } - } - } - return null; - } - - private static String format(final String message, final LogLevel level) { - return String.format("[%s] [%s] [%s] [%s] %s", LocalDateTime.now().format(DateTimeFormatter.ISO_TIME), level, Thread.currentThread().getName(), getCallerClassName(), message); - } - -} diff --git a/src/main/java/org/variantsync/vevos/simulation/util/fide/FeatureModelUtils.java b/src/main/java/org/variantsync/vevos/simulation/util/fide/FeatureModelUtils.java index 47b32fd..50dc19a 100644 --- a/src/main/java/org/variantsync/vevos/simulation/util/fide/FeatureModelUtils.java +++ b/src/main/java/org/variantsync/vevos/simulation/util/fide/FeatureModelUtils.java @@ -10,7 +10,7 @@ import de.ovgu.featureide.fm.core.job.monitor.ConsoleTimeMonitor; import net.ssehub.kernel_haven.variability_model.VariabilityModel; import org.variantsync.vevos.simulation.io.Resources; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/SPLCommit.java b/src/main/java/org/variantsync/vevos/simulation/variability/SPLCommit.java index 1cff95e..f382537 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/SPLCommit.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/SPLCommit.java @@ -2,18 +2,20 @@ import de.ovgu.featureide.fm.core.base.IFeatureModel; import net.lingala.zip4j.ZipFile; -import net.lingala.zip4j.exception.ZipException; import org.variantsync.functjonal.CachedValue; import org.variantsync.functjonal.Functjonal; import org.variantsync.functjonal.Lazy; import org.variantsync.functjonal.functions.FragileFunction; import org.variantsync.vevos.simulation.io.Resources; +import org.variantsync.vevos.simulation.io.data.CSV; import org.variantsync.vevos.simulation.repository.Commit; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.io.TypedPath; import org.variantsync.vevos.simulation.variability.pc.Artefact; import org.variantsync.vevos.simulation.variability.pc.EFilterOutcome; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.CodeMatching; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; @@ -23,91 +25,120 @@ public class SPLCommit extends Commit implements CachedValue { private final Lazy> kernelHavenLog; private final Lazy> featureModel; - private final Lazy> presenceConditions; + private final Lazy> presenceConditionsBefore; + private final Lazy> presenceConditionsAfter; + private final Lazy> presenceConditionsFallback; + private final Lazy> codeMatching; private final Lazy> message; private final Lazy>> filterCounts; private final Path dataDir; private final Path kernelHavenLogPath; private final Path featureModelPath; - private final Path presenceConditionsPath; + private final Path presenceConditionsBeforePath; + private final Path presenceConditionsAfterPath; + private final Path presenceConditionsFallbackPath; + private final Path codeMatchingBeforePath; + private final Path codeMatchingAfterPath; private final Path commitMessagePath; private final Path filterCountsPath; private SPLCommit[] parents; /** - * Constructor for commits that should only contain information about the commit id. - * TODO: Document params. + * Constructor for commits that should only contain information about the commit id. TODO: + * Document params. * * @param commitId The id of the commit */ public SPLCommit(final String commitId) { - this(commitId, null, null, null, null, null, null); + this(commitId, null, null, null, null, null, null, null, null, null, null); } - public SPLCommit( - final String commitId, - final Path dataDir, - final KernelHavenLogPath kernelHavenLog, - final FeatureModelPath featureModel, - final PresenceConditionPath presenceConditions, - final CommitMessagePath commitMessage, - final FilterCountsPath filterCounts) { + public SPLCommit(final String commitId, final Path dataDir, + final KernelHavenLogPath kernelHavenLog, final FeatureModelPath featureModel, + final PresenceConditionPath presenceConditionsBefore, + final PresenceConditionPath presenceConditionsAfter, + final PresenceConditionPath presenceConditionsFallback, + final CodeMatchingPath codeMatchingBefore, + final CodeMatchingPath codeMatchingAfter, + final CommitMessagePath commitMessage, final FilterCountsPath filterCounts) { super(commitId); this.dataDir = dataDir; this.kernelHavenLogPath = TypedPath.unwrapNullable(kernelHavenLog); this.featureModelPath = TypedPath.unwrapNullable(featureModel); - this.presenceConditionsPath = TypedPath.unwrapNullable(presenceConditions); + this.presenceConditionsBeforePath = TypedPath.unwrapNullable(presenceConditionsBefore); + this.presenceConditionsAfterPath = TypedPath.unwrapNullable(presenceConditionsAfter); + this.presenceConditionsFallbackPath = TypedPath.unwrapNullable(presenceConditionsFallback); + this.codeMatchingBeforePath = TypedPath.unwrapNullable(codeMatchingBefore); + this.codeMatchingAfterPath = TypedPath.unwrapNullable(codeMatchingAfter); this.commitMessagePath = TypedPath.unwrapNullable(commitMessage); this.filterCountsPath = TypedPath.unwrapNullable(filterCounts); - final FragileFunction tryUnzip = SPLCommit::tryUnzip; + final FragileFunction tryUnzip = SPLCommit::tryUnzip; // Lazy loading of log file - this.kernelHavenLog = Functjonal.mapFragileLazily( - kernelHavenLogPath, - tryUnzip.andThen(Files::readString), - () -> "Was not able to load KernelHaven log for commit " + commitId); + this.kernelHavenLog = Functjonal.mapFragileLazily(kernelHavenLogPath, + tryUnzip.andThen(Files::readString), + () -> "Was not able to load KernelHaven log for commit " + commitId); // Lazy loading of feature model - this.featureModel = Functjonal.mapFragileLazily( - featureModelPath, - tryUnzip.andThen(path -> Resources.Instance().load(IFeatureModel.class, path)), - () -> "Was not able to load feature model for id " + commitId); - // Lazy loading of presence condition - this.presenceConditions = Functjonal.mapFragileLazily( - presenceConditionsPath, - tryUnzip.andThen(path -> Resources.Instance().load(Artefact.class, path)), - () -> "Was not able to load presence conditions for id " + commitId); + this.featureModel = Functjonal.mapFragileLazily(featureModelPath, tryUnzip + .andThen(path -> Resources.Instance().load(IFeatureModel.class, path)), + () -> "Was not able to load feature model for id " + commitId); + // Lazy loading of presence conditions + this.presenceConditionsBefore = Functjonal.mapFragileLazily(presenceConditionsBeforePath, + tryUnzip.andThen(path -> Resources.Instance().load(Artefact.class, path)), + () -> "Was not able to load 'before' presence conditions for id " + + commitId); + this.presenceConditionsAfter = Functjonal.mapFragileLazily(presenceConditionsAfterPath, + tryUnzip.andThen(path -> Resources.Instance().load(Artefact.class, path)), + () -> "Was not able to load 'after' presence conditions for id " + + commitId); + this.presenceConditionsFallback = Functjonal.mapFragileLazily( + presenceConditionsFallbackPath, + tryUnzip.andThen(path -> Resources.Instance().load(Artefact.class, path)), + () -> "Was not able to load fallback presence conditions for id " + + commitId); + this.codeMatching = tryToLoadMatching(); // Lazy loading of commit message - this.message = Functjonal.mapFragileLazily( - commitMessagePath, - tryUnzip.andThen(Files::readString), - () -> "Was not able to load commit message for id " + commitId); + this.message = Functjonal.mapFragileLazily(commitMessagePath, + tryUnzip.andThen(Files::readString), + () -> "Was not able to load commit message for id " + commitId); // Lazy loading of filter counts - this.filterCounts = Functjonal.mapFragileLazily( - filterCountsPath, - tryUnzip.andThen(path -> { - final Map countsMap = new HashMap<>(); - Files.readAllLines(path).stream().map(l -> l.split(":")).forEach(parts -> countsMap.put(EFilterOutcome.valueOf(parts[0]), Integer.parseInt(parts[1].trim()))); - return countsMap; - }), - () -> "Was not able to load filter counts for id " + commitId); + this.filterCounts = Functjonal.mapFragileLazily(filterCountsPath, tryUnzip.andThen(path -> { + final Map countsMap = new HashMap<>(); + Files.readAllLines(path).stream().map(l -> l.split(":")) + .forEach(parts -> countsMap.put(EFilterOutcome.valueOf(parts[0]), + Integer.parseInt(parts[1].trim()))); + return countsMap; + }), () -> "Was not able to load filter counts for id " + commitId); } - private static Path tryUnzip(final Path path) throws ZipException { + private Lazy> tryToLoadMatching() { + Lazy> matchingBefore = Functjonal.mapFragileLazily(codeMatchingBeforePath, + path -> Resources.Instance().load(CSV.class, path), + () -> "Was not able to load 'after' presence conditions for id " + + this.id()); + Lazy> matchingAfter = Functjonal.mapFragileLazily(codeMatchingAfterPath, + path -> Resources.Instance().load(CSV.class, path), + () -> "Was not able to load 'after' presence conditions for id " + + this.id()); + return CodeMatching.lazyFromCSVs(matchingBefore, matchingAfter); + } + + private static Path tryUnzip(final Path path) throws IOException { if (!Files.exists(path)) { final Path zippedParent = Path.of(path.getParent() + ".zip"); Logger.debug("Checking whether there is an archive " + zippedParent); if (Files.exists(zippedParent)) { Logger.debug("Archive " + zippedParent.getFileName() + " found."); - try { - new ZipFile(zippedParent.toFile()).extractAll(String.valueOf(zippedParent.getParent())); - } catch (final ZipException e) { + try (var zip = new ZipFile(zippedParent.toFile())) { + zip.extractAll(String.valueOf(zippedParent.getParent())); + } catch (final IOException e) { Logger.error("Was not able to unzip " + zippedParent, e); throw e; } } else { - Logger.warning("Path " + path + " does not exist and no ZIP file found."); + Logger.warn("Path " + path + " does not exist and no ZIP file found."); return null; } } else { @@ -123,16 +154,20 @@ private static Path tryUnzip(final Path path) throws ZipException { public void forget() { kernelHavenLog.forget(); featureModel.forget(); - presenceConditions.forget(); + presenceConditionsBefore.forget(); + presenceConditionsAfter.forget(); + presenceConditionsFallback.forget(); message.forget(); filterCounts.forget(); } /** - * Return the parents of this commit. As the dataset only contains the data about non-error commits, - * not all SPLCommit objects are associated with their parents. Therefore, an Optional is returned. + * Return the parents of this commit. As the dataset only contains the data about non-error + * commits, not all SPLCommit objects are associated with their parents. Therefore, + * an Optional is returned. * - * @return An Optional containing the SPLCommit objects of the parent commits, if there are any. + * @return An Optional containing the SPLCommit objects of the parent + * commits, if there are any. */ public Optional parents() { return Optional.ofNullable(parents); @@ -164,10 +199,24 @@ public Lazy> featureModel() { } /** - * @return A Lazy that loads the presence conditions associated with this commit. + * @return A Lazy that loads the before change presence conditions associated with this commit. + */ + public Lazy> presenceConditionsBefore() { + return presenceConditionsBefore; + } + + /** + * @return A Lazy that loads the after change presence conditions associated with this commit. + */ + public Lazy> presenceConditionsAfter() { + return presenceConditionsAfter; + } + + /** + * @return A Lazy that loads the fallback presence conditions associated with this commit. */ - public Lazy> presenceConditions() { - return presenceConditions; + public Lazy> presenceConditionsFallback() { + return presenceConditionsFallback; } /** @@ -189,8 +238,16 @@ public Path getFeatureModelPath() { return featureModelPath; } - public Path getPresenceConditionsPath() { - return presenceConditionsPath; + public Path getPresenceConditionsBeforePath() { + return presenceConditionsBeforePath; + } + + public Path getPresenceConditionsAfterPath() { + return presenceConditionsAfterPath; + } + + public Path getPresenceConditionsFallbackPath() { + return presenceConditionsFallbackPath; } public Path getCommitMessagePath() { @@ -213,6 +270,10 @@ public record PresenceConditionPath(Path path) implements TypedPath { } + public record CodeMatchingPath(Path path) implements TypedPath { + + } + public record CommitMessagePath(Path path) implements TypedPath { } @@ -220,4 +281,5 @@ public record CommitMessagePath(Path path) implements TypedPath { public record FilterCountsPath(Path path) implements TypedPath { } -} \ No newline at end of file +} + diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/VariabilityDataset.java b/src/main/java/org/variantsync/vevos/simulation/variability/VariabilityDataset.java index 68bd079..1dc575c 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/VariabilityDataset.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/VariabilityDataset.java @@ -2,7 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.variantsync.functjonal.list.NonEmptyList; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.sequenceextraction.CleaningEvolutionStepsStream; import java.util.*; @@ -14,16 +14,20 @@ public class VariabilityDataset { private final List successCommits; private final List errorCommits; private final List partialSuccessCommits; + private final List emptyCommits; - public VariabilityDataset(@NotNull final List successCommits, @NotNull final List errorCommits, @NotNull final List partialSuccessCommits) { + public VariabilityDataset(@NotNull final List successCommits, @NotNull final List errorCommits, + @NotNull final List emptyCommits, @NotNull final List partialSuccessCommits) { this.successCommits = successCommits; this.errorCommits = errorCommits; + this.emptyCommits = emptyCommits; this.partialSuccessCommits = partialSuccessCommits; this.allCommits = new HashSet<>(); this.allCommits.addAll(successCommits); this.allCommits.addAll(errorCommits); + this.allCommits.addAll(emptyCommits); this.allCommits.addAll(partialSuccessCommits); - if (allCommits.size() != successCommits.size() + errorCommits.size() + partialSuccessCommits.size()) { + if (allCommits.size() != successCommits.size() + errorCommits.size() + emptyCommits.size() + partialSuccessCommits.size()) { final String errorMessage = "Some of the dataset's commits belong to more than one category (SUCCESS | ERROR | INCOMPLETE_PC)"; Logger.error(errorMessage); throw new IllegalArgumentException(errorMessage); @@ -51,6 +55,13 @@ public List getErrorCommits() { return errorCommits; } + /** + * @return A List of all commits for which there was no variability data for extraction. Thus, there is no ground truth. + */ + public List getEmptyCommits() { + return successCommits; + } + /** * @return A List of all commits for which variability data was extracted partially. */ diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/Artefact.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/Artefact.java index 1d495df..10761cd 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/Artefact.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/Artefact.java @@ -33,8 +33,10 @@ public interface Artefact { */ CaseSensitivePath getFile(); + @Deprecated /** * Simplifies the artefact w.r.t. to presence conditions and redundant structure. + * This method might no longer work properly with the new GT format and should be used with care. */ void simplify(); diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/ArtefactTree.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/ArtefactTree.java index 6e9bcd4..594610f 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/ArtefactTree.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/ArtefactTree.java @@ -17,6 +17,7 @@ */ public abstract class ArtefactTree> implements Artefact { private Node featureMapping; + private Node presenceCondition; private final CaseSensitivePath file; private ArtefactTree parent; protected List subtrees; @@ -24,18 +25,19 @@ public abstract class ArtefactTree> implements Art /** * Creates a new empty tree (node) with the given feature mapping. */ - public ArtefactTree(final Node featureMapping) { - this(featureMapping, new ArrayList<>(), null); + public ArtefactTree(final Node featureMapping, Node presenceCondition) { + this(featureMapping, presenceCondition, new ArrayList<>(), null); } /** * Creates a new tree (node) with the given feature mapping and subtrees representing (content of) the given file. */ - public ArtefactTree(final Node featureMapping, final List subtrees, final CaseSensitivePath file) { + public ArtefactTree(final Node featureMapping, final Node presenceCondition, final List subtrees, final CaseSensitivePath file) { Objects.requireNonNull(featureMapping); Objects.requireNonNull(subtrees); this.featureMapping = featureMapping; + this.presenceCondition = presenceCondition; this.file = file; setSubtrees(subtrees); @@ -45,6 +47,10 @@ protected void setFeatureMapping(final Node featureMapping) { this.featureMapping = featureMapping; } + protected void setPresenceCondition(final Node presenceCondition) { + this.presenceCondition = presenceCondition; + } + @Override public Node getFeatureMapping() { return featureMapping; @@ -52,10 +58,7 @@ public Node getFeatureMapping() { @Override public Node getPresenceCondition() { - final Artefact parent = getParent(); - return parent == null ? - featureMapping : - FormulaUtils.AndSimplified(parent.getPresenceCondition(), featureMapping); + return presenceCondition; } @Override @@ -145,7 +148,12 @@ public boolean isLeaf() { return subtrees.isEmpty(); } + + /** + * This method might no longer work properly with the new GT format and should be used with care. + */ @Override + @Deprecated public void simplify() { for (final Child c : subtrees) { c.simplify(); diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotation.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotation.java index 2bb429d..42133ce 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotation.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotation.java @@ -7,6 +7,8 @@ import org.variantsync.vevos.simulation.variability.pc.groundtruth.AnnotationGroundTruth; import org.variantsync.vevos.simulation.variability.pc.groundtruth.BlockMatching; import org.variantsync.vevos.simulation.variability.pc.groundtruth.GroundTruth; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; +import org.variantsync.vevos.simulation.variability.pc.options.ArtefactFilter; import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions; import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantAnnotation; import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantLine; @@ -17,6 +19,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -26,6 +29,7 @@ public class LineBasedAnnotation extends ArtefactTree { private final AnnotationStyle style; private int lineFrom; private int lineTo; + private LineType lineType; /** * Creates a new annotation starting at lineFrom and ending at lineTo including both @@ -40,15 +44,17 @@ public class LineBasedAnnotation extends ArtefactTree { * 6 #endif <-- lineTo * is reflected by LineBasedAnnotation(X, 3, 6, Internal); */ - public LineBasedAnnotation(final Node featureMapping, final int lineFrom, final int lineTo, final AnnotationStyle style) { - super(featureMapping); + public LineBasedAnnotation(final Node featureMapping, final Node presenceCondition, final LineType lineType, final int lineFrom, final int lineTo, final AnnotationStyle style) { + super(featureMapping, presenceCondition); + this.lineType = lineType; this.lineFrom = lineFrom; this.lineTo = lineTo; this.style = style; } public LineBasedAnnotation(final LineBasedAnnotation other) { - super(other.getFeatureMapping()); + super(other.getFeatureMapping(), other.getPresenceCondition()); + this.lineType = other.lineType; this.lineFrom = other.lineFrom; this.lineTo = other.lineTo; this.style = other.style; @@ -102,29 +108,19 @@ public Result generateVariant( throw new UnsupportedOperationException(); } - public Optional deriveForVariant(final Variant variant) { + public Optional deriveForVariant(final Variant variant, ArtefactFilter annotationFilter) { final BlockMatching matching = BlockMatching.MONOID.neutral(); - return deriveForVariant(variant, 0, matching).map(l -> new AnnotationGroundTruth(this, l, matching)); + return deriveForVariant(variant, annotationFilter, 0, matching).map(l -> new AnnotationGroundTruth(this, l, matching)); } - private Optional deriveForVariant(final Variant variant, int offset, final BlockMatching matching) { - // TODO: It should be sufficient to check the feature mapping here. - if (variant.isImplementing(getPresenceCondition())) { + private Optional deriveForVariant(final Variant variant, ArtefactFilter annotationFilter, int offset, final BlockMatching matching) { + if (annotationFilter.shouldKeep(this) && variant.isImplementing(this.getPresenceCondition())) { final int firstCodeLine = getLineFrom() + offset; - offset -= style.offset; // ignore #if - int lastAnnotationEnd = 0; /// convert all subtrees to variants final List newSubtrees = new ArrayList<>(getNumberOfSubtrees()); for (final LineBasedAnnotation splAnnotation : subtrees) { - // We have to increase the offset for each subtree overlap (i.e., in the case of #else), because overlapping subtrees - // share the same #endif, which should only be counted once. - if (splAnnotation.lineFrom == lastAnnotationEnd) { - offset++; - } - lastAnnotationEnd = splAnnotation.lineTo; - - final Optional mVariantAnnotation = splAnnotation.deriveForVariant(variant, offset, matching); + final Optional mVariantAnnotation = splAnnotation.deriveForVariant(variant, annotationFilter, offset, matching); // If the subtree is still present in the variant, it might have shrunk. // That can happen when the subtree as nested annotations inside it that code removed. if (mVariantAnnotation.isPresent()) { @@ -138,8 +134,8 @@ private Optional deriveForVariant(final Variant variant, in } } - final int lastCodeLine = getLineTo() + offset - style.offset; // ignore #endif - final LineBasedAnnotation meAsVariant = new LineBasedAnnotation(getFeatureMapping(), firstCodeLine, lastCodeLine, AnnotationStyle.External); + final int lastCodeLine = getLineTo() + offset; // ignore #endif + final LineBasedAnnotation meAsVariant = new LineBasedAnnotation(getFeatureMapping(), getPresenceCondition(), getLineType(), firstCodeLine, lastCodeLine, AnnotationStyle.External); meAsVariant.setSubtrees(newSubtrees); matching.put(this, meAsVariant); return Optional.of(meAsVariant); @@ -148,6 +144,10 @@ private Optional deriveForVariant(final Variant variant, in } } + public LineType getLineType() { + return this.lineType; + } + /** * Computes all lines that should be included in the given variant when evaluating the annotations in this artefact. * @@ -157,8 +157,8 @@ private Optional deriveForVariant(final Variant variant, in public VariantAnnotation getLinesToCopy(final Predicate isIncluded) { final List chunksToWrite = new ArrayList<>(); // final List chunksToWrite = new ArrayList<>(); - final int firstCodeLine = getLineFrom() + style.offset; // ignore #if - final int lastCodeLine = getLineTo() - style.offset; // ignore #endif + final int firstCodeLine = getLineFrom(); // ignore #if + final int lastCodeLine = getLineTo(); // ignore #endif int currentLine = firstCodeLine; for (final LineBasedAnnotation subtree : subtrees) { @@ -183,6 +183,10 @@ public VariantAnnotation getLinesToCopy(final Predicate isI ); } + /** + * This method might no longer work properly with the new GT format and should be used with care. + */ + @Deprecated public void simplify() { LineBasedAnnotationSimplifier.simplify(this); } diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotationSimplifier.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotationSimplifier.java index 560d463..ff2abba 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotationSimplifier.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotationSimplifier.java @@ -12,13 +12,6 @@ * Collections of methods for simplifying LineBasedAnnotations. */ public class LineBasedAnnotationSimplifier { - /** - * @return A simplified formula of the given annotation's feature mapping. - */ - public static Node simplifiedMapping(final LineBasedAnnotation root) { - // Actually we could employ presence condition simplification by Rhein et al. here. - return root.getFeatureMapping().simplifyTree(); - } /** * Inlines all subtrees with redundant annotation. Example: @@ -113,7 +106,8 @@ public static List mergedEqualNeighbours(final List flattenedSubtrees = mergedEqualNeighbours(flattenedSubtrees(root)); for (final LineBasedAnnotation child : flattenedSubtrees) { simplify(child); diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/SourceCodeFile.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/SourceCodeFile.java index de83b5c..39ebfb9 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/SourceCodeFile.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/SourceCodeFile.java @@ -6,12 +6,14 @@ import org.variantsync.functjonal.category.Traversable; import org.variantsync.vevos.simulation.feature.Variant; import org.variantsync.vevos.simulation.io.TextIO; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.fide.bugfix.FixTrueFalse; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.util.io.PathUtils; import org.variantsync.vevos.simulation.variability.pc.groundtruth.BlockMatching; import org.variantsync.vevos.simulation.variability.pc.groundtruth.GroundTruth; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; +import org.variantsync.vevos.simulation.variability.pc.options.ArtefactFilter; import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions; import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantAnnotation; import org.variantsync.vevos.simulation.variability.pc.visitor.SourceCodeFileVisitorFocus; @@ -26,12 +28,13 @@ public class SourceCodeFile extends ArtefactTree { private final LineBasedAnnotation rootAnnotation; - public SourceCodeFile(final Node featureMapping, final CaseSensitivePath relativePath) { - this(featureMapping, relativePath, new LineBasedAnnotation(FixTrueFalse.True, 1, 1, AnnotationStyle.External)); + public SourceCodeFile(final Node featureMapping, final Node presenceCondition, final CaseSensitivePath relativePath) { + this(featureMapping, presenceCondition, relativePath, + new LineBasedAnnotation(FixTrueFalse.True, FixTrueFalse.True, LineType.ROOT, 1, 1, AnnotationStyle.External)); } - private SourceCodeFile(final Node featureMapping, final CaseSensitivePath relativePath, final LineBasedAnnotation root) { - super(featureMapping, Collections.singletonList(root), relativePath); + private SourceCodeFile(final Node featureMapping, final Node presenceCondition, final CaseSensitivePath relativePath, final LineBasedAnnotation root) { + super(featureMapping, presenceCondition, Collections.singletonList(root), relativePath); rootAnnotation = root; } @@ -54,6 +57,8 @@ public Result generateVariant( return Result.Failure(new FileNotFoundException("Source file " + sourceFile + " does not exist!")); } + ArtefactFilter annotationFilter = strategy.lineFilter(); + return // Create the target file. PathUtils.createEmptyAsResult(targetFile.path()) @@ -61,7 +66,7 @@ public Result generateVariant( .bind(unit -> Traversable.sequence( // Compute ground truth for our variant (i.e., make the variant feature-aware rootAnnotation - .deriveForVariant(variant) + .deriveForVariant(variant, annotationFilter) .map(splAnnotationGroundTruth -> { final BlockMatching lineMatching = splAnnotationGroundTruth.matching(); // Retrieve all lines of code from the SPL file that should be included in the variant file. @@ -86,10 +91,10 @@ public Result generateVariant( // In case of success, return ground truth. Functjonal.match( splAnnotationGroundTruth -> GroundTruth.forSourceCodeFile( - new SourceCodeFile(getFeatureMapping(), getFile(), splAnnotationGroundTruth.variantArtefact()), + new SourceCodeFile(getFeatureMapping(), getPresenceCondition(), getFile(), splAnnotationGroundTruth.variantArtefact()), splAnnotationGroundTruth ), - () -> GroundTruth.withoutAnnotations(new SourceCodeFile(getFeatureMapping(), getFile())) + () -> GroundTruth.withoutAnnotations(new SourceCodeFile(getFeatureMapping(), getPresenceCondition(), getFile())) ), // In case of failure, log it (and implicitly transform IOException to Exception). ioexception -> { @@ -103,6 +108,10 @@ public LineBasedAnnotation getRootAnnotation() { return rootAnnotation; } + /** + * This method might no longer work properly with the new GT format and should be used with care. + */ + @Deprecated public void simplify() { rootAnnotation.simplify(); } diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/SyntheticArtefactTreeNode.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/SyntheticArtefactTreeNode.java index aec64b8..477212d 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/SyntheticArtefactTreeNode.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/SyntheticArtefactTreeNode.java @@ -3,7 +3,7 @@ import org.variantsync.functjonal.Cast; import org.variantsync.functjonal.Result; import org.variantsync.vevos.simulation.feature.Variant; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.fide.bugfix.FixTrueFalse; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.variability.pc.groundtruth.GroundTruth; @@ -31,7 +31,7 @@ public SyntheticArtefactTreeNode() { * Creates a new tree (node) with feature mapping True and the given subtrees. */ public SyntheticArtefactTreeNode(final List subtrees) { - super(FixTrueFalse.True, subtrees, null); + super(FixTrueFalse.True, FixTrueFalse.True, subtrees, null); } /** @@ -39,7 +39,7 @@ public SyntheticArtefactTreeNode(final List subtrees) { * @param other Object to create a plain copy of (without copying children). */ public SyntheticArtefactTreeNode(final ArtefactTree other) { - super(other.getFeatureMapping(), new ArrayList<>(), other.getFile()); + super(other.getFeatureMapping(), other.getPresenceCondition(), new ArrayList<>(), other.getFile()); } @Override diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/CodeMatching.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/CodeMatching.java new file mode 100644 index 0000000..23b6a0e --- /dev/null +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/CodeMatching.java @@ -0,0 +1,126 @@ +package org.variantsync.vevos.simulation.variability.pc.groundtruth; + +import org.variantsync.functjonal.Lazy; +import org.variantsync.functjonal.list.ListHeadTailView; +import org.variantsync.vevos.simulation.io.data.CSV; + +import java.nio.file.Path; +import java.util.*; + +public record CodeMatching(HashMap fileMatchingBefore, HashMap fileMatchingAfter) { + + public Optional beforeCommitMatch(Path pathToFile, int lineNumber) { + return Optional.of(fileMatchingBefore.get(pathToFile)).map(m -> m.matching.get(lineNumber)); + } + + public Optional afterCommitMatch(Path pathToFile, int lineNumber) { + return Optional.of(fileMatchingAfter.get(pathToFile)).map(m -> m.matching.get(lineNumber)); + } + + public static Lazy> lazyFromCSVs(Lazy> matchingBefore, Lazy> matchingAfter) { + return Lazy.of(() -> fromCSVs(matchingBefore, matchingAfter)); + } + + public static Optional fromCSVs(Lazy> matchingBefore, Lazy> matchingAfter) { + Optional mBefore = matchingBefore.run(); + Optional mAfter = matchingAfter.run(); + if (mBefore.isEmpty() || mAfter.isEmpty()) { + return Optional.empty(); + } + return Optional.of(fromCSVs(mBefore.get(), mAfter.get())); + } + + public static CodeMatching fromCSVs(CSV matchingBefore, CSV matchingAfter) { + HashMap fileMatchingBefore = parseCSV(matchingBefore); + HashMap fileMatchingAfter = parseCSV(matchingAfter); + // Make sure that the matchings agree + checkAgreement(fileMatchingBefore, fileMatchingAfter); + return new CodeMatching(fileMatchingBefore, fileMatchingAfter); + } + + private static HashMap parseCSV(CSV csv) { + // skip first entry as it is the csv header + final ListHeadTailView rows = new ListHeadTailView<>(csv.rows()).tail(); + final HashMap matching = new HashMap<>(); + for (final String[] row : rows) { + Path filePath = Path.of(row[0]); + int currentLine = Integer.parseInt(row[1]); + int matchedLine = Integer.parseInt(row[2]); + + // Retrieve or initialize the file matching + FileMatching fm = matching.computeIfAbsent(filePath, FileMatching::new); + fm.add(currentLine, matchedLine); + } + return matching; + } + + private static final class FileMatching { + private final Path filePath; + private final ArrayList matching; + + public FileMatching(Path filePath) { + super(); + this.filePath = filePath; + this.matching = new ArrayList<>(); + } + + public void add(int currentLine, int matchedLine) { + if (this.matching.size() != currentLine) { + throw new IllegalStateException(); + } + this.matching.add(matchedLine); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (FileMatching) obj; + return Objects.equals(this.filePath, that.filePath) && + Objects.equals(this.matching, that.matching); + } + + @Override + public int hashCode() { + return Objects.hash(filePath, matching); + } + + @Override + public String toString() { + return "FileMatching[" + + "filePath=" + filePath + ", " + + "matching=" + matching + ']'; + } + + } + + private static void checkAgreement(HashMap before, HashMap after) { + final IllegalArgumentException agreementMismatch = new IllegalArgumentException("The matchings do not agree."); + if (before.keySet().size() != after.keySet().size()) { + throw agreementMismatch; + } + for (Path path : before.keySet()) { + FileMatching beforeMatching = before.get(path); + FileMatching afterMatching = after.get(path); + if (afterMatching == null) { + throw agreementMismatch; + } + if (arraysDisagree(beforeMatching.matching, afterMatching.matching) + || arraysDisagree(afterMatching.matching, beforeMatching.matching)) { + throw agreementMismatch; + } + } + } + + private static boolean arraysDisagree(ArrayList first, ArrayList second) { + for (int i = 0; i < first.size(); i++) { + if (first.get(i) == -1) { + continue; + } + if (second.get(first.get(i)) != i) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/LineType.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/LineType.java new file mode 100644 index 0000000..ecf601d --- /dev/null +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/groundtruth/LineType.java @@ -0,0 +1,44 @@ +package org.variantsync.vevos.simulation.variability.pc.groundtruth; + +public enum LineType { + IF("if"), + ELSE("else"), + ELIF("elif"), + ARTIFACT("artifact"), + ENDIF("endif"), + ROOT("ROOT"); + + public final String name; + + private LineType(String name) { + this.name = name; + } + + public boolean isConditionalAnnotation() { + return this == IF || this == ELIF; + } + + public boolean isAnnotation() { + return this != ARTIFACT; + } + + public boolean notAMacro() {return this == ARTIFACT || this == ROOT;} + + public static LineType fromName(String name) { + LineType[] var1 = values(); + int var2 = var1.length; + + for(int var3 = 0; var3 < var2; ++var3) { + LineType candidate = var1[var3]; + if (candidate.toString().equalsIgnoreCase(name)) { + return candidate; + } + } + + throw new IllegalArgumentException("Given string \"" + name + "\" is not the name of a LineType."); + } + + public static int getRequiredBitCount() { + return 3; + } +} diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/options/VariantGenerationOptions.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/options/VariantGenerationOptions.java index 4e5a9cc..249cf29 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/options/VariantGenerationOptions.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/options/VariantGenerationOptions.java @@ -1,5 +1,6 @@ package org.variantsync.vevos.simulation.variability.pc.options; +import org.variantsync.vevos.simulation.variability.pc.LineBasedAnnotation; import org.variantsync.vevos.simulation.variability.pc.SourceCodeFile; public record VariantGenerationOptions( @@ -16,4 +17,13 @@ public static VariantGenerationOptions ExitOnError(final boolean withMacros, fin public static VariantGenerationOptions ExitOnErrorButAllowNonExistentFiles(final boolean withMacros, final ArtefactFilter filter) { return new VariantGenerationOptions(true, true, withMacros, filter); } + + public ArtefactFilter lineFilter() { + return (annotation -> { + if (withMacros) { + return true; + } + return annotation.getLineType().notAMacro(); + }); + } } diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantAnnotation.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantAnnotation.java index 3a1bddb..41f33db 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantAnnotation.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantAnnotation.java @@ -16,20 +16,10 @@ public record VariantAnnotation( public List project(final VariantGenerationOptions projectionOptions, final List splFileLines) { final List result = new ArrayList<>(); - final boolean genMacro = projectionOptions.withMacros() && !isTrue(); - - if (genMacro) { - result.add("#if " + FormulaUtils.toCPPString(condition)); - } - for (final VariantLineChunk child : lines) { result.addAll(child.project(projectionOptions, splFileLines)); } - if (genMacro) { - result.add("#endif"); - } - return result; } diff --git a/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantLine.java b/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantLine.java index b5c6dc9..a8c436f 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantLine.java +++ b/src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantLine.java @@ -1,9 +1,10 @@ package org.variantsync.vevos.simulation.variability.pc.variantlines; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions; import java.util.List; +import java.util.stream.Collectors; public record VariantLine(Integer lineNumber) implements VariantLineChunk { @Override @@ -23,8 +24,10 @@ public List project(final VariantGenerationOptions projectionOptions, fi // This was logged frequently and is caused by https://bugs.openjdk.java.net/browse/JDK-8199413 // Skipping the line really is the best solution, as the empty line is created by appending a line separator // to the previous line. I added the additional if-statement, to only catch cases in which more than one line - // is out of bounds, which indicates a serious problem. - Logger.error(logMessage); + // is out of bounds, which might indicate a problem. + Logger.debug(logMessage); + String lines = String.join("\n", splFileLines); + Logger.debug(lines); } return List.of(); diff --git a/src/main/java/org/variantsync/vevos/simulation/variants/VariantsRepository.java b/src/main/java/org/variantsync/vevos/simulation/variants/VariantsRepository.java index 4d08465..469d297 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variants/VariantsRepository.java +++ b/src/main/java/org/variantsync/vevos/simulation/variants/VariantsRepository.java @@ -8,7 +8,7 @@ import org.variantsync.vevos.simulation.repository.AbstractSPLRepository; import org.variantsync.vevos.simulation.repository.AbstractVariantsRepository; import org.variantsync.vevos.simulation.repository.Branch; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.variants.blueprints.VariantsRevisionBlueprint; import java.io.IOException; diff --git a/src/main/java/org/variantsync/vevos/simulation/variants/blueprints/VariantsRevisionFromVariabilityBlueprint.java b/src/main/java/org/variantsync/vevos/simulation/variants/blueprints/VariantsRevisionFromVariabilityBlueprint.java index 240109f..eec56ed 100644 --- a/src/main/java/org/variantsync/vevos/simulation/variants/blueprints/VariantsRevisionFromVariabilityBlueprint.java +++ b/src/main/java/org/variantsync/vevos/simulation/variants/blueprints/VariantsRevisionFromVariabilityBlueprint.java @@ -8,7 +8,7 @@ import org.variantsync.vevos.simulation.repository.AbstractSPLRepository; import org.variantsync.vevos.simulation.repository.AbstractVariantsRepository; import org.variantsync.vevos.simulation.repository.Branch; -import org.variantsync.vevos.simulation.util.Logger; +import org.tinylog.Logger; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.variability.SPLCommit; import org.variantsync.vevos.simulation.variability.pc.Artefact; @@ -32,17 +32,17 @@ public class VariantsRevisionFromVariabilityBlueprint extends VariantsRevisionBl private final SamplingStrategy sampler; /** - * Creates a new blueprint that can generate variants from the given splCommit that contains - * the variability information of a specific SPLCommit. + * Creates a new blueprint that can generate variants from the given splCommit that contains the + * variability information of a specific SPLCommit. + * * @param splCommit The variability commit from which a VariantsRevision should be created. - * @param predecessor The predecessor blueprint that generates the VariantsRevision that has to be generated before - * the revision of this blueprint. May be null, if this is the first blueprint to generate. + * @param predecessor The predecessor blueprint that generates the VariantsRevision that has to + * be generated before the revision of this blueprint. May be null, if this is the first + * blueprint to generate. */ - public VariantsRevisionFromVariabilityBlueprint( - final SPLCommit splCommit, - final VariantsRevisionFromVariabilityBlueprint predecessor, - final SamplingStrategy sampler) - { + public VariantsRevisionFromVariabilityBlueprint(final SPLCommit splCommit, + final VariantsRevisionFromVariabilityBlueprint predecessor, + final SamplingStrategy sampler) { super(predecessor); this.splCommit = splCommit; this.sampler = sampler; @@ -54,13 +54,15 @@ public SPLCommit getSPLCommit() { @Override protected Lazy computeSample() { - return splCommit.featureModel().map(featureModel -> sampler.sampleForRevision(featureModel, this)); + return splCommit.featureModel() + .map(featureModel -> sampler.sampleForRevision(featureModel, this)); } @Override public Lazy generateArtefactsFor(final VariantsRevision revision) { - return splCommit.presenceConditions().and(getSample()).map(ts -> { - // TODO: Should we implement handling of an empty optional, or do we consider this to be a fundamental error? + return splCommit.presenceConditionsFallback().and(getSample()).map(ts -> { + // TODO: Should we implement handling of an empty optional, or do we consider this to be + // a fundamental error? final Artefact traces = ts.first().orElseThrow(); final Sample sample = ts.second(); final AbstractSPLRepository splRepo = revision.getSPLRepo(); @@ -73,31 +75,35 @@ public Lazy generateArtefactsFor(final VariantsRevisi try { variantsRepo.checkoutBranch(branch); } catch (final IOException | GitAPIException e) { - throw new RuntimeException("Failed checkout of branch " + branch + " in variants repository."); + throw new RuntimeException("Failed checkout of branch " + branch + + " in variants repository."); } try { splRepo.checkoutCommit(splCommit); } catch (final IOException | GitAPIException e) { - throw new RuntimeException("Failed checkout of commit " + splCommit.id() + " in SPL Repository."); + throw new RuntimeException("Failed checkout of commit " + splCommit.id() + + " in SPL Repository."); } // Generate the code - final Result result = traces.generateVariant( - variant, - new CaseSensitivePath(splRepo.getPath()), - new CaseSensitivePath(variantsRepo.getPath()), - VariantGenerationOptions.ExitOnErrorButAllowNonExistentFiles(false, ArtefactFilter.KeepAll())); - Logger.log(result.map(u -> "Generating variant " + variant + " was successful!")); + final Result result = traces.generateVariant(variant, + new CaseSensitivePath(splRepo.getPath()), + new CaseSensitivePath(variantsRepo.getPath()), + VariantGenerationOptions.ExitOnErrorButAllowNonExistentFiles(false, + ArtefactFilter.KeepAll())); + Logger.debug(result.map(u -> "Generating variant " + variant + " was successful!")); // Commit the generated variant with the corresponding spl commit has as message. - final String commitMessage = splCommit.id() + " || " + splCommit.message() + " || " + variant.getName(); + final String commitMessage = splCommit.id() + " || " + splCommit.message() + " || " + + variant.getName(); final Optional variantCommit; try { variantCommit = variantsRepo.commit(commitMessage); } catch (final GitAPIException | IOException e) { - throw new RuntimeException("Failed to commit " + commitMessage + " to VariantsRepository."); + throw new RuntimeException("Failed to commit " + commitMessage + + " to VariantsRepository."); } variantCommit.ifPresent(commit -> commits.put(branch, commit)); diff --git a/src/main/resources/tinylog.properties b/src/main/resources/tinylog.properties new file mode 100644 index 0000000..564a22e --- /dev/null +++ b/src/main/resources/tinylog.properties @@ -0,0 +1,6 @@ +writer1 = console +writer1.level = info + +writer2 = file +writer2.level = debug +writer2.file = log.txt \ No newline at end of file diff --git a/src/test/java/org/variantsync/vevos/simulation/FeatureModelLoading.java b/src/test/java/org/variantsync/vevos/simulation/FeatureModelLoading.java index aea073e..4a0b682 100644 --- a/src/test/java/org/variantsync/vevos/simulation/FeatureModelLoading.java +++ b/src/test/java/org/variantsync/vevos/simulation/FeatureModelLoading.java @@ -2,6 +2,7 @@ import de.ovgu.featureide.fm.core.base.IFeatureModel; import de.ovgu.featureide.fm.core.base.IFeatureModelElement; +import org.junit.Assert; import org.junit.Test; import org.variantsync.vevos.simulation.variability.SPLCommit; @@ -19,13 +20,17 @@ public class FeatureModelLoading { @Test public void fromVariables() throws IOException { - Path variablesPath = Path.of("src/test/java/org/variantsync/vevos/simulation/variability/TEST_VARIABLES.txt"); - SPLCommit commit = new SPLCommit("aaaaa", null, null, new SPLCommit.FeatureModelPath(variablesPath), null, null, null); + Path variablesPath = Path.of( + "src/test/java/org/variantsync/vevos/simulation/variability/TEST_VARIABLES.txt"); + SPLCommit commit = new SPLCommit("aaaaa", null, null, + new SPLCommit.FeatureModelPath(variablesPath), null, null, null, null, null, null, + null); IFeatureModel featureModel = commit.featureModel().run().orElseThrow(); - Collection features = featureModel.getFeatures().stream().map(IFeatureModelElement::getName).collect(Collectors.toList()); - assert features.size() == 5; + Collection features = featureModel.getFeatures().stream() + .map(IFeatureModelElement::getName).toList(); + Assert.assertEquals(6, features.size()); for (String feature : Files.readAllLines(variablesPath)) { - assert features.contains(feature); + Assert.assertTrue(features.contains(feature)); } } } diff --git a/src/test/java/org/variantsync/vevos/simulation/KernelHavenPCIOTest.java b/src/test/java/org/variantsync/vevos/simulation/KernelHavenPCIOTest.java new file mode 100644 index 0000000..b0d1a7c --- /dev/null +++ b/src/test/java/org/variantsync/vevos/simulation/KernelHavenPCIOTest.java @@ -0,0 +1,16 @@ +package org.variantsync.vevos.simulation; + +import org.junit.Test; +import org.variantsync.vevos.simulation.io.kernelhaven.KernelHavenSPLPCIO; + +import java.nio.file.Path; + +public class KernelHavenPCIOTest { + + @Test + public void test() { + VEVOS.Initialize(); + KernelHavenSPLPCIO io = new KernelHavenSPLPCIO(); + io.load(Path.of("src/test/resources/gts/gt-1.spl.csv")); + } +} diff --git a/src/test/java/org/variantsync/vevos/simulation/MatchingIOTest.java b/src/test/java/org/variantsync/vevos/simulation/MatchingIOTest.java new file mode 100644 index 0000000..7f82b9d --- /dev/null +++ b/src/test/java/org/variantsync/vevos/simulation/MatchingIOTest.java @@ -0,0 +1,61 @@ +package org.variantsync.vevos.simulation; + +import org.junit.Assert; +import org.junit.Test; +import org.variantsync.vevos.simulation.io.data.CSV; +import org.variantsync.vevos.simulation.io.data.CSVIO; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.CodeMatching; + +import java.nio.file.Path; + +public class MatchingIOTest { + final Path FIRST_FILE = Path.of("miscutils/bc.c"); + final Path SECOND_FILE = Path.of("anotherFile.c"); + final Path THIRD_FILE = Path.of("thirdFile.c"); + + private CodeMatching loadCorrectMatching() { + VEVOS.Initialize(); + CSVIO io = new CSVIO(); + CSV csvBefore = io.load(Path.of("src/test/resources/matching/correct/code-matching.before.spl.csv")).getSuccess(); + CSV csvAfter = io.load(Path.of("src/test/resources/matching/correct/code-matching.after.spl.csv")).getSuccess(); + return CodeMatching.fromCSVs(csvBefore, csvAfter); + } + + private void loadIncorrectMatching() { + VEVOS.Initialize(); + CSVIO io = new CSVIO(); + CSV csvBefore = io.load(Path.of("src/test/resources/matching/incorrect/code-matching.before.spl.csv")).getSuccess(); + CSV csvAfter = io.load(Path.of("src/test/resources/matching/incorrect/code-matching.after.spl.csv")).getSuccess(); + CodeMatching.fromCSVs(csvBefore, csvAfter); + } + + @Test + public void allFilesLoaded() { + CodeMatching matching = loadCorrectMatching(); + Assert.assertEquals(0, (int) matching.beforeCommitMatch(FIRST_FILE, 0).get()); + Assert.assertEquals(0, (int) matching.beforeCommitMatch(SECOND_FILE, 0).get()); + Assert.assertEquals(0, (int) matching.beforeCommitMatch(THIRD_FILE, 0).get()); + } + + @Test + public void matchesAreCorrect() { + CodeMatching matching = loadCorrectMatching(); + Assert.assertEquals(1, (int) matching.beforeCommitMatch(FIRST_FILE, 1).get()); + Assert.assertEquals(3, (int) matching.beforeCommitMatch(FIRST_FILE, 3).get()); + + Assert.assertEquals(-1, (int) matching.beforeCommitMatch(SECOND_FILE, 1).get()); + Assert.assertEquals(1, (int) matching.beforeCommitMatch(SECOND_FILE, 3).get()); + Assert.assertEquals(3, (int) matching.afterCommitMatch(SECOND_FILE, 1).get()); + + Assert.assertEquals(1, (int) matching.beforeCommitMatch(THIRD_FILE, 1).get()); + Assert.assertEquals(-1, (int) matching.afterCommitMatch(THIRD_FILE, 2).get()); + Assert.assertEquals(5, (int) matching.beforeCommitMatch(THIRD_FILE, 3).get()); + Assert.assertEquals(3, (int) matching.afterCommitMatch(THIRD_FILE, 5).get()); + Assert.assertEquals(-1, (int) matching.afterCommitMatch(THIRD_FILE, 6).get()); + } + + @Test + public void findDisagreement() { + Assert.assertThrows(IllegalArgumentException.class, this::loadIncorrectMatching); + } +} diff --git a/src/test/java/org/variantsync/vevos/simulation/NewGTVariantGenerationTest.java b/src/test/java/org/variantsync/vevos/simulation/NewGTVariantGenerationTest.java new file mode 100644 index 0000000..7ce5972 --- /dev/null +++ b/src/test/java/org/variantsync/vevos/simulation/NewGTVariantGenerationTest.java @@ -0,0 +1,175 @@ +package org.variantsync.vevos.simulation; + +import de.ovgu.featureide.fm.core.analysis.cnf.formula.FeatureModelFormula; +import de.ovgu.featureide.fm.core.base.IFeatureModel; +import org.junit.Assert; +import org.junit.Test; +import org.tinylog.Logger; +import org.variantsync.functjonal.Result; +import org.variantsync.vevos.simulation.feature.Variant; +import org.variantsync.vevos.simulation.feature.config.FeatureIDEConfiguration; +import org.variantsync.vevos.simulation.feature.config.IConfiguration; +import org.variantsync.vevos.simulation.feature.config.SimpleConfiguration; +import org.variantsync.vevos.simulation.io.Resources; +import org.variantsync.vevos.simulation.io.featureide.FeatureModelIO; +import org.variantsync.vevos.simulation.io.kernelhaven.KernelHavenSPLPCIO; +import org.variantsync.vevos.simulation.io.kernelhaven.VariabilityModelLoader; +import org.variantsync.vevos.simulation.util.fide.FeatureModelUtils; +import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; +import org.variantsync.vevos.simulation.variability.pc.*; +import org.variantsync.vevos.simulation.variability.pc.options.ArtefactFilter; +import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +/** + * Tests for presence condition loading and variant generation. + */ +public class NewGTVariantGenerationTest { + final static Path BASE_PATH = Path.of("src/test/resources/new-gt-format/"); + final static Path SPL_PC = BASE_PATH.resolve("code-variability.after.spl.csv"); + final static CaseSensitivePath SPL_DIR = new CaseSensitivePath(BASE_PATH.resolve("spl")); + final static CaseSensitivePath GENERATION_DIR = new CaseSensitivePath(BASE_PATH.resolve("actual")); + final static CaseSensitivePath EXPECTED_RESULT_DIR = new CaseSensitivePath(BASE_PATH.resolve("expected")); + + @Test + public void variantA() throws IOException { + genericVariantGenerationTest("variant-A", "FEATURE_A"); + } + + @Test + public void variantAB() throws IOException { + genericVariantGenerationTest("variant-AB", "FEATURE_A", "FEATURE_B"); + } + + @Test + public void variantAD() throws IOException { + genericVariantGenerationTest("variant-AD", "FEATURE_A", "FEATURE_D"); + } + + @Test + public void variantB() throws IOException { + genericVariantGenerationTest("variant-B", "FEATURE_B"); + } + + @Test + public void variantBase() throws IOException { + genericVariantGenerationTest("variant-base"); + } + + @Test + public void variantBD() throws IOException { + genericVariantGenerationTest("variant-BD", "FEATURE_B", "FEATURE_D"); + } + + @Test + public void variantC() throws IOException { + genericVariantGenerationTest("variant-C", "FEATURE_C"); + } + + @Test + public void variantD() throws IOException { + genericVariantGenerationTest("variant-D", "FEATURE_D"); + } + + private void genericVariantGenerationTest(String variantName, String... variantFeatures) throws IOException { + Variant variant = initVariant(variantName, variantFeatures); + Artefact splPCs = loadSPLPCs(); + generateVariant(splPCs, variant); + assertCorrectGeneration(variant); + } + + private Artefact loadSPLPCs() { + KernelHavenSPLPCIO io = new KernelHavenSPLPCIO(); + return io.load(SPL_PC).expect("Was not able to load SPL PC file!"); + } + + private Variant initVariant(String name, String... features) { + return new Variant(name, new SimpleConfiguration(Arrays.asList(features))); + } + + private void generateVariant(Artefact splPCs, Variant variant) { + CaseSensitivePath targetDir = GENERATION_DIR.resolve(variant.getName()); + // Clean old files + removeFilesRecursively(targetDir.path().toFile()); + splPCs.generateVariant(variant, SPL_DIR, + targetDir, + VariantGenerationOptions.ExitOnErrorButAllowNonExistentFiles(false, ArtefactFilter.KeepAll())) + // Write ground truth + .bind(groundTruth -> Result.Try(() -> Resources.Instance().write( + Artefact.class, + groundTruth.variant(), + targetDir.resolve("ground_truth.variant.csv").path()))) + .bimap((success) -> { + Logger.debug("generated variant {}", variant.getName()); + return success; + } + , + (failure) -> { + Logger.error(failure); + throw new RuntimeException(failure); + }); + } + + private void assertCorrectGeneration(Variant variant) throws IOException { + CaseSensitivePath pathToActual = GENERATION_DIR.resolve(variant.getName()); + CaseSensitivePath pathToExpected = EXPECTED_RESULT_DIR.resolve(variant.getName()); + assertCorrectCode(pathToExpected, pathToActual); + assertCorrectPCs(pathToExpected, pathToActual); + } + + private void assertCorrectCode(CaseSensitivePath pathToExpected, CaseSensitivePath pathToActual) throws IOException { + List expectedCode = Files.readAllLines(pathToExpected.resolve("code.c").path()); + List actualCode = Files.readAllLines(pathToActual.resolve("code.c").path()); + Assert.assertTrue(compareLines(expectedCode, actualCode)); + } + + private void assertCorrectPCs(CaseSensitivePath pathToExpected, CaseSensitivePath pathToActual) throws IOException { + List expectedPCs = Files.readAllLines(pathToExpected.resolve("code-variability.variant.csv").path()); + List actualPCs = Files.readAllLines(pathToActual.resolve("ground_truth.variant.csv").path()); + Assert.assertTrue(compareLines(expectedPCs, actualPCs)); + } + + private boolean compareLines(List listA, List listB) { + boolean equal = true; + + if (listA.size() != listB.size()) { + Logger.error("Different number of lines: {} vs. {}", listA.size(), listB.size()); + equal = false; + } + + for (int lineNumber = 1; lineNumber <= Math.min(listA.size(), listB.size()); lineNumber++) { + String lineA = listA.get(lineNumber-1); + String lineB = listB.get(lineNumber-1); + if (!lineA.equals(lineB)) { + equal = false; + Logger.error("Line {} does not match: '{}' vs. '{}'", lineNumber, lineA, lineB); + } + } + + return equal; + } + + private static void removeFilesRecursively(File directory) { + if (directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + removeFilesRecursively(file); + } + } + } else if (directory.isFile()) { + boolean deleted = directory.delete(); + if (deleted) { + Logger.debug("Deleted file: {}", directory.getAbsolutePath()); + } else { + Logger.debug("Failed to delete file: {}", directory.getAbsolutePath()); + } + } + } +} diff --git a/src/test/java/org/variantsync/vevos/simulation/VariantGenerationTest.java b/src/test/java/org/variantsync/vevos/simulation/VariantGenerationTest.java index 6588bb8..4d1a045 100644 --- a/src/test/java/org/variantsync/vevos/simulation/VariantGenerationTest.java +++ b/src/test/java/org/variantsync/vevos/simulation/VariantGenerationTest.java @@ -2,17 +2,19 @@ import de.ovgu.featureide.fm.core.analysis.cnf.formula.FeatureModelFormula; import de.ovgu.featureide.fm.core.base.IFeatureModel; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.prop4j.And; import org.prop4j.Literal; import org.prop4j.Node; import org.prop4j.Or; +import org.tinylog.Logger; +import org.tinylog.Supplier; import org.variantsync.functjonal.Result; import org.variantsync.vevos.simulation.feature.Variant; import org.variantsync.vevos.simulation.feature.config.FeatureIDEConfiguration; import org.variantsync.vevos.simulation.feature.config.IConfiguration; -import org.variantsync.vevos.simulation.feature.config.SayYesToAllConfiguration; import org.variantsync.vevos.simulation.feature.sampling.FeatureIDESampler; import org.variantsync.vevos.simulation.feature.sampling.Sampler; import org.variantsync.vevos.simulation.io.ResourceLoader; @@ -20,12 +22,12 @@ import org.variantsync.vevos.simulation.io.TextIO; import org.variantsync.vevos.simulation.io.kernelhaven.KernelHavenSPLPCIO; import org.variantsync.vevos.simulation.sat.SAT; -import org.variantsync.vevos.simulation.util.Logger; import org.variantsync.vevos.simulation.util.fide.FeatureModelUtils; import org.variantsync.vevos.simulation.util.fide.bugfix.FixTrueFalse; import org.variantsync.vevos.simulation.util.io.CaseSensitivePath; import org.variantsync.vevos.simulation.util.io.PathUtils; import org.variantsync.vevos.simulation.variability.pc.*; +import org.variantsync.vevos.simulation.variability.pc.groundtruth.LineType; import org.variantsync.vevos.simulation.variability.pc.options.ArtefactFilter; import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions; @@ -43,7 +45,7 @@ private static class TestCaseData { // init in constructor CaseSensitivePath pcs, splDir, variantsDir; IFeatureModel features; - Result traces; + Result traces; public TestCaseData(final CaseSensitivePath pcs) { this.pcs = pcs; @@ -98,7 +100,7 @@ public boolean generate(final List variantsToTest, final boolean writeC } } - private static final ResourceLoader splPCLoader = new KernelHavenSPLPCIO(); + private static final KernelHavenSPLPCIO splPCLoader = new KernelHavenSPLPCIO(); private static final CaseSensitivePath resDir = CaseSensitivePath.of("src", "test", "resources", "variantgeneration"); private static CaseSensitivePath genDir; @@ -145,21 +147,42 @@ public void loadPCTest1FileCorrectly() { final Artefact expectedTrace; { // Build the expected result by hand. - final SourceCodeFile foofoo = new SourceCodeFile(FixTrueFalse.True, CaseSensitivePath.of("src", "FooFoo.cpp")); + final SourceCodeFile foofoo = new SourceCodeFile(FixTrueFalse.True, FixTrueFalse.True, CaseSensitivePath.of("src", "FooFoo.cpp")); { - final LineBasedAnnotation a = new LineBasedAnnotation(new Literal("A"), 4, 11, AnnotationStyle.Internal); - a.addTrace(new LineBasedAnnotation(new Literal("B"), 6, 8, AnnotationStyle.Internal)); - final LineBasedAnnotation tru = new LineBasedAnnotation(FixTrueFalse.True, 1, 21, AnnotationStyle.External); - tru.addTrace(a); - tru.addTrace(new LineBasedAnnotation(new Or(new And(new Literal("C"), new Literal("D")), new Literal("E")), 16, 18, AnnotationStyle.Internal)); + final LineBasedAnnotation a1 = new LineBasedAnnotation(new Literal("A"),new Literal("A"), LineType.IF, 4, 4, AnnotationStyle.Internal); + final LineBasedAnnotation a2 = new LineBasedAnnotation(new Literal("A"),new Literal("A"), LineType.ARTIFACT, 5, 5, AnnotationStyle.Internal); + final LineBasedAnnotation a3 = new LineBasedAnnotation(new Literal("A"),new Literal("A"), LineType.ARTIFACT, 9, 10, AnnotationStyle.Internal); + final LineBasedAnnotation a4 = new LineBasedAnnotation(new Literal("A"),new Literal("A"), LineType.ENDIF, 11, 11, AnnotationStyle.Internal); + + Supplier generateB = () -> { + Node left = new Literal("A"); + Node right = new Literal("B"); + return new And(left, right); + }; + final LineBasedAnnotation b1 = new LineBasedAnnotation(new Literal("B"), generateB.get(), LineType.IF, 6, 6, AnnotationStyle.Internal); + final LineBasedAnnotation b2 = new LineBasedAnnotation(new Literal("B"), generateB.get(), LineType.ARTIFACT, 7, 7, AnnotationStyle.Internal); + final LineBasedAnnotation b3 = new LineBasedAnnotation(new Literal("B"), generateB.get(), LineType.ENDIF, 8, 8, AnnotationStyle.Internal); + final LineBasedAnnotation tru = new LineBasedAnnotation(FixTrueFalse.True, FixTrueFalse.True, LineType.ROOT, 1, 21, AnnotationStyle.External); + tru.addTrace(a1); + tru.addTrace(a2); + tru.addTrace(b1); + tru.addTrace(b2); + tru.addTrace(b3); + tru.addTrace(a3); + tru.addTrace(a4); + + Supplier generateC = () -> new Or(new And(new Literal("C"), new Literal("D")), new Literal("E")); + tru.addTrace(new LineBasedAnnotation(generateC.get(), generateC.get(), LineType.IF, 16, 16, AnnotationStyle.Internal)); + tru.addTrace(new LineBasedAnnotation(generateC.get(), generateC.get(), LineType.ARTIFACT, 17, 17, AnnotationStyle.Internal)); + tru.addTrace(new LineBasedAnnotation(generateC.get(), generateC.get(), LineType.ENDIF, 18, 18, AnnotationStyle.Internal)); foofoo.addTrace(tru); } - final SourceCodeFile bar = new SourceCodeFile(new Literal("A"), CaseSensitivePath.of("src", "foo", "bar.cpp")); + final SourceCodeFile bar = new SourceCodeFile(new Literal("A"), new Literal("A"), CaseSensitivePath.of("src", "foo", "bar.cpp")); { // This is a challenging case for the importer. // We can not differentiate if a block starting at line 1 is an external annotation by Kernelhaven or an actual macro. - bar.addTrace(new LineBasedAnnotation(FixTrueFalse.False, 1, 4, AnnotationStyle.Internal)); + bar.addTrace(new LineBasedAnnotation(FixTrueFalse.False, FixTrueFalse.False, LineType.ROOT, 1, 4, AnnotationStyle.Internal)); } expectedTrace = new SyntheticArtefactTreeNode<>(Arrays.asList(foofoo, bar)); @@ -207,16 +230,18 @@ public void idempotentReadWriteOfPCFiles() throws Resources.ResourceIOException, readFromAndDirectlyWriteTo(intermediatePath, outputPath); // assert that text at intermediatePath is the same as at outputPath - assert TextIO.readAsString(intermediatePath.path()).equals(TextIO.readAsString(outputPath.path())); + Assert.assertEquals(TextIO.readAsString(intermediatePath.path()), TextIO.readAsString(outputPath.path())); } } @Test public void testPCQuery() { - assert pcTest1.traces.isSuccess(); + if (pcTest1.traces.isFailure()) { + throw new RuntimeException(pcTest1.traces.getFailure()); + } final Result result = pcTest1.traces.getSuccess().getPresenceConditionOf(CaseSensitivePath.of("src", "FooFoo.cpp"), 7); - Logger.log(result); + Logger.debug(result); assert result.isSuccess(); assert SAT.equivalent(result.getSuccess(), new And(new Literal("A"), new Literal("B"))); } diff --git a/src/test/resources/gts/gt-1.spl.csv b/src/test/resources/gts/gt-1.spl.csv new file mode 100644 index 0000000..028d94d --- /dev/null +++ b/src/test/resources/gts/gt-1.spl.csv @@ -0,0 +1,649 @@ +Path;File Condition;Block Condition;Presence Condition;start;end +miscutils/bc.c;1;1;1;1;7583 +miscutils/bc.c;1;0;0;166;166 +miscutils/bc.c;1;ENABLE_FEATURE_DC_SMALL;1 && ENABLE_FEATURE_DC_SMALL;167;167 +miscutils/bc.c;1;0;0;168;168 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;169;169 +miscutils/bc.c;1;0;0;170;170 +miscutils/bc.c;1;0;!ENABLE_FEATURE_DC_SMALL && 1 && 0;171;171 +miscutils/bc.c;1;0;0;172;172 +miscutils/bc.c;1;1;1 && !ENABLE_FEATURE_DC_SMALL && 1;173;173 +miscutils/bc.c;1;0;0;174;174 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;175;214 +miscutils/bc.c;1;0;0;215;215 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;216;219 +miscutils/bc.c;1;0;0;220;220 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;221;241 +miscutils/bc.c;1;0;0;242;242 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;243;248 +miscutils/bc.c;1;0;0;249;249 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;250;270 +miscutils/bc.c;1;0;0;271;271 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;272;280 +miscutils/bc.c;1;0;0;281;281 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;282;285 +miscutils/bc.c;1;0;0;286;286 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;287;309 +miscutils/bc.c;1;0;0;310;310 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;311;442 +miscutils/bc.c;1;0;0;443;443 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;444;469 +miscutils/bc.c;1;0;0;470;470 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;471;472 +miscutils/bc.c;1;0;0;473;473 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;474;525 +miscutils/bc.c;1;0;0;526;526 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;527;527 +miscutils/bc.c;1;0;0;528;528 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS || ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && (ENABLE_FEATURE_BC_SIGNALS || ENABLE_FEATURE_CLEAN_UP);529;529 +miscutils/bc.c;1;0;0;530;530 +miscutils/bc.c;1;!ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_CLEAN_UP && !ENABLE_FEATURE_DC_SMALL && 1;531;531 +miscutils/bc.c;1;0;0;532;532 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;533;627 +miscutils/bc.c;1;0;0;628;628 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;629;629 +miscutils/bc.c;1;0;0;630;630 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;631;660 +miscutils/bc.c;1;0;0;661;661 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;662;662 +miscutils/bc.c;1;0;0;663;663 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;664;694 +miscutils/bc.c;1;0;0;695;695 +miscutils/bc.c;1;INT_MAX__EQ__2147483647;!ENABLE_FEATURE_DC_SMALL && 1 && INT_MAX__EQ__2147483647;696;696 +miscutils/bc.c;1;0;0;697;697 +miscutils/bc.c;1;INT_MAX__EQ__9223372036854775807 && !INT_MAX__EQ__2147483647;INT_MAX__EQ__9223372036854775807 && !INT_MAX__EQ__2147483647 && !ENABLE_FEATURE_DC_SMALL && 1;698;698 +miscutils/bc.c;1;0;0;699;699 +miscutils/bc.c;1;!INT_MAX__EQ__9223372036854775807 && !INT_MAX__EQ__2147483647;!INT_MAX__EQ__9223372036854775807 && !INT_MAX__EQ__2147483647 && !ENABLE_FEATURE_DC_SMALL && 1;700;700 +miscutils/bc.c;1;0;0;701;701 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;702;702 +miscutils/bc.c;1;0;0;703;703 +miscutils/bc.c;1;UINT_MAX__EQ__4294967295;!ENABLE_FEATURE_DC_SMALL && 1 && UINT_MAX__EQ__4294967295;704;705 +miscutils/bc.c;1;0;0;706;706 +miscutils/bc.c;1;UINT_MAX__EQ__18446744073709551615 && !UINT_MAX__EQ__4294967295;UINT_MAX__EQ__18446744073709551615 && !UINT_MAX__EQ__4294967295 && !ENABLE_FEATURE_DC_SMALL && 1;707;708 +miscutils/bc.c;1;0;0;709;709 +miscutils/bc.c;1;!UINT_MAX__EQ__18446744073709551615 && !UINT_MAX__EQ__4294967295;!UINT_MAX__EQ__18446744073709551615 && !UINT_MAX__EQ__4294967295 && !ENABLE_FEATURE_DC_SMALL && 1;710;710 +miscutils/bc.c;1;0;0;711;711 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;712;729 +miscutils/bc.c;1;0;0;730;730 +miscutils/bc.c;1;ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_EDITING;731;731 +miscutils/bc.c;1;0;0;732;732 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;733;743 +miscutils/bc.c;1;0;0;744;744 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS;745;746 +miscutils/bc.c;1;0;0;747;747 +miscutils/bc.c;1;!ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_DC_SMALL && 1;748;749 +miscutils/bc.c;1;0;0;750;751 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;752;752 +miscutils/bc.c;1;0;0;753;753 +miscutils/bc.c;1;!ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_CLEAN_UP && !ENABLE_FEATURE_DC_SMALL && 1;754;754 +miscutils/bc.c;1;0;0;755;755 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;756;758 +miscutils/bc.c;1;0;0;759;759 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;760;775 +miscutils/bc.c;1;0;0;776;776 +miscutils/bc.c;1;ULONG_MAX__GT__0xffffffff;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ULONG_MAX__GT__0xffffffff;777;778 +miscutils/bc.c;1;0;0;779;779 +miscutils/bc.c;1;!ULONG_MAX__GT__0xffffffff;!ULONG_MAX__GT__0xffffffff && !ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;780;786 +miscutils/bc.c;1;0;0;787;787 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;788;826 +miscutils/bc.c;1;0;0;827;827 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;828;828 +miscutils/bc.c;1;0;0;829;829 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;830;895 +miscutils/bc.c;1;0;0;896;896 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;897;908 +miscutils/bc.c;1;0;0;909;909 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS || ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && (ENABLE_FEATURE_BC_SIGNALS || ENABLE_FEATURE_CLEAN_UP);910;914 +miscutils/bc.c;1;0;0;915;915 +miscutils/bc.c;1;!ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_CLEAN_UP && !ENABLE_FEATURE_DC_SMALL && 1;916;920 +miscutils/bc.c;1;0;0;921;921 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;922;989 +miscutils/bc.c;1;0;0;990;990 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;991;996 +miscutils/bc.c;1;0;0;997;997 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;998;1005 +miscutils/bc.c;1;0;0;1006;1006 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;1007;1012 +miscutils/bc.c;1;0;0;1013;1013 +miscutils/bc.c;1;!ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_CLEAN_UP && !ENABLE_FEATURE_DC_SMALL && 1;1014;1014 +miscutils/bc.c;1;0;0;1015;1015 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1016;1052 +miscutils/bc.c;1;0;0;1053;1053 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;1054;1072 +miscutils/bc.c;1;0;0;1073;1073 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1074;1107 +miscutils/bc.c;1;0;0;1108;1108 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;1109;1124 +miscutils/bc.c;1;0;0;1125;1125 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1126;1224 +miscutils/bc.c;1;0;0;1225;1225 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS && ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS && ENABLE_FEATURE_EDITING;1226;1239 +miscutils/bc.c;1;0;0;1240;1240 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1241;1317 +miscutils/bc.c;1;0;0;1318;1318 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;1319;1324 +miscutils/bc.c;1;0;0;1325;1325 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1326;1343 +miscutils/bc.c;1;0;0;1344;1344 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS;1345;1352 +miscutils/bc.c;1;0;0;1353;1353 +miscutils/bc.c;1;ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS && ENABLE_FEATURE_EDITING;1354;1372 +miscutils/bc.c;1;0;0;1373;1374 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1375;1382 +miscutils/bc.c;1;0;0;1383;1383 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING;1384;1389 +miscutils/bc.c;1;0;0;1390;1390 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1391;1525 +miscutils/bc.c;1;0;0;1526;1526 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;1527;1527 +miscutils/bc.c;1;0;0;1528;1528 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1529;1709 +miscutils/bc.c;1;0;0;1710;1710 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;1711;1711 +miscutils/bc.c;1;0;0;1712;1712 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1713;1724 +miscutils/bc.c;1;0;0;1725;1725 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;1726;1726 +miscutils/bc.c;1;0;0;1727;1727 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1728;1863 +miscutils/bc.c;1;0;0;1864;1864 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;1865;1865 +miscutils/bc.c;1;0;0;1866;1866 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1867;1907 +miscutils/bc.c;1;0;0;1908;1908 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS;1909;1911 +miscutils/bc.c;1;0;0;1912;1912 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;1913;2013 +miscutils/bc.c;1;0;0;2014;2014 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2015;2015 +miscutils/bc.c;1;0;0;2016;2016 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2017;2074 +miscutils/bc.c;1;0;0;2075;2075 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS;2076;2082 +miscutils/bc.c;1;0;0;2083;2083 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2084;2090 +miscutils/bc.c;1;0;0;2091;2091 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2092;2092 +miscutils/bc.c;1;0;0;2093;2093 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2094;2130 +miscutils/bc.c;1;0;0;2131;2131 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2132;2132 +miscutils/bc.c;1;0;0;2133;2133 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2134;2146 +miscutils/bc.c;1;0;0;2147;2147 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2148;2148 +miscutils/bc.c;1;0;0;2149;2149 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2150;2240 +miscutils/bc.c;1;0;0;2241;2241 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2242;2242 +miscutils/bc.c;1;0;0;2243;2243 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2244;2281 +miscutils/bc.c;1;0;0;2282;2282 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2283;2283 +miscutils/bc.c;1;0;0;2284;2284 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2285;2431 +miscutils/bc.c;1;0;0;2432;2432 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2433;2433 +miscutils/bc.c;1;0;0;2434;2434 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2435;2536 +miscutils/bc.c;1;0;0;2537;2537 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2538;2538 +miscutils/bc.c;1;0;0;2539;2539 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2540;2565 +miscutils/bc.c;1;0;0;2566;2566 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2567;2567 +miscutils/bc.c;1;0;0;2568;2568 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2569;2569 +miscutils/bc.c;1;0;0;2570;2570 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;2571;2621 +miscutils/bc.c;1;0;0;2622;2622 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;2623;2623 +miscutils/bc.c;1;0;0;2624;2625 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2626;2626 +miscutils/bc.c;1;0;0;2627;2627 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;2628;2644 +miscutils/bc.c;1;0;0;2645;2645 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;2646;2646 +miscutils/bc.c;1;0;0;2647;2648 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2649;2714 +miscutils/bc.c;1;0;0;2715;2715 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;2716;2749 +miscutils/bc.c;1;0;0;2750;2750 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2751;2864 +miscutils/bc.c;1;0;0;2865;2865 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2866;2866 +miscutils/bc.c;1;0;0;2867;2867 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2868;2883 +miscutils/bc.c;1;0;0;2884;2884 +miscutils/bc.c;1;0;!ENABLE_FEATURE_DC_SMALL && 1 && 0;2885;2889 +miscutils/bc.c;1;0;0;2890;2890 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2891;2952 +miscutils/bc.c;1;0;0;2953;2953 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2954;2954 +miscutils/bc.c;1;0;0;2955;2955 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2956;2964 +miscutils/bc.c;1;0;0;2965;2965 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;2966;2966 +miscutils/bc.c;1;0;0;2967;2967 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;2968;2968 +miscutils/bc.c;1;0;0;2969;2969 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;2970;3010 +miscutils/bc.c;1;0;0;3011;3011 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3012;3012 +miscutils/bc.c;1;0;0;3013;3013 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3014;3043 +miscutils/bc.c;1;0;0;3044;3044 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3045;3045 +miscutils/bc.c;1;0;0;3046;3046 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3047;3088 +miscutils/bc.c;1;0;0;3089;3089 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3090;3090 +miscutils/bc.c;1;0;0;3091;3091 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3092;3278 +miscutils/bc.c;1;0;0;3279;3279 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;3280;3280 +miscutils/bc.c;1;0;0;3281;3281 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;3282;3299 +miscutils/bc.c;1;0;0;3300;3300 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;3301;3301 +miscutils/bc.c;1;0;0;3302;3302 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;3303;3338 +miscutils/bc.c;1;0;0;3339;3339 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;3340;3340 +miscutils/bc.c;1;0;0;3341;3341 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;3342;3423 +miscutils/bc.c;1;0;0;3424;3424 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;3425;3508 +miscutils/bc.c;1;0;0;3509;3509 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;3510;3510 +miscutils/bc.c;1;0;0;3511;3511 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;3512;3579 +miscutils/bc.c;1;0;0;3580;3580 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3581;3595 +miscutils/bc.c;1;0;0;3596;3596 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3597;3599 +miscutils/bc.c;1;0;0;3600;3600 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3601;3627 +miscutils/bc.c;1;0;0;3628;3628 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3629;3629 +miscutils/bc.c;1;0;0;3630;3630 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3631;3654 +miscutils/bc.c;1;0;0;3655;3655 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3656;3656 +miscutils/bc.c;1;0;0;3657;3657 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3658;3685 +miscutils/bc.c;1;0;0;3686;3686 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3687;3687 +miscutils/bc.c;1;0;0;3688;3688 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3689;3724 +miscutils/bc.c;1;0;0;3725;3725 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3726;3726 +miscutils/bc.c;1;0;0;3727;3727 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3728;3777 +miscutils/bc.c;1;0;0;3778;3778 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3779;3779 +miscutils/bc.c;1;0;0;3780;3780 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3781;3797 +miscutils/bc.c;1;0;0;3798;3798 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3799;3799 +miscutils/bc.c;1;0;0;3800;3800 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3801;3825 +miscutils/bc.c;1;0;0;3826;3826 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3827;3827 +miscutils/bc.c;1;0;0;3828;3828 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3829;3856 +miscutils/bc.c;1;0;0;3857;3857 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3858;3858 +miscutils/bc.c;1;0;0;3859;3859 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3860;3916 +miscutils/bc.c;1;0;0;3917;3917 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3918;3918 +miscutils/bc.c;1;0;0;3919;3919 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3920;3945 +miscutils/bc.c;1;0;0;3946;3946 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3947;3947 +miscutils/bc.c;1;0;0;3948;3948 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3949;3960 +miscutils/bc.c;1;0;0;3961;3961 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;3962;3962 +miscutils/bc.c;1;0;0;3963;3963 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;3964;4001 +miscutils/bc.c;1;0;0;4002;4002 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4003;4003 +miscutils/bc.c;1;0;0;4004;4004 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4005;4039 +miscutils/bc.c;1;0;0;4040;4040 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4041;4041 +miscutils/bc.c;1;0;0;4042;4042 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4043;4110 +miscutils/bc.c;1;0;0;4111;4111 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4112;4112 +miscutils/bc.c;1;0;0;4113;4113 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4114;4166 +miscutils/bc.c;1;0;0;4167;4167 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4168;4168 +miscutils/bc.c;1;0;0;4169;4169 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4170;4191 +miscutils/bc.c;1;0;0;4192;4192 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4193;4193 +miscutils/bc.c;1;0;0;4194;4194 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4195;4230 +miscutils/bc.c;1;0;0;4231;4231 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4232;4232 +miscutils/bc.c;1;0;0;4233;4233 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4234;4308 +miscutils/bc.c;1;0;0;4309;4309 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4310;4310 +miscutils/bc.c;1;0;0;4311;4311 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4312;4347 +miscutils/bc.c;1;0;0;4348;4348 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4349;4349 +miscutils/bc.c;1;0;0;4350;4350 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4351;4425 +miscutils/bc.c;1;0;0;4426;4426 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4427;4427 +miscutils/bc.c;1;0;0;4428;4428 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4429;4484 +miscutils/bc.c;1;0;0;4485;4485 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4486;4486 +miscutils/bc.c;1;0;0;4487;4487 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4488;4514 +miscutils/bc.c;1;0;0;4515;4515 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4516;4516 +miscutils/bc.c;1;0;0;4517;4517 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4518;4632 +miscutils/bc.c;1;0;0;4633;4633 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4634;4634 +miscutils/bc.c;1;0;0;4635;4635 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4636;4656 +miscutils/bc.c;1;0;0;4657;4657 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4658;4658 +miscutils/bc.c;1;0;0;4659;4659 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4660;4930 +miscutils/bc.c;1;0;0;4931;4931 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4932;4932 +miscutils/bc.c;1;0;0;4933;4933 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4934;4938 +miscutils/bc.c;1;0;0;4939;4939 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;4940;4940 +miscutils/bc.c;1;0;0;4941;4941 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;4942;4942 +miscutils/bc.c;1;0;0;4943;4943 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;4944;4944 +miscutils/bc.c;1;0;0;4945;4945 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;4946;4962 +miscutils/bc.c;1;0;0;4963;4963 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;4964;4964 +miscutils/bc.c;1;0;0;4965;4965 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;4966;4982 +miscutils/bc.c;1;0;0;4983;4983 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;4984;4984 +miscutils/bc.c;1;0;0;4985;4985 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;4986;5004 +miscutils/bc.c;1;0;0;5005;5005 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5006;5006 +miscutils/bc.c;1;0;0;5007;5007 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5008;5031 +miscutils/bc.c;1;0;0;5032;5032 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5033;5033 +miscutils/bc.c;1;0;0;5034;5034 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5035;5104 +miscutils/bc.c;1;0;0;5105;5105 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5106;5106 +miscutils/bc.c;1;0;0;5107;5107 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5108;5131 +miscutils/bc.c;1;0;0;5132;5132 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5133;5133 +miscutils/bc.c;1;0;0;5134;5134 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5135;5151 +miscutils/bc.c;1;0;0;5152;5152 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5153;5153 +miscutils/bc.c;1;0;0;5154;5154 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5155;5155 +miscutils/bc.c;1;0;0;5156;5156 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5157;5165 +miscutils/bc.c;1;0;0;5166;5166 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5167;5167 +miscutils/bc.c;1;0;0;5168;5168 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5169;5267 +miscutils/bc.c;1;0;0;5268;5268 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5269;5269 +miscutils/bc.c;1;0;0;5270;5270 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5271;5307 +miscutils/bc.c;1;0;0;5308;5308 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5309;5309 +miscutils/bc.c;1;0;0;5310;5310 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5311;5335 +miscutils/bc.c;1;0;0;5336;5336 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5337;5337 +miscutils/bc.c;1;0;0;5338;5338 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5339;5367 +miscutils/bc.c;1;0;0;5368;5368 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5369;5369 +miscutils/bc.c;1;0;0;5370;5370 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5371;5424 +miscutils/bc.c;1;0;0;5425;5425 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5426;5426 +miscutils/bc.c;1;0;0;5427;5427 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5428;5457 +miscutils/bc.c;1;0;0;5458;5458 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5459;5464 +miscutils/bc.c;1;0;0;5465;5465 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5466;5519 +miscutils/bc.c;1;0;0;5520;5520 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5521;5526 +miscutils/bc.c;1;0;0;5527;5527 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5528;5635 +miscutils/bc.c;1;0;0;5636;5636 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5637;5637 +miscutils/bc.c;1;0;0;5638;5638 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5639;5668 +miscutils/bc.c;1;0;0;5669;5669 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5670;5670 +miscutils/bc.c;1;0;0;5671;5671 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5672;5672 +miscutils/bc.c;1;0;0;5673;5673 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5674;5677 +miscutils/bc.c;1;0;0;5678;5678 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5679;5679 +miscutils/bc.c;1;0;0;5680;5681 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5682;5704 +miscutils/bc.c;1;0;0;5705;5705 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5706;5706 +miscutils/bc.c;1;0;0;5707;5707 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5708;5752 +miscutils/bc.c;1;0;0;5753;5753 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5754;5754 +miscutils/bc.c;1;0;0;5755;5755 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5756;5773 +miscutils/bc.c;1;0;0;5774;5774 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5775;5775 +miscutils/bc.c;1;0;0;5776;5776 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5777;5824 +miscutils/bc.c;1;0;0;5825;5825 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5826;5826 +miscutils/bc.c;1;0;0;5827;5827 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5828;5828 +miscutils/bc.c;1;0;0;5829;5829 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5830;5853 +miscutils/bc.c;1;0;0;5854;5854 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;5855;5855 +miscutils/bc.c;1;0;0;5856;5857 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5858;5873 +miscutils/bc.c;1;0;0;5874;5874 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5875;5878 +miscutils/bc.c;1;0;0;5879;5879 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5880;5900 +miscutils/bc.c;1;0;0;5901;5901 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5902;5902 +miscutils/bc.c;1;0;0;5903;5903 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5904;5917 +miscutils/bc.c;1;0;0;5918;5918 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;5919;5929 +miscutils/bc.c;1;0;0;5930;5930 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5931;5938 +miscutils/bc.c;1;0;0;5939;5939 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;5940;5949 +miscutils/bc.c;1;0;0;5950;5950 +miscutils/bc.c;1;!ENABLE_BC;!ENABLE_BC && !ENABLE_FEATURE_DC_SMALL && 1;5951;5951 +miscutils/bc.c;1;0;0;5952;5952 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5953;5994 +miscutils/bc.c;1;0;0;5995;5995 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;5996;5996 +miscutils/bc.c;1;0;0;5997;5997 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;5998;5998 +miscutils/bc.c;1;0;0;5999;5999 +miscutils/bc.c;1;!ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && !ENABLE_DC;6000;6002 +miscutils/bc.c;1;0;0;6003;6003 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6004;6012 +miscutils/bc.c;1;0;0;6013;6013 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6014;6042 +miscutils/bc.c;1;0;0;6043;6043 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6044;6048 +miscutils/bc.c;1;0;0;6049;6049 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6050;6050 +miscutils/bc.c;1;0;0;6051;6051 +miscutils/bc.c;1;!ERRORS_ARE_FATAL;!ERRORS_ARE_FATAL && !ENABLE_FEATURE_DC_SMALL && 1;6052;6052 +miscutils/bc.c;1;0;0;6053;6053 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6054;6090 +miscutils/bc.c;1;0;0;6091;6091 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6092;6092 +miscutils/bc.c;1;0;0;6093;6093 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6094;6094 +miscutils/bc.c;1;0;0;6095;6095 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6096;6127 +miscutils/bc.c;1;0;0;6128;6128 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;6129;6129 +miscutils/bc.c;1;0;0;6130;6130 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6131;6185 +miscutils/bc.c;1;0;0;6186;6186 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;6187;6187 +miscutils/bc.c;1;0;0;6188;6188 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6189;6232 +miscutils/bc.c;1;0;0;6233;6233 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ERRORS_ARE_FATAL;6234;6234 +miscutils/bc.c;1;0;0;6235;6236 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6237;6270 +miscutils/bc.c;1;0;0;6271;6271 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6272;6273 +miscutils/bc.c;1;0;0;6274;6274 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6275;6278 +miscutils/bc.c;1;0;0;6279;6279 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6280;6282 +miscutils/bc.c;1;0;0;6283;6284 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6285;6291 +miscutils/bc.c;1;0;0;6292;6292 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6293;6300 +miscutils/bc.c;1;0;0;6301;6301 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6302;6302 +miscutils/bc.c;1;0;0;6303;6303 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6304;6304 +miscutils/bc.c;1;0;0;6305;6305 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6306;6331 +miscutils/bc.c;1;0;0;6332;6332 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6333;6333 +miscutils/bc.c;1;0;0;6334;6334 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6335;6379 +miscutils/bc.c;1;0;0;6380;6380 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6381;6381 +miscutils/bc.c;1;0;0;6382;6382 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6383;6464 +miscutils/bc.c;1;0;0;6465;6465 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6466;6466 +miscutils/bc.c;1;0;0;6467;6467 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6468;6493 +miscutils/bc.c;1;0;0;6494;6494 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6495;6495 +miscutils/bc.c;1;0;0;6496;6496 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6497;6521 +miscutils/bc.c;1;0;0;6522;6522 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6523;6523 +miscutils/bc.c;1;0;0;6524;6524 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6525;6625 +miscutils/bc.c;1;0;0;6626;6626 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC && ERRORS_ARE_FATAL;6627;6627 +miscutils/bc.c;1;0;0;6628;6629 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6630;6693 +miscutils/bc.c;1;0;0;6694;6694 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6695;6725 +miscutils/bc.c;1;0;0;6726;6726 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6727;6802 +miscutils/bc.c;1;0;0;6803;6803 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;6804;6809 +miscutils/bc.c;1;0;0;6810;6810 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6811;6813 +miscutils/bc.c;1;0;0;6814;6814 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;6815;6883 +miscutils/bc.c;1;0;0;6884;6884 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6885;6899 +miscutils/bc.c;1;0;0;6900;6900 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6901;6901 +miscutils/bc.c;1;0;0;6902;6902 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6903;6939 +miscutils/bc.c;1;0;0;6940;6940 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6941;6941 +miscutils/bc.c;1;0;0;6942;6942 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6943;6974 +miscutils/bc.c;1;0;0;6975;6975 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;6976;6976 +miscutils/bc.c;1;0;0;6977;6977 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;6978;7068 +miscutils/bc.c;1;0;0;7069;7069 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;7070;7070 +miscutils/bc.c;1;0;0;7071;7071 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7072;7072 +miscutils/bc.c;1;0;0;7073;7073 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;7074;7086 +miscutils/bc.c;1;0;0;7087;7087 +miscutils/bc.c;1;ENABLE_FEATURE_BC_LONG_OPTIONS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC && ENABLE_FEATURE_BC_LONG_OPTIONS;7088;7095 +miscutils/bc.c;1;0;0;7096;7096 +miscutils/bc.c;1;!ENABLE_FEATURE_BC_LONG_OPTIONS;!ENABLE_FEATURE_BC_LONG_OPTIONS && !ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;7097;7097 +miscutils/bc.c;1;0;0;7098;7098 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;7099;7313 +miscutils/bc.c;1;0;0;7314;7314 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7315;7320 +miscutils/bc.c;1;0;0;7321;7321 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;7322;7337 +miscutils/bc.c;1;0;0;7338;7338 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7339;7357 +miscutils/bc.c;1;0;0;7358;7358 +miscutils/bc.c;1;ERRORS_ARE_FATAL;!ENABLE_FEATURE_DC_SMALL && 1 && ERRORS_ARE_FATAL;7359;7359 +miscutils/bc.c;1;0;0;7360;7360 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7361;7361 +miscutils/bc.c;1;0;0;7362;7362 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;7363;7367 +miscutils/bc.c;1;0;0;7368;7368 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP && ENABLE_DC;7369;7369 +miscutils/bc.c;1;0;0;7370;7370 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;7371;7392 +miscutils/bc.c;1;0;0;7393;7393 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7394;7415 +miscutils/bc.c;1;0;0;7416;7416 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;7417;7418 +miscutils/bc.c;1;0;0;7419;7419 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7420;7450 +miscutils/bc.c;1;0;0;7451;7451 +miscutils/bc.c;1;ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_EDITING;7452;7452 +miscutils/bc.c;1;0;0;7453;7453 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7454;7463 +miscutils/bc.c;1;0;0;7464;7464 +miscutils/bc.c;1;ENABLE_FEATURE_BC_SIGNALS;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_BC_SIGNALS;7465;7484 +miscutils/bc.c;1;0;0;7485;7485 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7486;7493 +miscutils/bc.c;1;0;0;7494;7494 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;7495;7497 +miscutils/bc.c;1;0;0;7498;7498 +miscutils/bc.c;1;ENABLE_FEATURE_EDITING;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_EDITING;7499;7499 +miscutils/bc.c;1;0;0;7500;7500 +miscutils/bc.c;1;ENABLE_FEATURE_CLEAN_UP;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_FEATURE_CLEAN_UP;7501;7501 +miscutils/bc.c;1;0;0;7502;7502 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7503;7505 +miscutils/bc.c;1;0;0;7506;7506 +miscutils/bc.c;1;ENABLE_BC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_BC;7507;7522 +miscutils/bc.c;1;0;0;7523;7523 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7524;7524 +miscutils/bc.c;1;0;0;7525;7525 +miscutils/bc.c;1;ENABLE_DC;!ENABLE_FEATURE_DC_SMALL && 1 && ENABLE_DC;7526;7579 +miscutils/bc.c;1;0;0;7580;7580 +miscutils/bc.c;1;!ENABLE_FEATURE_DC_SMALL;!ENABLE_FEATURE_DC_SMALL && 1;7581;7581 +miscutils/bc.c;1;0;0;7582;7582 diff --git a/src/test/resources/matching/correct/code-matching.after.spl.csv b/src/test/resources/matching/correct/code-matching.after.spl.csv new file mode 100644 index 0000000..5ff03d1 --- /dev/null +++ b/src/test/resources/matching/correct/code-matching.after.spl.csv @@ -0,0 +1,14 @@ +Path;Line Number; Counterpart +miscutils/bc.c;0;0 +miscutils/bc.c;1;1 +miscutils/bc.c;2;2 +miscutils/bc.c;3;3 +anotherFile.c;0;0 +anotherFile.c;1;3 +thirdFile.c;0;0 +thirdFile.c;1;1 +thirdFile.c;2;-1 +thirdFile.c;3;-1 +thirdFile.c;4;2 +thirdFile.c;5;3 +thirdFile.c;6;-1 \ No newline at end of file diff --git a/src/test/resources/matching/correct/code-matching.before.spl.csv b/src/test/resources/matching/correct/code-matching.before.spl.csv new file mode 100644 index 0000000..f883c68 --- /dev/null +++ b/src/test/resources/matching/correct/code-matching.before.spl.csv @@ -0,0 +1,13 @@ +Path;Line Number; Counterpart +miscutils/bc.c;0;0 +miscutils/bc.c;1;1 +miscutils/bc.c;2;2 +miscutils/bc.c;3;3 +anotherFile.c;0;0 +anotherFile.c;1;-1 +anotherFile.c;2;-1 +anotherFile.c;3;1 +thirdFile.c;0;0 +thirdFile.c;1;1 +thirdFile.c;2;4 +thirdFile.c;3;5 \ No newline at end of file diff --git a/src/test/resources/matching/incorrect/code-matching.after.spl.csv b/src/test/resources/matching/incorrect/code-matching.after.spl.csv new file mode 100644 index 0000000..69f5e6e --- /dev/null +++ b/src/test/resources/matching/incorrect/code-matching.after.spl.csv @@ -0,0 +1,14 @@ +Path;Line Number; Counterpart +miscutils/bc.c;0;0 +miscutils/bc.c;1;1 +miscutils/bc.c;2;2 +miscutils/bc.c;3;3 +anotherFile.c;0;0 +anotherFile.c;1;2 +thirdFile.c;0;0 +thirdFile.c;1;1 +thirdFile.c;2;-1 +thirdFile.c;3;-1 +thirdFile.c;4;2 +thirdFile.c;5;3 +thirdFile.c;6;-1 \ No newline at end of file diff --git a/src/test/resources/matching/incorrect/code-matching.before.spl.csv b/src/test/resources/matching/incorrect/code-matching.before.spl.csv new file mode 100644 index 0000000..f883c68 --- /dev/null +++ b/src/test/resources/matching/incorrect/code-matching.before.spl.csv @@ -0,0 +1,13 @@ +Path;Line Number; Counterpart +miscutils/bc.c;0;0 +miscutils/bc.c;1;1 +miscutils/bc.c;2;2 +miscutils/bc.c;3;3 +anotherFile.c;0;0 +anotherFile.c;1;-1 +anotherFile.c;2;-1 +anotherFile.c;3;1 +thirdFile.c;0;0 +thirdFile.c;1;1 +thirdFile.c;2;4 +thirdFile.c;3;5 \ No newline at end of file diff --git a/src/test/resources/new-gt-format/actual/.gitignore b/src/test/resources/new-gt-format/actual/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/src/test/resources/new-gt-format/actual/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/src/test/resources/new-gt-format/code-variability.after.spl.csv b/src/test/resources/new-gt-format/code-variability.after.spl.csv new file mode 100644 index 0000000..954d2a6 --- /dev/null +++ b/src/test/resources/new-gt-format/code-variability.after.spl.csv @@ -0,0 +1,12 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;1;1;1;ROOT;1;18 +code.c;1;FEATURE_A;FEATURE_A;if;2;2 +code.c;1;FEATURE_A;FEATURE_A;artifact;3;4 +code.c;1;!FEATURE_A && (FEATURE_B || FEATURE_C);!FEATURE_A && (FEATURE_B || FEATURE_C);elif;5;6 +code.c;1;!FEATURE_A && (FEATURE_B || FEATURE_C);!FEATURE_A && (FEATURE_B || FEATURE_C);artifact;7;8 +code.c;1;!FEATURE_A && !(FEATURE_B || FEATURE_C);!FEATURE_A && !(FEATURE_B || FEATURE_C);else;9;9 +code.c;1;!FEATURE_A && !(FEATURE_B || FEATURE_C);!FEATURE_A && !(FEATURE_B || FEATURE_C);artifact;10;11 +code.c;1;!FEATURE_A && !(FEATURE_B || FEATURE_C);!FEATURE_A && !(FEATURE_B || FEATURE_C);endif;12;12 +code.c;1;FEATURE_D;FEATURE_D;if;13;13 +code.c;1;FEATURE_D;FEATURE_D;artifact;14;15 +code.c;1;FEATURE_D;FEATURE_D;endif;16;16 diff --git a/src/test/resources/new-gt-format/expected/variant-A/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-A/code-variability.variant.csv new file mode 100644 index 0000000..dd5245e --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-A/code-variability.variant.csv @@ -0,0 +1,3 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;5 +code.c;True;FEATURE_A;FEATURE_A;artifact;2;3 diff --git a/src/test/resources/new-gt-format/expected/variant-A/code.c b/src/test/resources/new-gt-format/expected/variant-A/code.c new file mode 100644 index 0000000..0628e58 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-A/code.c @@ -0,0 +1,5 @@ +// This is a test for the new ground truth format +int a = 0; +int b = 0; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-AB/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-AB/code-variability.variant.csv new file mode 100644 index 0000000..dd5245e --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-AB/code-variability.variant.csv @@ -0,0 +1,3 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;5 +code.c;True;FEATURE_A;FEATURE_A;artifact;2;3 diff --git a/src/test/resources/new-gt-format/expected/variant-AB/code.c b/src/test/resources/new-gt-format/expected/variant-AB/code.c new file mode 100644 index 0000000..0628e58 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-AB/code.c @@ -0,0 +1,5 @@ +// This is a test for the new ground truth format +int a = 0; +int b = 0; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-AD/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-AD/code-variability.variant.csv new file mode 100644 index 0000000..1bb65be --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-AD/code-variability.variant.csv @@ -0,0 +1,4 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;7 +code.c;True;FEATURE_A;FEATURE_A;artifact;2;3 +code.c;True;FEATURE_D;FEATURE_D;artifact;4;5 diff --git a/src/test/resources/new-gt-format/expected/variant-AD/code.c b/src/test/resources/new-gt-format/expected/variant-AD/code.c new file mode 100644 index 0000000..b169185 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-AD/code.c @@ -0,0 +1,7 @@ +// This is a test for the new ground truth format +int a = 0; +int b = 0; +int g = 0; +int h = 0; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-B/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-B/code-variability.variant.csv new file mode 100644 index 0000000..90d7e98 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-B/code-variability.variant.csv @@ -0,0 +1,3 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;5 +code.c;True;(!FEATURE_A && (FEATURE_B || FEATURE_C));(!FEATURE_A && (FEATURE_B || FEATURE_C));artifact;2;3 diff --git a/src/test/resources/new-gt-format/expected/variant-B/code.c b/src/test/resources/new-gt-format/expected/variant-B/code.c new file mode 100644 index 0000000..403bc69 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-B/code.c @@ -0,0 +1,5 @@ +// This is a test for the new ground truth format +int c = 0; +int d = 1; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-BD/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-BD/code-variability.variant.csv new file mode 100644 index 0000000..e9b9ebd --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-BD/code-variability.variant.csv @@ -0,0 +1,4 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;7 +code.c;True;(!FEATURE_A && (FEATURE_B || FEATURE_C));(!FEATURE_A && (FEATURE_B || FEATURE_C));artifact;2;3 +code.c;True;FEATURE_D;FEATURE_D;artifact;4;5 diff --git a/src/test/resources/new-gt-format/expected/variant-BD/code.c b/src/test/resources/new-gt-format/expected/variant-BD/code.c new file mode 100644 index 0000000..715b39c --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-BD/code.c @@ -0,0 +1,7 @@ +// This is a test for the new ground truth format +int c = 0; +int d = 1; +int g = 0; +int h = 0; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-C/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-C/code-variability.variant.csv new file mode 100644 index 0000000..90d7e98 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-C/code-variability.variant.csv @@ -0,0 +1,3 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;5 +code.c;True;(!FEATURE_A && (FEATURE_B || FEATURE_C));(!FEATURE_A && (FEATURE_B || FEATURE_C));artifact;2;3 diff --git a/src/test/resources/new-gt-format/expected/variant-C/code.c b/src/test/resources/new-gt-format/expected/variant-C/code.c new file mode 100644 index 0000000..403bc69 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-C/code.c @@ -0,0 +1,5 @@ +// This is a test for the new ground truth format +int c = 0; +int d = 1; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-D/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-D/code-variability.variant.csv new file mode 100644 index 0000000..2b94637 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-D/code-variability.variant.csv @@ -0,0 +1,4 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;7 +code.c;True;(!FEATURE_A && !((FEATURE_B || FEATURE_C)));(!FEATURE_A && !((FEATURE_B || FEATURE_C)));artifact;2;3 +code.c;True;FEATURE_D;FEATURE_D;artifact;4;5 diff --git a/src/test/resources/new-gt-format/expected/variant-D/code.c b/src/test/resources/new-gt-format/expected/variant-D/code.c new file mode 100644 index 0000000..517ba4f --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-D/code.c @@ -0,0 +1,7 @@ +// This is a test for the new ground truth format +int e = 1; +int f = 1; +int g = 0; +int h = 0; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/expected/variant-base/code-variability.variant.csv b/src/test/resources/new-gt-format/expected/variant-base/code-variability.variant.csv new file mode 100644 index 0000000..a14625e --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-base/code-variability.variant.csv @@ -0,0 +1,3 @@ +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +code.c;True;True;True;ROOT;1;5 +code.c;True;(!FEATURE_A && !((FEATURE_B || FEATURE_C)));(!FEATURE_A && !((FEATURE_B || FEATURE_C)));artifact;2;3 diff --git a/src/test/resources/new-gt-format/expected/variant-base/code.c b/src/test/resources/new-gt-format/expected/variant-base/code.c new file mode 100644 index 0000000..5eafb47 --- /dev/null +++ b/src/test/resources/new-gt-format/expected/variant-base/code.c @@ -0,0 +1,5 @@ +// This is a test for the new ground truth format +int e = 1; +int f = 1; +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/new-gt-format/spl/code.c b/src/test/resources/new-gt-format/spl/code.c new file mode 100644 index 0000000..faefabe --- /dev/null +++ b/src/test/resources/new-gt-format/spl/code.c @@ -0,0 +1,18 @@ +// This is a test for the new ground truth format +# if FEATURE_A +int a = 0; +int b = 0; +# else if FEATURE_B || \ + FEATURE_C +int c = 0; +int d = 1; +#else +int e = 1; +int f = 1; +#endif +#if FEATURE_D +int g = 0; +int h = 0; +#endif +// common code +// EOF \ No newline at end of file diff --git a/src/test/resources/simple-variability-metadata/EMPTY_COMMITS.txt b/src/test/resources/simple-variability-metadata/EMPTY_COMMITS.txt new file mode 100644 index 0000000..966ebc1 --- /dev/null +++ b/src/test/resources/simple-variability-metadata/EMPTY_COMMITS.txt @@ -0,0 +1 @@ +60dc0000009b003b6f68c34902b3390e26111111 diff --git a/src/test/resources/variantgeneration/KernelHavenPCs.spl.csv b/src/test/resources/variantgeneration/KernelHavenPCs.spl.csv index bd4fdd7..8fc8bb8 100644 --- a/src/test/resources/variantgeneration/KernelHavenPCs.spl.csv +++ b/src/test/resources/variantgeneration/KernelHavenPCs.spl.csv @@ -1,6 +1,13 @@ -Path;File Condition;Block Condition;Presence Condition;start;end -src/FooFoo.cpp ; true ;true;;1;21 -src/FooFoo.cpp;true;A;;4;10 -src/FooFoo.cpp;true;B ; ;6;7 -src/FooFoo.cpp;true;(C && D) || E;;16; 17 - src/foo/bar.cpp;A;false;;1;4 +Path;File Condition;Block Condition;Presence Condition;Line Type;start;end +src/FooFoo.cpp ; true ;true;true;ROOT;1;21 +src/FooFoo.cpp;true;A;A;if;4;4 +src/FooFoo.cpp;true;A;A;artifact;5;5 +src/FooFoo.cpp;true;B ; A && B ;if;6;6 +src/FooFoo.cpp;true;B ; A && B ;artifact;7;7 +src/FooFoo.cpp;true;B ; A && B ;endif;8;8 +src/FooFoo.cpp;true;A;A;artifact;9;10 +src/FooFoo.cpp;true;A;A;endif;11;11 +src/FooFoo.cpp;true;(C && D) || E;(C && D) || E;if;16; 16 +src/FooFoo.cpp;true;(C && D) || E;(C && D) || E;artifact;17; 17 +src/FooFoo.cpp;true;(C && D) || E;(C && D) || E;endif;18; 18 + src/foo/bar.cpp;A;false;false;ROOT;1;4 diff --git a/src/test/resources/variantgeneration/KernelHavenPCs_illformed.spl.csv b/src/test/resources/variantgeneration/KernelHavenPCs_illformed.spl.csv index 4f33600..79742a6 100644 --- a/src/test/resources/variantgeneration/KernelHavenPCs_illformed.spl.csv +++ b/src/test/resources/variantgeneration/KernelHavenPCs_illformed.spl.csv @@ -1,3 +1,3 @@ Path;File Condition;Block Condition;Presence Condition;start;end -src/FooFoo.cpp;true;true;;1;10 -src/FooFoo.cpp;true;true;;5;15 \ No newline at end of file +src/FooFoo.cpp;true;true;;ROOT;1;10 +src/FooFoo.cpp;true;true;;artifact;5;15 \ No newline at end of file