From fd635c17fe6493dddef61a71f253e2db6caa9b34 Mon Sep 17 00:00:00 2001
From: D061177
Date: Sun, 5 Jul 2026 16:09:00 +0200
Subject: [PATCH] Add --cache flag to generate-map and cache auto-resolve to
query
generate-map: new --cache flag writes the generated map to the local cache at ~/.gradle/smart-test-picker/PROJECT_NAME/local-coverage-map.json so that select-tests and query can resolve it automatically.
query: --map is no longer required. When omitted, resolves from local cache using --prefer-map mode (nearest/remote/local), same behavior as select-tests. Adds --project-dir option for cache directory resolution.
---
.../cli/GenerateMapCommand.java | 27 ++++++---
.../oss/smarttestpicker/cli/QueryCommand.java | 57 +++++++++++++++++--
2 files changed, 70 insertions(+), 14 deletions(-)
diff --git a/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/GenerateMapCommand.java b/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/GenerateMapCommand.java
index 269c64c..6d7725f 100644
--- a/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/GenerateMapCommand.java
+++ b/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/GenerateMapCommand.java
@@ -7,6 +7,7 @@
import java.util.concurrent.Callable;
import com.sap.oss.smarttestpicker.engine.CoverageMapEngine;
+import com.sap.oss.smarttestpicker.store.CoverageMapResolver;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -18,16 +19,12 @@
* records which classes and methods each test covers, along with git metadata
* (commitId, baseBranch, timestamp) for change detection.
*
- * Supports three output formats:
- *
- * - Plain JSON (default) -- human-readable, with pretty printing
- * - Indexed JSON ({@code --indexed}) -- deduplicated class/method strings with
- * integer references, typically 10-15x smaller
- * - Gzip ({@code --gzip}) -- additional compression, combinable with indexed format
- *
+ * When {@code --cache} is specified, the generated map is also written to the
+ * local cache at {@code ~/.gradle/smart-test-picker/PROJECT_NAME/local-coverage-map.json}
+ * so that {@code select-tests} and {@code query} can resolve it automatically.
*
* @see CoverageMapEngine
- * @see com.sap.oss.smarttestpicker.mapper.IndexedCoverageMap
+ * @see CoverageMapResolver
*/
@Command(
name = "generate-map",
@@ -61,6 +58,10 @@ public class GenerateMapCommand implements Callable
description = "Compress output with gzip")
private boolean gzip;
+ @Option(names = "--cache",
+ description = "Also write to local cache (~/.gradle/smart-test-picker/PROJECT_NAME/local-coverage-map.json)")
+ private boolean cache;
+
@Override
public Integer call()
{
@@ -82,10 +83,20 @@ public Integer call()
logger.info("Base branch: {}", baseBranch);
logger.info("Indexed: {}", indexed);
logger.info("Gzip: {}", gzip);
+ logger.info("Cache: {}", cache);
try
{
new CoverageMapEngine().generate(xmlDir, output, baseBranch, projectDir, logger, indexed, gzip);
+
+ if (cache)
+ {
+ CoverageMapResolver resolver = new CoverageMapResolver(projectDir, logger);
+ File cacheFile = resolver.getLocalMapPath();
+ new CoverageMapEngine().generate(xmlDir, cacheFile, baseBranch, projectDir, logger, indexed, gzip);
+ logger.info("[SmartTestPicker] Cached local map: {}", cacheFile.getAbsolutePath());
+ }
+
return 0;
}
catch (IOException e)
diff --git a/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/QueryCommand.java b/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/QueryCommand.java
index 30ea5aa..377c8d2 100644
--- a/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/QueryCommand.java
+++ b/smart-test-picker-cli/src/main/java/com/sap/oss/smarttestpicker/cli/QueryCommand.java
@@ -11,6 +11,7 @@
import com.sap.oss.smarttestpicker.mapper.CoverageMap;
import com.sap.oss.smarttestpicker.mapper.CoverageMapReader;
+import com.sap.oss.smarttestpicker.store.CoverageMapResolver;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -19,6 +20,10 @@
/**
* CLI subcommand for querying a coverage map interactively.
*
+ * The coverage map can be provided explicitly via {@code --map}, or resolved
+ * automatically from the local cache using the {@code --prefer-map} mode
+ * (same behavior as {@code select-tests}).
+ *
* Provides multiple query modes for debugging and exploring test coverage data:
*
* - {@code --test} -- show all classes and methods covered by a specific test
@@ -28,10 +33,8 @@
* - {@code --stats} -- display aggregate statistics (totals, min/max/avg coverage)
*
*
- * Reads all three coverage map formats transparently: plain JSON, indexed JSON,
- * and gzip-compressed files.
- *
* @see CoverageMapReader
+ * @see CoverageMapResolver
*/
@Command(
name = "query",
@@ -41,10 +44,18 @@
public class QueryCommand implements Callable
{
- @Option(names = "--map", required = true,
- description = "Coverage map JSON file")
+ @Option(names = "--map",
+ description = "Coverage map JSON file. If not provided, resolves from local cache.")
private File mapFile;
+ @Option(names = "--prefer-map", defaultValue = "nearest",
+ description = "Map selection mode when using cache: nearest, remote, local (default: nearest)")
+ private String preferMap;
+
+ @Option(names = "--project-dir",
+ description = "Project root directory (default: current dir)")
+ private File projectDir;
+
@ArgGroup(exclusive = true, multiplicity = "1")
private QueryMode queryMode;
@@ -74,7 +85,27 @@ static class QueryMode
@Override
public Integer call()
{
- if (!mapFile.exists())
+ if (projectDir == null)
+ {
+ projectDir = new File(System.getProperty("user.dir"));
+ }
+
+ ConsoleLogger logger = new ConsoleLogger();
+
+ // Resolve map: explicit --map takes precedence over cache
+ if (mapFile == null)
+ {
+ CoverageMapResolver.PreferMode mode = parsePreferMode(preferMap);
+ CoverageMapResolver resolver = new CoverageMapResolver(projectDir, logger);
+ mapFile = resolver.resolve(mode);
+
+ if (mapFile == null)
+ {
+ System.err.println("No coverage map available. Use --map or run 'pull-map' first.");
+ return 1;
+ }
+ }
+ else if (!mapFile.exists())
{
System.err.println("Error: coverage map not found: " + mapFile);
return 1;
@@ -120,6 +151,20 @@ else if (queryMode.method != null)
}
}
+ private CoverageMapResolver.PreferMode parsePreferMode(String value)
+ {
+ switch (value.toLowerCase())
+ {
+ case "remote":
+ return CoverageMapResolver.PreferMode.REMOTE;
+ case "local":
+ return CoverageMapResolver.PreferMode.LOCAL;
+ case "nearest":
+ default:
+ return CoverageMapResolver.PreferMode.NEAREST;
+ }
+ }
+
private int showStats(CoverageMap map)
{
int totalTests = map.getTestMappings().size();