ScanCommand.java

1
package org.egothor.methodatlas.command;
2
3
import java.io.IOException;
4
import java.io.PrintWriter;
5
import java.nio.file.Path;
6
import java.nio.file.Paths;
7
import java.time.Instant;
8
import java.util.List;
9
import java.util.logging.Level;
10
import java.util.logging.Logger;
11
12
import org.egothor.methodatlas.AiResultCache;
13
import org.egothor.methodatlas.ClassificationOverride;
14
import org.egothor.methodatlas.CliConfig;
15
import org.egothor.methodatlas.OutputMode;
16
import org.egothor.methodatlas.TestMethodSink;
17
import org.egothor.methodatlas.ai.AiSuggestionEngine;
18
import org.egothor.methodatlas.api.TestDiscovery;
19
import org.egothor.methodatlas.api.TestDiscoveryConfig;
20
import org.egothor.methodatlas.emit.OutputEmitter;
21
22
/**
23
 * CLI command handler for the default CSV and {@code -plain} output modes.
24
 *
25
 * <p>
26
 * Scans one or more source roots, optionally enriches the output with AI
27
 * suggestions, and emits test-method records incrementally to the supplied
28
 * writer.
29
 * </p>
30
 *
31
 * @see org.egothor.methodatlas.emit.OutputEmitter
32
 * @see SarifCommand
33
 * @see GitHubAnnotationsCommand
34
 */
35
public final class ScanCommand implements Command {
36
37
    private static final Logger LOG = Logger.getLogger(ScanCommand.class.getName());
38
39
    private final CliConfig cliConfig;
40
    private final TestDiscoveryConfig discoveryConfig;
41
    private final AiSuggestionEngine aiEngine;
42
    private final ClassificationOverride override;
43
    private final AiResultCache aiCache;
44
45
    /**
46
     * Creates a new scan command.
47
     *
48
     * @param cliConfig       full parsed CLI configuration
49
     * @param discoveryConfig discovery configuration forwarded to providers
50
     * @param aiEngine        AI engine providing suggestions; {@code null} when
51
     *                        AI is disabled
52
     * @param override        human classification overrides
53
     * @param aiCache         AI result cache
54
     */
55
    public ScanCommand(CliConfig cliConfig, TestDiscoveryConfig discoveryConfig,
56
            AiSuggestionEngine aiEngine, ClassificationOverride override,
57
            AiResultCache aiCache) {
58
        this.cliConfig = cliConfig;
59
        this.discoveryConfig = discoveryConfig;
60
        this.aiEngine = aiEngine;
61
        this.override = override;
62
        this.aiCache = aiCache;
63
    }
64
65
    /**
66
     * Runs the scan and emits output incrementally.
67
     *
68
     * @param out writer that receives all emitted output
69
     * @return {@code 0} if all files were processed successfully, {@code 1} if
70
     *         any file produced a parse or processing error
71
     * @throws IOException if traversing a file tree fails
72
     */
73
    @Override
74
    @SuppressWarnings("PMD.NPathComplexity") // combinatorial expansion of optional CSV columns; inherent to the format
75
    public int execute(PrintWriter out) throws IOException {
76 2 1. execute : removed conditional - replaced equality check with false → KILLED
2. execute : removed conditional - replaced equality check with true → KILLED
        boolean aiEnabled = aiEngine != null;
77 4 1. execute : removed conditional - replaced equality check with true → SURVIVED
2. execute : removed conditional - replaced equality check with false → SURVIVED
3. execute : removed conditional - replaced equality check with false → SURVIVED
4. execute : removed conditional - replaced equality check with true → KILLED
        boolean confidenceEnabled = aiEnabled && cliConfig.aiOptions().confidence();
78
        boolean contentHashEnabled = cliConfig.contentHash();
79 2 1. execute : removed conditional - replaced equality check with false → SURVIVED
2. execute : removed conditional - replaced equality check with true → KILLED
        List<Path> roots = cliConfig.paths().isEmpty() ? List.of(Paths.get(".")) : cliConfig.paths();
80
81
        OutputEmitter emitter = new OutputEmitter(out, aiEnabled, confidenceEnabled, contentHashEnabled,
82
                cliConfig.driftDetect(), cliConfig.emitSourceRoot());
83
84 2 1. execute : removed conditional - replaced equality check with true → KILLED
2. execute : removed conditional - replaced equality check with false → KILLED
        if (cliConfig.emitMetadata()) {
85
            String version = ScanCommand.class.getPackage().getImplementationVersion();
86
            String taxonomyInfo = CommandSupport.resolveTaxonomyInfo(cliConfig.aiOptions(), aiEnabled);
87 3 1. execute : removed conditional - replaced equality check with false → SURVIVED
2. execute : removed conditional - replaced equality check with true → SURVIVED
3. execute : removed call to org/egothor/methodatlas/emit/OutputEmitter::emitMetadata → KILLED
            emitter.emitMetadata(version != null ? version : "dev", Instant.now().toString(), taxonomyInfo);
88
        }
89
90 1 1. execute : removed call to org/egothor/methodatlas/emit/OutputEmitter::emitCsvHeader → KILLED
        emitter.emitCsvHeader(cliConfig.outputMode());
91
92
        final OutputMode mode = cliConfig.outputMode();
93
        final boolean emitSourceRoot = cliConfig.emitSourceRoot();
94
95
        // Scan each root with its own sink so the source_root value can be captured
96
        // per root. When emitSourceRoot is false, sourceRoot is null and the column
97
        // is omitted from the output.
98
        List<TestDiscovery> providers = CommandSupport.loadProviders(discoveryConfig);
99
        boolean hadErrors = false;
100
        try {
101
            for (Path root : roots) {
102 2 1. execute : removed conditional - replaced equality check with false → SURVIVED
2. execute : removed conditional - replaced equality check with true → SURVIVED
                String sourceRoot = emitSourceRoot ? CommandSupport.computeFilePrefix(List.of(root)) : null;
103
                TestMethodSink rootSink = (fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion) ->
104 1 1. lambda$execute$0 : removed call to org/egothor/methodatlas/emit/OutputEmitter::emit → KILLED
                        emitter.emit(mode, fqcn, method, loc, contentHash, tags, displayName, suggestion, sourceRoot);
105 2 1. execute : removed conditional - replaced equality check with true → SURVIVED
2. execute : removed conditional - replaced equality check with false → KILLED
                if (CommandSupport.runDiscovery(root, providers, cliConfig.aiOptions(), aiEngine,
106
                        CommandSupport.filterSink(rootSink, cliConfig.securityOnly()),
107
                        cliConfig.contentHash(), override, aiCache)) {
108
                    hadErrors = true;
109
                }
110
            }
111
        } finally {
112 1 1. execute : removed call to org/egothor/methodatlas/command/CommandSupport::closeAll → SURVIVED
            CommandSupport.closeAll(providers);
113
        }
114
115
        if (aiCache.isActive() && LOG.isLoggable(Level.INFO)) {
116
            LOG.log(Level.INFO, "AI cache: {0} hit(s), {1} miss(es)",
117
                    new Object[] { aiCache.hits(), aiCache.misses() });
118
        }
119 2 1. execute : removed conditional - replaced equality check with true → SURVIVED
2. execute : removed conditional - replaced equality check with false → KILLED
        return hadErrors ? 1 : 0;
120
    }
121
}

Mutations

76

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

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

77

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

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

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

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

79

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

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

84

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

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

87

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

2.2
Location : execute
Killed by : org.egothor.methodatlas.MethodAtlasAppScanCoverageTest.[engine:junit-jupiter]/[class:org.egothor.methodatlas.MethodAtlasAppScanCoverageTest]/[method:emitMetadata_aiEnabled_emitsBuiltinDefaultTaxonomy(java.nio.file.Path)]
removed call to org/egothor/methodatlas/emit/OutputEmitter::emitMetadata → KILLED

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

90

1.1
Location : execute
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/OutputEmitter::emitCsvHeader → KILLED

102

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

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

104

1.1
Location : lambda$execute$0
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/OutputEmitter::emit → KILLED

105

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

2.2
Location : execute
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

112

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

119

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

2.2
Location : execute
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

Active mutators

Tests examined


Report generated by PIT 1.22.1