StemmerModelRegistry.java

1
/*******************************************************************************
2
 * Copyright (C) 2026, Leo Galambos
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions and the following disclaimer.
10
 *
11
 * 2. Redistributions in binary form must reproduce the above copyright notice,
12
 *    this list of conditions and the following disclaimer in the documentation
13
 *    and/or other materials provided with the distribution.
14
 *
15
 * 3. Neither the name of the copyright holder nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 ******************************************************************************/
31
package org.egothor.stemmer;
32
33
import java.io.BufferedReader;
34
import java.io.IOException;
35
import java.io.InputStreamReader;
36
import java.net.URL;
37
import java.nio.charset.StandardCharsets;
38
import java.util.ArrayList;
39
import java.util.Collections;
40
import java.util.Enumeration;
41
import java.util.LinkedHashMap;
42
import java.util.List;
43
import java.util.Map;
44
import java.util.Objects;
45
import java.util.Properties;
46
47
/**
48
 * Immutable deterministic registry of models discovered from classpath indexes.
49
 *
50
 * <p>Discovery enumerates every {@value #INDEX_RESOURCE} visible to the selected
51
 * class loader, validates the referenced descriptors, sorts them by stable model
52
 * identifier, and rejects duplicate identifiers. Selection never depends on
53
 * classpath order. Registry creation validates metadata and resource presence;
54
 * {@link StemmerPatchTrieLoader} verifies resource bytes when loading a model.</p>
55
 *
56
 * <p>Registry creation is not globally cached. Applications should normally
57
 * discover once for a class-loader scope and retain the immutable result.</p>
58
 */
59
@SuppressWarnings({ "PMD.UseProperClassLoader", "PMD.ControlStatementBraces" })
60
public final class StemmerModelRegistry {
61
    /** Fixed classpath index name used by every model artifact. */
62
    public static final String INDEX_RESOURCE = "META-INF/radixor/models.index";
63
    private static final String FORMAT = "radixor-dictionary-tsv-gzip";
64
    private static final int FORMAT_VERSION = 1;
65
    private final Map<String, StemmerModelDescriptor> descriptors;
66
67
    /** Creates an immutable registry from already validated descriptors. */
68
    private StemmerModelRegistry(final Map<String, StemmerModelDescriptor> descriptors) {
69
        this.descriptors = Collections.unmodifiableMap(new LinkedHashMap<>(descriptors));
70
    }
71
72
    /**
73
     * Discovers models through the current thread context class loader.
74
     *
75
     * <p>If the context loader is {@code null}, the defining class loader of this
76
     * registry is used.</p>
77
     *
78
     * @return immutable registry in stable model-ID order
79
     * @throws IOException if index or descriptor resources cannot be enumerated or read
80
     * @throws DuplicateStemmerModelException if two descriptors declare one model ID
81
     * @throws StemmerModelIntegrityException if an index, descriptor, or declared resource is invalid
82
     * @throws UnsupportedStemmerModelFormatException if a descriptor uses an unsupported format
83
     */
84
    public static StemmerModelRegistry fromContextClassLoader() throws IOException {
85
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
86 2 1. fromContextClassLoader : negated conditional → SURVIVED
2. fromContextClassLoader : replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::fromContextClassLoader → KILLED
        return fromClassLoader(classLoader == null ? StemmerModelRegistry.class.getClassLoader() : classLoader);
87
    }
88
89
    /**
90
     * Discovers and validates every indexed descriptor visible to an explicit class loader.
91
     *
92
     * @param classLoader class loader whose indexed model resources are visible
93
     * @return immutable registry in stable model-ID order
94
     * @throws NullPointerException if {@code classLoader} is {@code null}
95
     * @throws IOException if index or descriptor resources cannot be enumerated or read
96
     * @throws DuplicateStemmerModelException if two descriptors declare one model ID
97
     * @throws StemmerModelIntegrityException if an index, descriptor, or declared resource is invalid
98
     * @throws UnsupportedStemmerModelFormatException if a descriptor uses an unsupported format
99
     */
100
    public static StemmerModelRegistry fromClassLoader(final ClassLoader classLoader) throws IOException {
101
        Objects.requireNonNull(classLoader, "classLoader");
102
        final List<URL> indexes = Collections.list(classLoader.getResources(INDEX_RESOURCE));
103 2 1. fromClassLoader : removed call to java/util/List::sort → SURVIVED
2. lambda$fromClassLoader$0 : replaced int return with 0 for org/egothor/stemmer/StemmerModelRegistry::lambda$fromClassLoader$0 → SURVIVED
        indexes.sort((left, right) -> left.toExternalForm().compareTo(right.toExternalForm()));
104
        final List<StemmerModelDescriptor> discovered = new ArrayList<>();
105
        for (URL index : indexes) {
106 1 1. fromClassLoader : removed call to org/egothor/stemmer/StemmerModelRegistry::readIndex → KILLED
            readIndex(index, classLoader, discovered);
107
        }
108 1 1. fromClassLoader : removed call to java/util/Collections::sort → SURVIVED
        Collections.sort(discovered);
109
        final Map<String, StemmerModelDescriptor> byId = new LinkedHashMap<>();
110
        for (StemmerModelDescriptor descriptor : discovered) {
111
            final StemmerModelDescriptor previous = byId.putIfAbsent(descriptor.id(), descriptor);
112 1 1. fromClassLoader : negated conditional → KILLED
            if (previous != null) {
113
                throw new DuplicateStemmerModelException("Duplicate model ID '" + descriptor.id() + "' at "
114
                        + previous.source() + " and " + descriptor.source() + ".");
115
            }
116
        }
117 1 1. fromClassLoader : replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::fromClassLoader → KILLED
        return new StemmerModelRegistry(byId);
118
    }
119
120
    /** Returns all descriptors in stable model-identifier order. */
121 1 1. models : replaced return value with Collections.emptyList for org/egothor/stemmer/StemmerModelRegistry::models → KILLED
    public List<StemmerModelDescriptor> models() { return List.copyOf(this.descriptors.values()); }
122
123
    /**
124
     * Returns the model with an exact stable identifier.
125
     *
126
     * @param modelId exact stable model identifier
127
     * @return matching descriptor
128
     * @throws NullPointerException if {@code modelId} is {@code null}
129
     * @throws StemmerModelNotFoundException if the selected class loader exposes no matching descriptor
130
     */
131
    public StemmerModelDescriptor require(final String modelId) {
132
        Objects.requireNonNull(modelId, "modelId");
133
        final StemmerModelDescriptor descriptor = this.descriptors.get(modelId);
134 1 1. require : negated conditional → KILLED
        if (descriptor == null) {
135
            throw new StemmerModelNotFoundException("No model '" + modelId + "' is available. Add org.egothor:radixor-model-"
136
                    + modelId + ":<version> to the runtime classpath.");
137
        }
138 1 1. require : replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::require → KILLED
        return descriptor;
139
    }
140
141
    /**
142
     * Returns all models for a language in stable model-identifier order.
143
     *
144
     * @param language language to filter
145
     * @return immutable list, possibly empty
146
     * @throws NullPointerException if {@code language} is {@code null}
147
     */
148
    public List<StemmerModelDescriptor> findByLanguage(final StemmerPatchTrieLoader.Language language) {
149
        Objects.requireNonNull(language, "language");
150 3 1. findByLanguage : replaced return value with Collections.emptyList for org/egothor/stemmer/StemmerModelRegistry::findByLanguage → KILLED
2. lambda$findByLanguage$1 : negated conditional → KILLED
3. lambda$findByLanguage$1 : replaced boolean return with true for org/egothor/stemmer/StemmerModelRegistry::lambda$findByLanguage$1 → KILLED
        return this.descriptors.values().stream().filter(value -> value.language() == language).toList();
151
    }
152
153
    /**
154
     * Resolves the language's documented default model without classpath-order fallback.
155
     *
156
     * @param language language whose {@link StemmerPatchTrieLoader.Language#defaultModelId()} is required
157
     * @return exact default-model descriptor
158
     * @throws NullPointerException if {@code language} is {@code null}
159
     * @throws StemmerModelNotFoundException if the default model is not visible
160
     * @throws StemmerModelIntegrityException if the default descriptor declares another language
161
     */
162
    public StemmerModelDescriptor requireDefault(final StemmerPatchTrieLoader.Language language) {
163
        Objects.requireNonNull(language, "language");
164
        final StemmerModelDescriptor descriptor = this.descriptors.get(language.defaultModelId());
165 1 1. requireDefault : negated conditional → KILLED
        if (descriptor == null) {
166
            throw new StemmerModelNotFoundException("No default model '" + language.defaultModelId()
167
                    + "' is available for language " + language + ". Add org.egothor:radixor-model-"
168
                    + language.defaultModelId() + ":<version> to the runtime classpath.");
169
        }
170 1 1. requireDefault : negated conditional → KILLED
        if (descriptor.language() != language) {
171
            throw new StemmerModelIntegrityException("Default model '" + descriptor.id() + "' declares language "
172
                    + descriptor.language() + " instead of " + language + ".");
173
        }
174 1 1. requireDefault : replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::requireDefault → KILLED
        return descriptor;
175
    }
176
177
    /** Reads one deterministic index and appends its descriptors. */
178
    private static void readIndex(final URL index, final ClassLoader classLoader,
179
            final List<StemmerModelDescriptor> descriptors) throws IOException {
180
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(index.openStream(), StandardCharsets.UTF_8))) {
181
            String line;
182
            int lineNumber = 0;
183 1 1. readIndex : negated conditional → KILLED
            while ((line = reader.readLine()) != null) {
184 1 1. readIndex : Changed increment from 1 to -1 → SURVIVED
                lineNumber++;
185
                final String path = line.trim();
186 2 1. readIndex : negated conditional → KILLED
2. readIndex : negated conditional → KILLED
                if (path.isEmpty() || path.startsWith("#")) continue;
187 1 1. readIndex : negated conditional → KILLED
                if (!path.matches("META-INF/radixor/models/[a-z0-9-]+\\.properties")) {
188
                    throw new StemmerModelIntegrityException("Malformed model index entry at " + index + ":" + lineNumber + ": " + path);
189
                }
190
                final Enumeration<URL> resources = classLoader.getResources(path);
191 1 1. readIndex : negated conditional → KILLED
                if (!resources.hasMoreElements()) throw new StemmerModelIntegrityException("Indexed descriptor is missing: " + path + " from " + index);
192 1 1. readIndex : negated conditional → KILLED
                while (resources.hasMoreElements()) descriptors.add(readDescriptor(resources.nextElement(), classLoader));
193
            }
194
        }
195
    }
196
197
    /** Parses and validates one immutable descriptor. */
198
    private static StemmerModelDescriptor readDescriptor(final URL source, final ClassLoader classLoader) throws IOException {
199
        final Properties properties = new Properties();
200
        try (InputStreamReader reader = new InputStreamReader(source.openStream(), StandardCharsets.UTF_8)) {
201 1 1. readDescriptor : removed call to java/util/Properties::load → KILLED
            properties.load(reader);
202
        }
203
        final String id = required(properties, "model.id", source);
204 1 1. readDescriptor : negated conditional → KILLED
        if (!id.matches("[a-z]{2,3}(?:-[a-z]{2})?-[a-z0-9]+(?:-[a-z0-9]+)*")) throw new StemmerModelIntegrityException("Invalid model.id '" + id + "' at " + source);
205
        final String format = required(properties, "model.format", source);
206
        final int version;
207
        try { version = Integer.parseInt(required(properties, "model.formatVersion", source)); }
208
        catch (NumberFormatException exception) { throw new StemmerModelIntegrityException("Invalid model.formatVersion at " + source, exception); }
209 2 1. readDescriptor : negated conditional → KILLED
2. readDescriptor : negated conditional → KILLED
        if (!FORMAT.equals(format) || version != FORMAT_VERSION) throw new UnsupportedStemmerModelFormatException("Unsupported model format " + format + " version " + version + " at " + source + ".");
210
        final StemmerPatchTrieLoader.Language language;
211
        try { language = StemmerPatchTrieLoader.Language.valueOf(required(properties, "model.language", source)); }
212
        catch (IllegalArgumentException exception) { throw new StemmerModelIntegrityException("Invalid model.language at " + source, exception); }
213
        final String resource = required(properties, "model.resource", source);
214 1 1. readDescriptor : negated conditional → KILLED
        if (!resource.equals("org/egothor/stemmer/models/" + id + "/stemmer.gz")) throw new StemmerModelIntegrityException("Invalid model.resource for '" + id + "' at " + source);
215 1 1. readDescriptor : negated conditional → KILLED
        if (classLoader.getResource(resource) == null) throw new StemmerModelIntegrityException("Model resource is missing: " + resource + " declared at " + source);
216
        final String checksum = required(properties, "model.sha256", source);
217 1 1. readDescriptor : negated conditional → KILLED
        if (!checksum.matches("[0-9a-f]{64}")) throw new StemmerModelIntegrityException("Invalid model.sha256 at " + source);
218
        final boolean rightToLeft = requiredBoolean(properties, "model.rightToLeft", source);
219 1 1. readDescriptor : negated conditional → KILLED
        if (rightToLeft != language.isRightToLeft()) {
220
            throw new StemmerModelIntegrityException("Model right-to-left metadata for '" + id
221
                    + "' does not match language " + language + " at " + source + ".");
222
        }
223 1 1. readDescriptor : replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::readDescriptor → KILLED
        return new StemmerModelDescriptor(id, required(properties, "model.version", source), language,
224
                required(properties, "model.displayName", source), resource,
225
                requiredBoolean(properties, "model.default", source), format, version, checksum, rightToLeft, source,
226
                classLoader);
227
    }
228
229
    /** Returns a required nonblank property. */
230
    private static String required(final Properties properties, final String key, final URL source) {
231
        final String value = properties.getProperty(key);
232 2 1. required : negated conditional → KILLED
2. required : negated conditional → KILLED
        if (value == null || value.isBlank()) throw new StemmerModelIntegrityException("Required property '" + key + "' is missing at " + source);
233 1 1. required : replaced return value with "" for org/egothor/stemmer/StemmerModelRegistry::required → KILLED
        return value.trim();
234
    }
235
236
    /** Returns one required strict boolean property. */
237
    private static boolean requiredBoolean(final Properties properties, final String key, final URL source) {
238
        final String value = required(properties, key, source);
239 2 1. requiredBoolean : replaced boolean return with true for org/egothor/stemmer/StemmerModelRegistry::requiredBoolean → KILLED
2. requiredBoolean : replaced boolean return with false for org/egothor/stemmer/StemmerModelRegistry::requiredBoolean → KILLED
        return switch (value) {
240
            case "true" -> true;
241
            case "false" -> false;
242
            default -> throw new StemmerModelIntegrityException(
243
                    "Property '" + key + "' must be true or false at " + source + ".");
244
        };
245
    }
246
}

Mutations

86

1.1
Location : fromContextClassLoader
Killed by : none
negated conditional → SURVIVED
Covering tests

2.2
Location : fromContextClassLoader
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::fromContextClassLoader → KILLED

103

1.1
Location : fromClassLoader
Killed by : none
removed call to java/util/List::sort → SURVIVED
Covering tests

2.2
Location : lambda$fromClassLoader$0
Killed by : none
replaced int return with 0 for org/egothor/stemmer/StemmerModelRegistry::lambda$fromClassLoader$0 → SURVIVED Covering tests

106

1.1
Location : fromClassLoader
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
removed call to org/egothor/stemmer/StemmerModelRegistry::readIndex → KILLED

108

1.1
Location : fromClassLoader
Killed by : none
removed call to java/util/Collections::sort → SURVIVED
Covering tests

112

1.1
Location : fromClassLoader
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

117

1.1
Location : fromClassLoader
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::fromClassLoader → KILLED

121

1.1
Location : models
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
replaced return value with Collections.emptyList for org/egothor/stemmer/StemmerModelRegistry::models → KILLED

134

1.1
Location : require
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

138

1.1
Location : require
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::require → KILLED

150

1.1
Location : findByLanguage
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
replaced return value with Collections.emptyList for org/egothor/stemmer/StemmerModelRegistry::findByLanguage → KILLED

2.2
Location : lambda$findByLanguage$1
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

3.3
Location : lambda$findByLanguage$1
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
replaced boolean return with true for org/egothor/stemmer/StemmerModelRegistry::lambda$findByLanguage$1 → KILLED

165

1.1
Location : requireDefault
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

170

1.1
Location : requireDefault
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

174

1.1
Location : requireDefault
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::requireDefault → KILLED

183

1.1
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

184

1.1
Location : readIndex
Killed by : none
Changed increment from 1 to -1 → SURVIVED
Covering tests

186

1.1
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

2.2
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

187

1.1
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

191

1.1
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

192

1.1
Location : readIndex
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:discoversModelsDeterministically()]
negated conditional → KILLED

201

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
removed call to java/util/Properties::load → KILLED

204

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

209

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

2.2
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

214

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

215

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

217

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

219

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

223

1.1
Location : readDescriptor
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced return value with null for org/egothor/stemmer/StemmerModelRegistry::readDescriptor → KILLED

232

1.1
Location : required
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

2.2
Location : required
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
negated conditional → KILLED

233

1.1
Location : required
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced return value with "" for org/egothor/stemmer/StemmerModelRegistry::required → KILLED

239

1.1
Location : requiredBoolean
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced boolean return with true for org/egothor/stemmer/StemmerModelRegistry::requiredBoolean → KILLED

2.2
Location : requiredBoolean
Killed by : org.egothor.stemmer.StemmerModelRegistryTest.[engine:junit-jupiter]/[class:org.egothor.stemmer.StemmerModelRegistryTest]/[method:rejectsMissingModel()]
replaced boolean return with false for org/egothor/stemmer/StemmerModelRegistry::requiredBoolean → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1