• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LearnLib / learnlib / 31522550532

11 Aug 2026 06:24PM UTC coverage: 95.489% (+0.2%) from 95.334%
31522550532

push

github

web-flow
LearnLib CLI (#166)

* initial work on CLI application

* update build profiles

* github: add worflow for attaching cli artifacts

* cli: include OS classifiers in artifact name

* do not include cli module in releases for now

* update docs

* tidy:pom

* add support for SAF serialization

* cli: add ADT and LSHARP algorithm

* cli oracles still need some rework regarding error handling

* cleanup CLI oracles

* experiment: allow for logging intermediate hypotheses

* jacoco: also depend on cli module before aggregating reports

* cli: improve documentation

* fix code-analysis

* cli: add support for parallel oracles

* cli: add unit- and integration-tests

* add support for snapshotting

* use LocalDateTime

* simplify --eqo-* param names

* Revert "simplify --eqo-* param names"

This reverts commit d209e962d.

* add MalerPnueli + RivestSchapire learner and adjust learner names

* include license information in jlink artifact

* cleanup checkerframework config

* add rudimentary class validation during deserialization + cleanups

* add changelog

* add missing --add-opens + test

* cleanup

* wording

* handle python availability more gracefully

* conditionally set --add-opens option

* cli: invoke script via python interpreter

as on Windows the scripts alone are not detected as executable applications

* sett full (and platform-specific) path in jlink IT

* wording

* coveralls: s/Example/Experiment/

685 of 703 new or added lines in 31 files covered. (97.44%)

2 existing lines in 1 file now uncovered.

15536 of 16270 relevant lines covered (95.49%)

1.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.8
/cli/src/main/java/de/learnlib/cli/util/SnapshottingExperiment.java
1
/* Copyright (C) 2013-2026 TU Dortmund University
2
 * This file is part of LearnLib <https://learnlib.de>.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package de.learnlib.cli.util;
17

18
import java.io.IOException;
19
import java.nio.file.Files;
20
import java.nio.file.Path;
21
import java.time.LocalDateTime;
22
import java.time.format.DateTimeFormatter;
23

24
import de.learnlib.Resumable;
25
import de.learnlib.algorithm.LearningAlgorithm;
26
import de.learnlib.cli.option.Options;
27
import de.learnlib.oracle.EquivalenceOracle;
28
import de.learnlib.util.Experiment;
29
import net.automatalib.alphabet.Alphabet;
30
import net.automatalib.automaton.concept.FiniteRepresentation;
31
import net.automatalib.serialization.InputModelSerializer;
32
import org.apache.fory.Fory;
33
import org.apache.fory.exception.ForyException;
34
import org.apache.fory.logging.LoggerFactory;
35
import org.apache.fory.resolver.AllowListChecker;
36
import org.apache.fory.resolver.AllowListChecker.CheckLevel;
37
import org.checkerframework.checker.nullness.qual.Nullable;
38
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
39

40
final class SnapshottingExperiment<A extends FiniteRepresentation, I, D> extends Experiment<A, I, D> {
41

42
    private static final Fory FORY;
43
    private static final DateTimeFormatter DTF;
44

45
    static {
46
        // use same config as test-support to automatically test proper white-listing
47
        final AllowListChecker checker = new AllowListChecker();
1✔
48
        checker.setCheckLevel(CheckLevel.STRICT);
1✔
49
        checker.allowClass("de.learnlib.*");
1✔
50
        checker.allowClass("net.automatalib.*");
1✔
51
        FORY = Fory.builder()
1✔
52
                   .requireClassRegistration(false)
1✔
53
                   .withCodegen(false)
1✔
54
                   .withRefTracking(true)
1✔
55
                   .withTypeChecker(checker)
1✔
56
                   .withXlang(false)
1✔
57
                   .build();
1✔
58
        LoggerFactory.useSlf4jLogging(true);
1✔
59
        DTF = DateTimeFormatter.ofPattern("-yyyyMMdd-HHmmss-");
1✔
60
    }
1✔
61

62
    private final Resumable<?> resumable;
63
    private final String fingerPrint;
64

65
    private final @Nullable Path resumeFrom;
66
    private final @Nullable Path snapshotDir;
67

68
    SnapshottingExperiment(LearningAlgorithm<? extends A, I, D> learningAlgorithm,
69
                           Resumable<?> resumable,
70
                           EquivalenceOracle<? super A, I, D> equivalenceAlgorithm,
71
                           Alphabet<I> inputs,
72
                           InputModelSerializer<I, ? super A> serializer,
73
                           Options options) {
74
        super(learningAlgorithm, equivalenceAlgorithm, inputs, serializer);
1✔
75

76
        this.resumable = resumable;
1✔
77
        this.fingerPrint = DTF.format(LocalDateTime.now());
1✔
78
        this.resumeFrom = options.resumeFrom;
1✔
79
        this.snapshotDir = options.snapshotDir;
1✔
80

81
        if (this.resumeFrom != null && !this.resumeFrom.toFile().isFile()) {
1✔
82
            throw new IllegalArgumentException(String.format("Provided resume path '%s' is not a file",
1✔
83
                                                             options.resumeFrom));
84
        }
85

86
        if (this.snapshotDir != null && !this.snapshotDir.toFile().isDirectory()) {
1✔
87
            throw new IllegalArgumentException(String.format("Provided snapshot path '%s' is not a directory",
1✔
88
                                                             options.snapshotDir));
89
        }
90
    }
1✔
91

92
    @Override
93
    protected void initializeLearning() {
94
        if (this.resumeFrom == null) {
1✔
95
            super.initializeLearning();
1✔
96
        } else {
97
            LOGGER.info("Resuming learning process from file '{}'", resumeFrom);
1✔
98
            try {
99
                resumLearner(this.resumable);
1✔
NEW
100
            } catch (IOException | ForyException e) {
×
NEW
101
                LOGGER.warn("Could not resume learning process. Starting from scratch...", e);
×
NEW
102
                super.initializeLearning();
×
103
            }
1✔
104
        }
105
    }
1✔
106

107
    @RequiresNonNull("this.resumeFrom")
108
    private <T> void resumLearner(Resumable<T> resumable) throws IOException {
109
        @SuppressWarnings("unchecked")
110
        final T state = (T) FORY.deserialize(Files.readAllBytes(resumeFrom));
1✔
111
        resumable.resume(state);
1✔
112
    }
1✔
113

114
    @Override
115
    protected void postRefinementHook() {
116
        if (this.snapshotDir == null) {
1✔
117
            super.postRefinementHook();
1✔
118
        } else {
119
            snapshotState(this.resumable);
1✔
120
        }
121
    }
1✔
122

123
    @RequiresNonNull("this.snapshotDir")
124
    private <T> void snapshotState(Resumable<T> resumable) {
125
        final T suspend = resumable.suspend();
1✔
126
        final String round = Integer.toString(getRound() - 1); // do not count startLearning round
1✔
127
        final String fileName = "learnlib" + fingerPrint + round + ".bin";
1✔
128
        LOGGER.info("Writing snapshot to file '{}'", fileName);
1✔
129
        try {
130
            Files.write(this.snapshotDir.resolve(fileName), FORY.serialize(suspend));
1✔
NEW
131
        } catch (IOException | ForyException e) {
×
NEW
132
            LOGGER.warn("Could not write learner state. Continuing without...", e);
×
133
        }
1✔
134
    }
1✔
135
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc