Skip to content

Commit a998606

Browse files
committed
finalized example
1 parent c226aaf commit a998606

3 files changed

Lines changed: 112 additions & 24 deletions

File tree

README.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ In the following we give a step by step example in how the library can be used t
1616
- sample variants randomly, or use a predefined set of variants for simulation,
1717
- generate variants for each step in the evolution history,
1818
- obtain the ground truth of generated variants.
19-
The examples source code can also be found in [GenerationExample.java](src/main/java/vevos/examples/GenerationExample.java).
20-
We also give a brief introduction of the key features of the library we use in the following example.
19+
20+
The example's source code can also be found in [GenerationExample.java](src/main/java/vevos/examples/GenerationExample.java).
21+
A similar and executable version of this example can be found in [VEVOSBenchmark.java](src/main/java/vevos/examples/VEVOSBenchmark.java), which gathers some rudimentary data on runtime performance for Linux and Busybox variant simulation.
2122

2223
At the very begin of your program, you have to initialize the library:
2324
```java
2425
VEVOS.Initialize();
2526
```
2627
This initializes the libraries logging and binding to FeatureIDE.
28+
You may also set a log level for the library here via `Logger::setLogLevel`.
2729

2830
We can then start by specifying the necessary paths to (1) the git repository of the input software product line, (2) the directory of the extracted ground truth dataset, (3) and a directory to which we want to generate variants. (We use case sensitive paths to also allow the generation of Linux variants under Windows).
2931
```java
@@ -47,7 +49,7 @@ From the loaded `dataset`, we can obtain the available evolution step.
4749
An evolution step describes a commit-sized change to the input software product line, and is defined by the (child) commit performing a change to a previous (parent) commit.
4850
Note that the evolution steps are not ordered because commits in the input product-line repository might not have been ordered as the commits might have been extracted from different branches.
4951
Alternatively, we can also request a continuous history of evolution steps instead of an unordered set.
50-
Therefore, a `SequenceExtractor` is used to determine how the successfully extracted commits should ordered.
52+
Therefore, a `SequenceExtractor` is used to determine how the successfully extracted commits should be ordered.
5153
In this example, we use the `LongestNonOverlappingSequences` extractor to sort the commits into one single continuous history.
5254
Nevertheless, merge commits and error commits (where VEVOS/Extraction failed) are excluded from the history and thus, the returned list of commits has gaps.
5355
Because of these gaps, we obtain a list of sub-histories, where each sub-history is continuous but sub-histories are divided by merge and error commits.
@@ -63,6 +65,16 @@ final VariabilityHistory history = dataset.getVariabilityHistory(new LongestNonO
6365
/// Thus, we divide the history into sub-histories at each failed commit.
6466
final NonEmptyList<NonEmptyList<SPLCommit>> sequencesInHistory = history.commitSequences();
6567
```
68+
A sequence extraction might fail, for example if all commits are unrelated and no continuous history could be derived.
69+
In case this happens, as an alternative, you can either iterate over all `evolutionSteps` or iterate over all success commits in isolation:
70+
```java
71+
final List<SPLCommit> successCommits = dataset.getSuccessCommits();
72+
```
73+
In particular, the `VariabilityDataset` provides:
74+
- _success commits_ for which the extraction of feature mappings and feature model succeeded,
75+
- _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,
76+
- _error commits_ for which the extraction failed.
77+
6678
To generate variants, we have to specify which variants should be generated.
6779
Therefore, a `Sampler` is used that returns the set of variants to use for a certain feature model.
6880
Apart from the possibility of introducing custom samplers, VEVOS/Simulation comes with two built-in ways for sampling:
@@ -91,6 +103,16 @@ final Sample variantsToGenerate = new Sample(List.of(
91103

92104
Sampler variantsSampler = new ConstSampler(variantsToGenerate);
93105
```
106+
For the generation of variants we have to be able to access the repository of the input software product line to retrieve its source code.
107+
We reference the repository with an instance of `SPLRepository`:
108+
```java
109+
/// in general:
110+
final SPLRepository splRepository = new SPLRepository(splRepositoryPath.path());
111+
/// for Busybox:
112+
final SPLRepository splRepository = new BusyboxRepository(splRepositoryPath.path());
113+
```
114+
Note that Busybox has a special subclass called `BusyboxRepository` that performs some necessary pre- and postprocessing on the product lines source code.
115+
94116
We are now ready to traverse the evolution history to generate variants:
95117
```java
96118
for (final NonEmptyList<SPLCommit> subhistory : history.commitSequences()) {
@@ -108,6 +130,7 @@ A `Lazy` caches its loaded value so loading is only performed once.
108130
(Loaded data that is not required anymore can and should be freed by invoking `Lazy::forget`.)
109131
As the extraction of feature model or presence condition might have failed, both types are again wrapped in an `Optional` that contains a value if extraction was successful.
110132
Let's assume the extraction succeeded by just invoking `orElseThrow` here.
133+
(However, if also partial success commits are considered, one might need a more careful procedure here.)
111134
```java
112135
final Artefact pcs = loadPresenceConditions.run().orElseThrow();
113136
final IFeatureModel featureModel = loadFeatureModel.run().orElseThrow();
@@ -126,6 +149,17 @@ Here, we just instruct the generation to exit in case an error happens but we co
126149
final ArtefactFilter<SourceCodeFile> artefactFilter = ArtefactFilter.KeepAll();
127150
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(artefactFilter);
128151
```
152+
To generate variants, we have to access the source code of the input software product line, at the currently inspected commit.
153+
We thus checkout the current commit in the product line's repository:
154+
```java
155+
try {
156+
splRepository.checkoutCommit(splCommit);
157+
} catch (final GitAPIException | IOException e) {
158+
Logger.error("Failed to checkout commit " + splCommit.id() + " of " + splRepository.getPath() + "!", e);
159+
return;
160+
}
161+
```
162+
129163
Finally, we may indeed generate our variants:
130164
```java
131165
for (final Variant variant : variants) {
@@ -152,7 +186,18 @@ In contrast, the suffix is `.spl.csv` for ground truth presence conditions of th
152186

153187
/// We can also export the ground truth PCs of the variant.
154188
Resources.Instance().write(Artefact.class, presenceConditionsOfVariant, variantDir.resolve("pcs.variant.csv").path());
155-
}
189+
}
190+
}
191+
```
192+
In case we use Busybox as our input product line, we have to clean its repository as a last step:
193+
```java
194+
if (splRepository instanceof BusyboxRepository b) {
195+
try {
196+
b.postprocess();
197+
} catch (final GitAPIException | IOException e) {
198+
Logger.error("Busybox postprocessing failed, please clean up manually (e.g., git stash, git stash drop) at " + splRepository.getPath(), e);
199+
}
200+
}
156201
```
157202
This was round-trip about the major features of VEVOS/Simulation. Further features and convencience methods can be found in our documentation.
158203

src/main/java/vevos/examples/GenerationExample.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package vevos.examples;
22

33
import de.ovgu.featureide.fm.core.base.IFeatureModel;
4+
import org.eclipse.jgit.api.errors.GitAPIException;
45
import vevos.VEVOS;
56
import vevos.feature.Variant;
67
import vevos.feature.config.SimpleConfiguration;
@@ -12,6 +13,8 @@
1213
import vevos.functjonal.Result;
1314
import vevos.functjonal.list.NonEmptyList;
1415
import vevos.io.Resources;
16+
import vevos.repository.BusyboxRepository;
17+
import vevos.repository.SPLRepository;
1518
import vevos.util.Logger;
1619
import vevos.util.io.CaseSensitivePath;
1720
import vevos.variability.EvolutionStep;
@@ -26,6 +29,7 @@
2629
import vevos.variability.pc.options.VariantGenerationOptions;
2730
import vevos.variability.sequenceextraction.LongestNonOverlappingSequences;
2831

32+
import java.io.IOException;
2933
import java.util.List;
3034
import java.util.Map;
3135
import java.util.Optional;
@@ -65,6 +69,10 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio
6569
}
6670
Logger.info("");
6771

72+
/// In case the sequence extraction fails, you might either use the evolutionSteps directly or
73+
/// iterate over all commits in isolation:
74+
// final List<SPLCommit> successCommits = dataset.getSuccessCommits();
75+
6876
/// Now lets use the variant generator to generate variants.
6977
/// First, create a sampler that determines which variants to generate at each evolution step.
7078
Sampler variantsSampler;
@@ -88,6 +96,15 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio
8896
variantsSampler = new ConstSampler(variantsToGenerate);
8997
}
9098

99+
/// Access the input software-product line's repository.
100+
/// In case you use Busybox as input SPL, use the BusyboxRepository class.
101+
/// Busybox requires special pre- and postprocessing steps upon commit checkout, that are handled by the
102+
/// BusyboxRepository class.
103+
final SPLRepository splRepository =
104+
new SPLRepository(splRepositoryPath.path());
105+
// new BusyboxRepository(splRepositoryPath.path());
106+
107+
91108
/// For the entire history
92109
for (final NonEmptyList<SPLCommit> subhistory : history.commitSequences()) {
93110
for (final SPLCommit splCommit : subhistory) {
@@ -117,6 +134,14 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio
117134
final ArtefactFilter<SourceCodeFile> artefactFilter = ArtefactFilter.KeepAll();
118135
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(artefactFilter);
119136

137+
/// Checkout the considered commit of the input SPL to access its source code.
138+
try {
139+
splRepository.checkoutCommit(splCommit);
140+
} catch (final GitAPIException | IOException e) {
141+
Logger.error("Failed to checkout commit " + splCommit.id() + " of " + splRepository.getPath() + "!", e);
142+
return;
143+
}
144+
120145
for (final Variant variant : variants) {
121146
/// Let's put the variant into our target directory but indexed by commit hash and its name.
122147
final CaseSensitivePath variantDir = variantsGenerationDir.resolve(splCommit.id(), variant.getName());
@@ -143,6 +168,14 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio
143168
result.getFailure());
144169
}
145170
}
171+
172+
if (splRepository instanceof BusyboxRepository b) {
173+
try {
174+
b.postprocess();
175+
} catch (final GitAPIException | IOException e) {
176+
Logger.error("Busybox postprocessing failed, please clean up manually (e.g., git stash, git stash drop) at " + splRepository.getPath(), e);
177+
}
178+
}
146179
}
147180
}
148181
}

src/test/java/vevos/VEVOSBenchmark.java renamed to src/main/java/vevos/examples/VEVOSBenchmark.java

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
package vevos;
1+
package vevos.examples;
22

33
import de.ovgu.featureide.fm.core.base.IFeatureModel;
4-
import org.junit.Test;
4+
import vevos.VEVOS;
55
import vevos.feature.Variant;
66
import vevos.feature.sampling.FeatureIDESampler;
77
import vevos.feature.sampling.Sample;
@@ -37,27 +37,25 @@ private record Repo(
3737
CaseSensitivePath groundTruthDatasetPath,
3838
CaseSensitivePath variantsGenerationDir,
3939
Function<CaseSensitivePath, SPLRepository> createRepo,
40-
Consumer<SPLRepository> prepare,
4140
Consumer<SPLRepository> cleanup) {}
4241

43-
private static final CaseSensitivePath WIN_SUBMISSION_DIR = CaseSensitivePath.of("/mnt/c/Users/Paul Bittner/Documents/MyDocuments/Projects/VEVOS/Submission/Extraction/extraction-results/");
44-
private static final CaseSensitivePath WSL_VARIANT_EVOLUTION_DATASETS_DIR = CaseSensitivePath.of("../variantevolution_datasets/");
42+
/// TODO: Specify your paths here.
43+
private static final CaseSensitivePath SPL_REPOS_DIR = CaseSensitivePath.of("path/to/SPL/repos");
44+
private static final CaseSensitivePath DATASETS_DIR = CaseSensitivePath.of("path/to/datasets/");
45+
private static final CaseSensitivePath VARIANT_GENERATION_DIR = CaseSensitivePath.of("path/to/datasets/");
4546

4647
private static final Repo LINUX = new Repo(
47-
WSL_VARIANT_EVOLUTION_DATASETS_DIR.resolve("linux"),
48-
WIN_SUBMISSION_DIR.resolve("linux"),
49-
CaseSensitivePath.of("../dockerbrezel/linux"),
48+
SPL_REPOS_DIR.resolve("linux"),
49+
DATASETS_DIR.resolve("linux"),
50+
VARIANT_GENERATION_DIR.resolve("linux"),
5051
path -> new SPLRepository(path.path()),
51-
s -> {},
5252
s -> {}
5353
);
5454
private static final Repo BUSYBOX = new Repo(
55-
WSL_VARIANT_EVOLUTION_DATASETS_DIR.resolve("busybox/busybox"),
56-
WSL_VARIANT_EVOLUTION_DATASETS_DIR.resolve("busybox/variability"),
57-
// WIN_SUBMISSION_DIR.resolve("busybox"),
58-
CaseSensitivePath.of("../dockerbrezel/busybox"),
55+
SPL_REPOS_DIR.resolve("busybox"),
56+
DATASETS_DIR.resolve("busybox"),
57+
VARIANT_GENERATION_DIR.resolve("busybox"),
5958
path -> new BusyboxRepository(path.path()),
60-
s -> { },
6159
s -> {
6260
BusyboxRepository b = Cast.unchecked(s);
6361
try {
@@ -76,7 +74,7 @@ private static String logTime(final String task, final double seconds) {
7674
return msg;
7775
}
7876

79-
private static void resultEntry(final StringBuilder builder, String entry) {
77+
private static void resultEntry(final StringBuilder builder, final String entry) {
8078
builder.append(entry).append("\r\n");
8179
}
8280

@@ -118,7 +116,6 @@ public static void benchmark(final Repo repo) throws Exception {
118116
for (final SPLCommit splCommit : subhistory) {
119117
Logger.info("-- Processing commit " + splCommit.id() + " --");
120118
splRepo.checkoutCommit(splCommit);
121-
repo.prepare.accept(splRepo);
122119

123120
clock.start();
124121
final Lazy<Optional<IFeatureModel>> loadFeatureModel = splCommit.featureModel();
@@ -193,13 +190,26 @@ public static void benchmark(final Repo repo) throws Exception {
193190
TextIO.write(repo.variantsGenerationDir.resolve("benchmarkdata.txt").path(), result);
194191
}
195192

196-
@Test
197-
public void benchmarkLinux() throws Exception {
193+
public static void benchmarkLinux() throws Exception {
198194
benchmark(LINUX);
199195
}
200196

201-
@Test
202-
public void benchmarkBusybox() throws Exception {
197+
public static void benchmarkBusybox() throws Exception {
203198
benchmark(BUSYBOX);
204199
}
200+
201+
public static void main(final String[] args) throws Exception {
202+
if (args.length < 1) {
203+
Logger.error("Expected exactly one argument that either is \"busybox\" or \"linux\"!");
204+
}
205+
206+
final String repoName = args[0];
207+
if ("linux".equalsIgnoreCase(repoName)) {
208+
benchmarkLinux();
209+
} else if ("busybox".equalsIgnoreCase(repoName)) {
210+
benchmarkBusybox();
211+
} else {
212+
Logger.error("Unknown repository " + repoName + "! Expected \"busybox\" or \"linux\"!");
213+
}
214+
}
205215
}

0 commit comments

Comments
 (0)