ScanOrchestrator.java

1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright 2026 Egothor
3
// Copyright 2026 Accenture
4
package org.egothor.methodatlas.command;
5
6
import java.io.IOException;
7
import java.nio.file.Path;
8
import java.util.ArrayList;
9
import java.util.LinkedHashMap;
10
import java.util.List;
11
import java.util.Map;
12
import java.util.logging.Level;
13
import java.util.logging.Logger;
14
import java.util.stream.Collectors;
15
16
import org.egothor.methodatlas.AiResultCache;
17
import org.egothor.methodatlas.emit.ClassificationOverride;
18
import org.egothor.methodatlas.CliConfig;
19
import org.egothor.methodatlas.emit.TestMethodSink;
20
import org.egothor.methodatlas.ai.AiClassSuggestion;
21
import org.egothor.methodatlas.ai.AiMethodSuggestion;
22
import org.egothor.methodatlas.ai.AiOptions;
23
import org.egothor.methodatlas.ai.AiSuggestionEngine;
24
import org.egothor.methodatlas.ai.AiSuggestionException;
25
import org.egothor.methodatlas.ai.PromptBuilder;
26
import org.egothor.methodatlas.ai.SuggestionLookup;
27
import org.egothor.methodatlas.api.DiscoveredMethod;
28
import org.egothor.methodatlas.api.TestDiscovery;
29
import org.egothor.methodatlas.api.TestDiscoveryConfig;
30
31
/**
32
 * Orchestrates the scan-and-emit loop that every command mode is built around.
33
 *
34
 * <p>
35
 * Each {@link Command} mode varies in three places — output format, per-record
36
 * sink behaviour, and whether records stream or buffer — but they all share
37
 * the same core sequence:
38
 * </p>
39
 * <ol>
40
 *   <li>Load all configured {@link TestDiscovery} providers.</li>
41
 *   <li>For each scan root, run every provider, merge their results, and
42
 *       group methods by class.</li>
43
 *   <li>For each class, optionally consult the AI engine through a layered
44
 *       cache + override lookup.</li>
45
 *   <li>Forward each method record to the supplied sink.</li>
46
 *   <li>Close all providers.</li>
47
 * </ol>
48
 *
49
 * <p>
50
 * This class owns that sequence. Commands compose it with a
51
 * {@link PluginLoader} (passed in at construction) and configure the
52
 * per-record sink, AI runtime, and content-hash policy at call time.
53
 * </p>
54
 *
55
 * <h2>API shape</h2>
56
 *
57
 * <p>
58
 * Two entry points serve the two common patterns:
59
 * </p>
60
 * <ul>
61
 *   <li>{@link #scan} manages the provider lifecycle internally; it is the
62
 *       right call for SARIF, JSON, and GitHub-annotation modes that buffer
63
 *       or emit unconditionally.</li>
64
 *   <li>{@link #runDiscovery} processes a single root against pre-loaded
65
 *       providers; it is the right call for CSV and plain-text modes that
66
 *       compute per-root metadata (such as the {@code source_root} column)
67
 *       before forwarding records.</li>
68
 * </ul>
69
 *
70
 * <p>
71
 * The apply-tags flow has its own shape: {@link #collectMethodsByFile}
72
 * groups discovered methods by source file (the caller owns the provider
73
 * lifecycle so it can read each provider's
74
 * {@link TestDiscovery#hadErrors()} afterwards), and
75
 * {@link #gatherAiSuggestionsForFile} resolves AI suggestions for one
76
 * file at a time.
77
 * </p>
78
 *
79
 * <h2>Thread safety</h2>
80
 *
81
 * <p>
82
 * This class is thread-safe. The injected {@link PluginLoader} is
83
 * thread-safe and {@link java.util.ServiceLoader} resolution is
84
 * idempotent; nothing else is shared between calls.
85
 * </p>
86
 *
87
 * @see PluginLoader
88
 * @see AiRuntime
89
 * @see Command
90
 * @since 1.0.0
91
 */
92
public final class ScanOrchestrator {
93
94
    private static final Logger LOG = Logger.getLogger(ScanOrchestrator.class.getName());
95
96
    private final PluginLoader pluginLoader;
97
98
    /**
99
     * Creates a new orchestrator that will resolve providers through
100
     * {@code pluginLoader}.
101
     *
102
     * @param pluginLoader plugin loader used by {@link #scan} and
103
     *                     {@link #collectMethodsByFile}; must not be
104
     *                     {@code null}
105
     */
106
    public ScanOrchestrator(PluginLoader pluginLoader) {
107
        this.pluginLoader = pluginLoader;
108
    }
109
110
    /**
111
     * Scans every configured root, forwarding each discovered test method to
112
     * {@code sink}. Loads and closes the {@link TestDiscovery} providers
113
     * internally so callers do not need to manage the lifecycle.
114
     *
115
     * @param roots           source roots to scan; must not be {@code null}
116
     * @param cliConfig       full parsed CLI configuration
117
     * @param discoveryConfig discovery configuration forwarded to providers
118
     * @param aiEngine        AI engine providing suggestions; may be
119
     *                        {@code null} when AI is disabled
120
     * @param sink            receiver of discovered test method records
121
     * @param override        human classification overrides
122
     * @param aiCache         AI result cache
123
     * @return {@code 0} if all files were processed successfully, {@code 1}
124
     *         if any file produced a parse or processing error
125
     * @throws IOException if traversing a file tree fails
126
     */
127
    public int scan(List<Path> roots, CliConfig cliConfig, TestDiscoveryConfig discoveryConfig,
128
            AiSuggestionEngine aiEngine, TestMethodSink sink,
129
            ClassificationOverride override, AiResultCache aiCache) throws IOException {
130
        List<TestDiscovery> providers = pluginLoader.loadProviders(discoveryConfig);
131
        boolean hadErrors = false;
132
        try {
133
            for (Path root : roots) {
134 2 1. scan : removed conditional - replaced equality check with false → SURVIVED
2. scan : removed conditional - replaced equality check with true → SURVIVED
                if (runDiscovery(root, providers, cliConfig.aiOptions(), aiEngine, sink,
135
                        cliConfig.contentHash(), override, aiCache)) {
136
                    hadErrors = true;
137
                }
138
            }
139
        } finally {
140 1 1. scan : removed call to org/egothor/methodatlas/command/PluginLoader::closeAll → SURVIVED
            pluginLoader.closeAll(providers);
141
        }
142 2 1. scan : removed conditional - replaced equality check with true → SURVIVED
2. scan : removed conditional - replaced equality check with false → SURVIVED
        return hadErrors ? 1 : 0;
143
    }
144
145
    /**
146
     * Runs all configured {@link TestDiscovery} providers on {@code root},
147
     * merges their results, orchestrates AI analysis per class, and forwards
148
     * each method record to {@code sink}.
149
     *
150
     * <p>
151
     * Providers are passed in pre-loaded; callers manage the lifecycle
152
     * (typically through {@link PluginLoader#closeAll(List)} in a
153
     * {@code finally} block) so that they can share one provider list across
154
     * multiple roots while still computing per-root metadata before each
155
     * call.
156
     * </p>
157
     *
158
     * @param root               directory to scan
159
     * @param providers          list of pre-configured discovery providers
160
     * @param aiOptions          AI configuration for the current run
161
     * @param aiEngine           AI engine, or {@code null} when AI is disabled
162
     * @param sink               receiver of discovered test method records
163
     * @param contentHashEnabled whether to include the class content hash in
164
     *                           emitted records
165
     * @param override           human classification overrides
166
     * @param aiCache            AI result cache
167
     * @return {@code true} if any provider encountered a parse or processing
168
     *         error
169
     * @throws IOException if traversing the file tree fails
170
     */
171
    @SuppressWarnings("PMD.CloseResource") // providers are owned by the caller; this method does not close them
172
    public boolean runDiscovery(Path root, List<TestDiscovery> providers,
173
            AiOptions aiOptions, AiSuggestionEngine aiEngine, TestMethodSink sink,
174
            boolean contentHashEnabled, ClassificationOverride override,
175
            AiResultCache aiCache) throws IOException {
176
177
        List<DiscoveredMethod> methods = new ArrayList<>();
178
        boolean hadErrors = false;
179
        for (TestDiscovery provider : providers) {
180 1 1. runDiscovery : removed call to java/util/stream/Stream::forEach → KILLED
            provider.discover(root).forEach(methods::add);
181 2 1. runDiscovery : removed conditional - replaced equality check with true → SURVIVED
2. runDiscovery : removed conditional - replaced equality check with false → KILLED
            if (provider.hadErrors()) {
182
                hadErrors = true;
183
            }
184
        }
185
186
        Map<String, List<DiscoveredMethod>> byClass = methods.stream()
187
                .collect(Collectors.groupingBy(DiscoveredMethod::fqcn,
188
                        LinkedHashMap::new, Collectors.toList()));
189
190
        AiRuntime ai = new AiRuntime(aiOptions, aiEngine, override, aiCache);
191
192
        for (Map.Entry<String, List<DiscoveredMethod>> entry : byClass.entrySet()) {
193
            String fqcn = entry.getKey();
194
            List<DiscoveredMethod> classMethods = entry.getValue();
195
196
            String classSource = classMethods.get(0).sourceContent().get().orElse(null);
197
198 6 1. runDiscovery : removed conditional - replaced equality check with true → SURVIVED
2. runDiscovery : removed conditional - replaced equality check with true → SURVIVED
3. runDiscovery : removed conditional - replaced equality check with false → SURVIVED
4. runDiscovery : removed conditional - replaced equality check with false → SURVIVED
5. runDiscovery : removed conditional - replaced equality check with false → KILLED
6. runDiscovery : removed conditional - replaced equality check with true → KILLED
            String lookupHash = (contentHashEnabled || aiCache.isActive()) && classSource != null
199
                    ? ContentHasher.hashClass(classSource) : null;
200 2 1. runDiscovery : removed conditional - replaced equality check with true → SURVIVED
2. runDiscovery : removed conditional - replaced equality check with false → KILLED
            String outputHash = contentHashEnabled ? lookupHash : null;
201
202
            String fileStem = classMethods.get(0).fileStem();
203
            List<String> methodNames = classMethods.stream().map(DiscoveredMethod::method).toList();
204
            List<PromptBuilder.TargetMethod> targetMethods = classMethods.stream()
205
                    .map(ScanOrchestrator::toTargetMethod)
206
                    .toList();
207
208
            SuggestionLookup suggestions = resolveSuggestionLookup(
209
                    fileStem, fqcn, classSource, methodNames, targetMethods, ai, lookupHash);
210
211
            for (DiscoveredMethod m : classMethods) {
212 1 1. runDiscovery : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
                sink.record(m.fqcn(), m.method(), m.beginLine(), m.loc(), outputHash,
213
                        m.tags(), m.displayName(),
214
                        suggestions.find(m.method()).orElse(null));
215
            }
216
        }
217
218 2 1. runDiscovery : replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED
2. runDiscovery : replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED
        return hadErrors;
219
    }
220
221
    /**
222
     * Collects all discovered methods from every configured root, keyed by
223
     * source-file path. Methods whose {@link DiscoveredMethod#filePath()} is
224
     * {@code null} are silently skipped.
225
     *
226
     * <p>
227
     * Providers are passed in pre-loaded so the caller can read each
228
     * provider's {@link TestDiscovery#hadErrors()} after the call and decide
229
     * how to propagate the exit code; the caller also owns closing them.
230
     * </p>
231
     *
232
     * @param roots     scan roots; must not be {@code null}
233
     * @param providers configured and already-loaded discovery providers;
234
     *                  must not be {@code null}
235
     * @return mutable map from source-file path to the methods found in that
236
     *         file; insertion order matches discovery order
237
     * @throws IOException if directory traversal fails for any root
238
     */
239
    @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops",
240
            "PMD.CloseResource"}) // providers are owned by the caller
241
    public Map<Path, List<DiscoveredMethod>> collectMethodsByFile(
242
            List<Path> roots, List<TestDiscovery> providers) throws IOException {
243
        Map<Path, List<DiscoveredMethod>> byFile = new LinkedHashMap<>();
244
        for (Path root : roots) {
245
            for (TestDiscovery provider : providers) {
246 1 1. collectMethodsByFile : removed call to java/util/stream/Stream::forEach → KILLED
                provider.discover(root).forEach(m -> {
247 2 1. lambda$collectMethodsByFile$1 : removed conditional - replaced equality check with true → SURVIVED
2. lambda$collectMethodsByFile$1 : removed conditional - replaced equality check with false → KILLED
                    if (m.filePath() != null) {
248 1 1. lambda$collectMethodsByFile$0 : replaced return value with Collections.emptyList for org/egothor/methodatlas/command/ScanOrchestrator::lambda$collectMethodsByFile$0 → KILLED
                        byFile.computeIfAbsent(m.filePath(), k -> new ArrayList<>()).add(m);
249
                    }
250
                });
251
            }
252
        }
253 1 1. collectMethodsByFile : replaced return value with Collections.emptyMap for org/egothor/methodatlas/command/ScanOrchestrator::collectMethodsByFile → KILLED
        return byFile;
254
    }
255
256
    /**
257
     * Resolves AI security-classification suggestions for every class in
258
     * {@code byClass} and populates {@code tagsToApply} and
259
     * {@code displayNames} with the results for methods that are
260
     * security-relevant.
261
     *
262
     * <p>
263
     * A display-name suggestion is only placed into {@code displayNames}
264
     * when the discovered method has no existing {@code @DisplayName} in
265
     * source (i.e. {@link DiscoveredMethod#displayName()} returns
266
     * {@code null}). This prevents AI-generated names from overwriting
267
     * manually authored ones.
268
     * </p>
269
     *
270
     * @param byClass      discovered methods grouped by FQCN for one source file
271
     * @param ai           AI runtime carrying the engine, override, and cache
272
     * @param aiCache      AI result cache used to compute the content-hash lookup key
273
     * @param tagsToApply  output accumulator: method name to tag values to write
274
     * @param displayNames output accumulator: method name to display name to write
275
     */
276
    public void gatherAiSuggestionsForFile(Map<String, List<DiscoveredMethod>> byClass,
277
            AiRuntime ai, AiResultCache aiCache,
278
            Map<String, List<String>> tagsToApply, Map<String, String> displayNames) {
279
        for (Map.Entry<String, List<DiscoveredMethod>> classEntry : byClass.entrySet()) {
280
            String fqcn = classEntry.getKey();
281
            List<DiscoveredMethod> classMethods = classEntry.getValue();
282
283
            String classSource = classMethods.get(0).sourceContent().get().orElse(null);
284 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → NO_COVERAGE
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → NO_COVERAGE
            String lookupHash = aiCache.isActive() && classSource != null
285
                    ? ContentHasher.hashClass(classSource) : null;
286
            String fileStem = classMethods.get(0).fileStem();
287
            List<String> methodNames = classMethods.stream().map(DiscoveredMethod::method).toList();
288
            List<PromptBuilder.TargetMethod> targetMethods = classMethods.stream()
289
                    .map(ScanOrchestrator::toTargetMethod).toList();
290
291
            SuggestionLookup suggestions = resolveSuggestionLookup(
292
                    fileStem, fqcn, classSource, methodNames, targetMethods, ai, lookupHash);
293
294
            for (DiscoveredMethod m : classMethods) {
295
                AiMethodSuggestion suggestion = suggestions.find(m.method()).orElse(null);
296 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
                if (suggestion == null || !suggestion.securityRelevant()) {
297
                    continue;
298
                }
299 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
                if (suggestion.displayName() != null && !suggestion.displayName().isBlank()
300 2 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → KILLED
                        && m.displayName() == null) {
301
                    displayNames.putIfAbsent(m.method(), suggestion.displayName());
302
                }
303 4 1. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
2. gatherAiSuggestionsForFile : removed conditional - replaced equality check with true → SURVIVED
3. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
4. gatherAiSuggestionsForFile : removed conditional - replaced equality check with false → KILLED
                if (suggestion.tags() != null && !suggestion.tags().isEmpty()) {
304
                    tagsToApply.putIfAbsent(m.method(), suggestion.tags());
305
                }
306
            }
307
        }
308
    }
309
310
    /**
311
     * Wraps a {@link TestMethodSink} so that only records that pass all
312
     * active filters are forwarded to {@code delegate}.
313
     *
314
     * <p>
315
     * Two independent filters are supported and composed in order:
316
     * </p>
317
     * <ol>
318
     *   <li><b>Security-only filter</b> — when {@code securityOnly} is
319
     *       {@code true}, records whose {@link AiMethodSuggestion} is
320
     *       {@code null} or has {@code securityRelevant=false} are dropped.</li>
321
     *   <li><b>Confidence threshold filter</b> — when {@code confidenceEnabled}
322
     *       is {@code true} <em>and</em> {@code minConfidence > 0.0}, records
323
     *       whose {@link AiMethodSuggestion} is {@code null} or has a
324
     *       {@link AiMethodSuggestion#confidence()} below {@code minConfidence}
325
     *       are dropped. This filter is a no-op when {@code confidenceEnabled}
326
     *       is {@code false} because the confidence field is always
327
     *       {@code 0.0} when confidence scoring was not requested.</li>
328
     * </ol>
329
     *
330
     * <p>
331
     * When neither filter is active the original {@code delegate} is
332
     * returned unchanged (zero overhead).
333
     * </p>
334
     *
335
     * @param delegate          the underlying sink to forward matching
336
     *                          records to
337
     * @param securityOnly      whether to enable the security-relevance filter
338
     * @param minConfidence     minimum confidence score (inclusive) required
339
     *                          to pass the confidence filter; {@code 0.0}
340
     *                          disables it
341
     * @param confidenceEnabled whether confidence scoring was requested;
342
     *                          must be {@code true} for the confidence
343
     *                          filter to activate
344
     * @return filtered sink, or {@code delegate} unchanged when all filters
345
     *         are off
346
     */
347
    public TestMethodSink filterSink(TestMethodSink delegate, boolean securityOnly,
348
            double minConfidence, boolean confidenceEnabled) {
349
        TestMethodSink sink = delegate;
350 2 1. filterSink : removed conditional - replaced equality check with false → KILLED
2. filterSink : removed conditional - replaced equality check with true → KILLED
        if (securityOnly) {
351
            final TestMethodSink next = sink;
352
            sink = (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) -> {
353 4 1. lambda$filterSink$2 : removed conditional - replaced equality check with true → KILLED
2. lambda$filterSink$2 : removed conditional - replaced equality check with false → KILLED
3. lambda$filterSink$2 : removed conditional - replaced equality check with true → KILLED
4. lambda$filterSink$2 : removed conditional - replaced equality check with false → KILLED
                if (suggestion != null && suggestion.securityRelevant()) {
354 1 1. lambda$filterSink$2 : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
                    next.record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
355
                }
356
            };
357
        }
358 5 1. filterSink : removed conditional - replaced comparison check with true → SURVIVED
2. filterSink : changed conditional boundary → SURVIVED
3. filterSink : removed conditional - replaced equality check with true → KILLED
4. filterSink : removed conditional - replaced comparison check with false → KILLED
5. filterSink : removed conditional - replaced equality check with false → KILLED
        if (confidenceEnabled && minConfidence > 0.0) {
359
            final double threshold = minConfidence;
360
            final TestMethodSink next = sink;
361
            sink = (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) -> {
362 5 1. lambda$filterSink$3 : changed conditional boundary → SURVIVED
2. lambda$filterSink$3 : removed conditional - replaced comparison check with false → KILLED
3. lambda$filterSink$3 : removed conditional - replaced equality check with true → KILLED
4. lambda$filterSink$3 : removed conditional - replaced equality check with false → KILLED
5. lambda$filterSink$3 : removed conditional - replaced comparison check with true → KILLED
                if (suggestion != null && suggestion.confidence() >= threshold) {
363 1 1. lambda$filterSink$3 : removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED
                    next.record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
364
                }
365
            };
366
        }
367 1 1. filterSink : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::filterSink → KILLED
        return sink;
368
    }
369
370
    // -------------------------------------------------------------------------
371
    // Static utilities
372
    // -------------------------------------------------------------------------
373
374
    /**
375
     * Converts a single discovered test method into a prompt target descriptor.
376
     *
377
     * <p>
378
     * Exposed publicly because {@link ManualPrepareCommand} also needs to
379
     * build prompt-target lists from discovered methods when writing manual
380
     * work files; the conversion logic must stay aligned across both call
381
     * sites to keep the prompt format consistent.
382
     * </p>
383
     *
384
     * @param m discovered test method; must not be {@code null}
385
     * @return corresponding prompt target descriptor; never {@code null}
386
     * @see PromptBuilder.TargetMethod
387
     */
388
    public static PromptBuilder.TargetMethod toTargetMethod(DiscoveredMethod m) {
389 1 1. toTargetMethod : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::toTargetMethod → KILLED
        return new PromptBuilder.TargetMethod(
390
                m.method(),
391 3 1. toTargetMethod : changed conditional boundary → KILLED
2. toTargetMethod : removed conditional - replaced comparison check with true → KILLED
3. toTargetMethod : removed conditional - replaced comparison check with false → KILLED
                m.beginLine() > 0 ? m.beginLine() : null,
392 3 1. toTargetMethod : removed conditional - replaced comparison check with true → KILLED
2. toTargetMethod : removed conditional - replaced comparison check with false → KILLED
3. toTargetMethod : changed conditional boundary → KILLED
                m.endLine() > 0 ? m.endLine() : null);
393
    }
394
395
    // -------------------------------------------------------------------------
396
    // Private helpers
397
    // -------------------------------------------------------------------------
398
399
    private static SuggestionLookup resolveSuggestionLookup(String fileStem, String fqcn,
400
            String classSource, List<String> methodNames, List<PromptBuilder.TargetMethod> targetMethods,
401
            AiRuntime ai, String contentHash) {
402 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → SURVIVED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
        if (methodNames.isEmpty()) {
403 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE
            return SuggestionLookup.from(null);
404
        }
405
406 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
        if (ai.engine() == null) {
407 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames));
408
        }
409
410
        // Check the cache before making an API call.
411
        AiClassSuggestion cached = ai.cache().lookup(contentHash).orElse(null);
412 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
2. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
        if (cached != null) {
413 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return SuggestionLookup.from(ai.override().apply(fqcn, cached, methodNames));
414
        }
415
416 2 1. resolveSuggestionLookup : removed conditional - replaced equality check with false → SURVIVED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → KILLED
        if (classSource == null) {
417 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE
            return SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames));
418
        }
419
420 5 1. resolveSuggestionLookup : changed conditional boundary → SURVIVED
2. resolveSuggestionLookup : removed conditional - replaced equality check with true → SURVIVED
3. resolveSuggestionLookup : removed conditional - replaced comparison check with true → KILLED
4. resolveSuggestionLookup : removed conditional - replaced equality check with false → KILLED
5. resolveSuggestionLookup : removed conditional - replaced comparison check with false → KILLED
        if (ai.options().enabled() && classSource.length() > ai.options().maxClassChars()) {
421
            if (LOG.isLoggable(Level.INFO)) {
422
                LOG.log(Level.INFO, "Skipping AI for {0}: class source too large ({1} chars)",
423
                        new Object[] { fqcn, classSource.length() });
424
            }
425 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames));
426
        }
427
428
        if (LOG.isLoggable(Level.INFO)) {
429
            LOG.log(Level.INFO, "Querying AI for {0} ({1} methods)", new Object[] { fqcn, targetMethods.size() });
430
        }
431
432
        try {
433
            AiClassSuggestion aiClassSuggestion =
434
                    ai.engine().suggestForClass(fileStem, fqcn, classSource, targetMethods);
435 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return SuggestionLookup.from(ai.override().apply(fqcn, aiClassSuggestion, methodNames));
436
        } catch (AiSuggestionException e) {
437
            if (LOG.isLoggable(Level.WARNING)) {
438
                LOG.log(Level.WARNING, "AI suggestion failed for class " + fqcn, e);
439
            }
440 1 1. resolveSuggestionLookup : replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED
            return SuggestionLookup.from(ai.override().apply(fqcn, null, methodNames));
441
        }
442
    }
443
}

Mutations

134

1.1
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

140

1.1
Location : scan
Killed by : none
removed call to org/egothor/methodatlas/command/PluginLoader::closeAll → SURVIVED
Covering tests

142

1.1
Location : scan
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : scan
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

180

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_noPackageDeclaration_usesSimpleClassNameAsFqcn(java.nio.file.Path)]
removed call to java/util/stream/Stream::forEach → KILLED

181

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_unparseableFile_returnsExitCode1(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

198

1.1
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

3.3
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppContentHashTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppContentHashTest]/[method:plainMode_hashTokenPresent_whenEnabled(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

4.4
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

5.5
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

6.6
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppContentHashTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppContentHashTest]/[method:plainMode_hashTokenPresent_whenEnabled(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

200

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppContentHashTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppContentHashTest]/[method:plainMode_hashTokenPresent_whenEnabled(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : runDiscovery
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

212

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_noPackageDeclaration_usesSimpleClassNameAsFqcn(java.nio.file.Path)]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

218

1.1
Location : runDiscovery
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:csvMode_unparseableFile_returnsExitCode1(java.nio.file.Path)]
replaced boolean return with false for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → KILLED

2.2
Location : runDiscovery
Killed by : none
replaced boolean return with true for org/egothor/methodatlas/command/ScanOrchestrator::runDiscovery → SURVIVED
Covering tests

246

1.1
Location : collectMethodsByFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed call to java/util/stream/Stream::forEach → KILLED

247

1.1
Location : lambda$collectMethodsByFile$1
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : lambda$collectMethodsByFile$1
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

248

1.1
Location : lambda$collectMethodsByFile$0
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
replaced return value with Collections.emptyList for org/egothor/methodatlas/command/ScanOrchestrator::lambda$collectMethodsByFile$0 → KILLED

253

1.1
Location : collectMethodsByFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
replaced return value with Collections.emptyMap for org/egothor/methodatlas/command/ScanOrchestrator::collectMethodsByFile → KILLED

284

1.1
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → NO_COVERAGE

3.3
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

4.4
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

296

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

3.3
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_nonSecurityMethod_notAnnotated(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

4.4
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

299

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

3.3
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

4.4
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_addsTagImport(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

300

1.1
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_existingAnnotationsNotDuplicated(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

303

1.1
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED
Covering tests

2.2
Location : gatherAiSuggestionsForFile
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

3.3
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

4.4
Location : gatherAiSuggestionsForFile
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

350

1.1
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_neitherFilterActive_returnsDelegateUnchanged()]
removed conditional - replaced equality check with true → KILLED

353

1.1
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with true → KILLED

2.2
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with true → KILLED

4.4
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed conditional - replaced equality check with false → KILLED

354

1.1
Location : lambda$filterSink$2
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_securityOnly_dropsNonSecurityRecords()]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

358

1.1
Location : filterSink
Killed by : none
removed conditional - replaced comparison check with true → SURVIVED
Covering tests

2.2
Location : filterSink
Killed by : none
changed conditional boundary → SURVIVED Covering tests

3.3
Location : filterSink
Killed by : org.egothor.methodatlas.MethodAtlasAppJsonTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppJsonTest]/[method:minConfidence_noEffectWithoutAiConfidence(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

4.4
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with false → KILLED

5.5
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with false → KILLED

362

1.1
Location : lambda$filterSink$3
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with false → KILLED

3.3
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with true → KILLED

4.4
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced equality check with false → KILLED

5.5
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed conditional - replaced comparison check with true → KILLED

363

1.1
Location : lambda$filterSink$3
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_confidenceFilter_dropsLowConfidenceRecords()]
removed call to org/egothor/methodatlas/emit/TestMethodSink::record → KILLED

367

1.1
Location : filterSink
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:filterSink_neitherFilterActive_returnsDelegateUnchanged()]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::filterSink → KILLED

389

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::toTargetMethod → KILLED

391

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
changed conditional boundary → KILLED

2.2
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
removed conditional - replaced comparison check with true → KILLED

3.3
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_validBeginAndEndLines_preservesBoth()]
removed conditional - replaced comparison check with false → KILLED

392

1.1
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
removed conditional - replaced comparison check with true → KILLED

2.2
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_validBeginAndEndLines_preservesBoth()]
removed conditional - replaced comparison check with false → KILLED

3.3
Location : toTargetMethod
Killed by : org.egothor.methodatlas.command.ScanOrchestratorTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.command.ScanOrchestratorTest]/[method:toTargetMethod_nonPositiveLineNumbers_collapseToNull()]
changed conditional boundary → KILLED

402

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

403

1.1
Location : resolveSuggestionLookup
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE

406

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

407

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsUnsupportedLanguageTest]/[method:applyTags_javaOnly_noSkipCountInSummary(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

412

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:aiCache_hit_engineNotCalledForUnchangedClass(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

413

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:aiCache_hit_engineNotCalledForUnchangedClass(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

416

1.1
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced equality check with true → KILLED

417

1.1
Location : resolveSuggestionLookup
Killed by : none
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → NO_COVERAGE

420

1.1
Location : resolveSuggestionLookup
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_withAi_annotatesSecurityRelevantMethod(java.nio.file.Path)]
removed conditional - replaced comparison check with true → KILLED

3.3
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
removed conditional - replaced equality check with false → KILLED

4.4
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
removed conditional - replaced comparison check with false → KILLED

5.5
Location : resolveSuggestionLookup
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

425

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:csvMode_oversizedClass_skipsAiLookup_andLeavesAiColumnsEmpty(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

435

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppApplyTagsTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppApplyTagsTest]/[method:applyTags_noImportsAddedWhenNothingAnnotated(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

440

1.1
Location : resolveSuggestionLookup
Killed by : org.egothor.methodatlas.MethodAtlasAppAiTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppAiTest]/[method:plainMode_aiFailureForOneClass_continuesScanningAndFallsBackForThatClass(java.nio.file.Path)]
replaced return value with null for org/egothor/methodatlas/command/ScanOrchestrator::resolveSuggestionLookup → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1