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

LearnLib / learnlib / 16696694729

02 Aug 2025 06:52PM UTC coverage: 94.289% (-0.09%) from 94.383%
16696694729

push

github

mtf90
add CLI membership oracles

closes #149

111 of 131 new or added lines in 4 files covered. (84.73%)

12630 of 13395 relevant lines covered (94.29%)

1.72 hits per line

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

85.71
/oracles/membership-oracles/src/main/java/de/learnlib/oracle/membership/StdInOutputOracle.java
1
/* Copyright (C) 2013-2025 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.oracle.membership;
17

18
import java.io.IOException;
19
import java.io.StringReader;
20
import java.util.List;
21
import java.util.Objects;
22
import java.util.StringJoiner;
23
import java.util.function.BiFunction;
24

25
import de.learnlib.oracle.SingleQueryOracle;
26
import net.automatalib.common.util.process.ProcessUtil;
27
import net.automatalib.word.Word;
28
import org.checkerframework.checker.nullness.qual.Nullable;
29
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
30
import org.slf4j.Logger;
31
import org.slf4j.LoggerFactory;
32

33
/**
34
 * An oracle that delegates its queries to an external program via the command-line interface. Outputs of the queries
35
 * are determined based on the provided output transformer.
36
 * <p>
37
 * Queries are passed to the program's stdin stream (via the queries' {@link Word#toString()} method. You may adjust
38
 * formatting via the available properties in AutomataLib's settings). Depending on whether a {@code reset} symbol has
39
 * been specified, this oracle assumes either a stateless ({@code reset == null}) or stateful ({@code reset != null})
40
 * communication.
41
 * <p>
42
 * In a stateless communication, all symbols of a query are passed to the program at once and invocations should be
43
 * treated independently from each other. In a stateful communication, the program is executed multiple times with a
44
 * single query symbol each, preceded by a single invocation with only the {@code reset} symbol. The exit code of the
45
 * last invocation determines the query response.
46
 *
47
 * @param <I>
48
 *         input symbol type
49
 * @param <D>
50
 *         output domain type
51
 */
52
public class StdInOutputOracle<I, D> implements SingleQueryOracle<I, D> {
53

54
    private static final Logger LOGGER = LoggerFactory.getLogger(StdInOutputOracle.class);
2✔
55

56
    private final List<String> commandLine;
57
    private final BiFunction<String, Integer, D> outputTransformer;
58
    private final @Nullable String reset;
59

60
    /**
61
     * Constructor. Does not set a {@code reset} symbol.
62
     *
63
     * @param commandLine
64
     *         the command line, containing the main binary and potential additional arguments
65
     * @param outputTransformer
66
     *         the transformer for the program's output. Receives the full (stdout) output as well as the length of the
67
     *         query prefix for properly offsetting potentially {@link Word}-based output types.
68
     *
69
     * @see #StdInOutputOracle(List, BiFunction, String)
70
     */
71
    public StdInOutputOracle(List<String> commandLine, BiFunction<String, Integer, D> outputTransformer) {
72
        this(commandLine, outputTransformer, null);
2✔
73
    }
2✔
74

75
    /**
76
     * Constructor.
77
     *
78
     * @param commandLine
79
     *         the command line, containing the main binary and potential additional arguments
80
     * @param outputTransformer
81
     *         the transformer for the program's output. Receives the full (stdout) output as well as the length of the
82
     *         query prefix for properly offsetting potentially {@link Word}-based output types.
83
     * @param reset
84
     *         the symbol passed to the program to indicate a reset
85
     */
86
    public StdInOutputOracle(List<String> commandLine,
87
                             BiFunction<String, Integer, D> outputTransformer,
88
                             @Nullable String reset) {
2✔
89
        this.commandLine = commandLine;
2✔
90
        this.outputTransformer = outputTransformer;
2✔
91
        this.reset = reset;
2✔
92
    }
2✔
93

94
    @Override
95
    public D answerQuery(Word<I> prefix, Word<I> suffix) {
96
        return reset == null ? answerStatelessQuery(prefix, suffix) : answerStatefulQuery(prefix, suffix);
2✔
97
    }
98

99
    private D answerStatelessQuery(Word<I> prefix, Word<I> suffix) {
100
        final StringJoiner sj = new StringJoiner(System.lineSeparator());
2✔
101

102
        try {
103
            ProcessUtil.invokeProcess(commandLine,
2✔
104
                                      new StringReader(prefix.concat(suffix).toString()),
2✔
105
                                      sj::add,
2✔
106
                                      LOGGER::warn);
2✔
107
            return outputTransformer.apply(sj.toString(), prefix.length());
2✔
NEW
108
        } catch (IOException | InterruptedException e) {
×
NEW
109
            throw new IllegalStateException(e);
×
110
        }
111
    }
112

113
    @RequiresNonNull("this.reset")
114
    private D answerStatefulQuery(Word<I> prefix, Word<I> suffix) {
115
        final StringJoiner sj = new StringJoiner(System.lineSeparator());
2✔
116

117
        try {
118
            ProcessUtil.invokeProcess(commandLine, new StringReader(reset), LOGGER::debug, LOGGER::warn);
2✔
119

120
            for (I p : prefix) {
2✔
121
                ProcessUtil.invokeProcess(commandLine, new StringReader(Objects.toString(p)), sj::add, LOGGER::warn);
2✔
122
            }
2✔
123

124
            for (I s : suffix) {
2✔
125
                ProcessUtil.invokeProcess(commandLine, new StringReader(Objects.toString(s)), sj::add, LOGGER::warn);
2✔
126
            }
2✔
127

128
            return outputTransformer.apply(sj.toString(), prefix.length());
2✔
NEW
129
        } catch (IOException | InterruptedException e) {
×
NEW
130
            throw new IllegalStateException(e);
×
131
        }
132
    }
133
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc