From 12503a5b4c99373c2ddc9d42bdc2bdc0cdbb7c03 Mon Sep 17 00:00:00 2001 From: skedwards88 Date: Fri, 20 Feb 2026 14:46:51 -0700 Subject: [PATCH 1/3] ignore cs builds --- .gitignore | 1 + .../docs/core/planner/TestPlanBuilder.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.gitignore b/.gitignore index fa212aa..54d2984 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +astra-client-docs-tests.sln ### IntelliJ IDEA ### .idea/modules.xml diff --git a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java index 8f5aac8..7d25a3b 100644 --- a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java +++ b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java @@ -189,6 +189,11 @@ private static TreeMap> findFilesToTestInRoot(Path roo return; } + // Skip files in build output directories (obj/, bin/, target/, etc.) + if (isInBuildOutputDirectory(child)) { + return; + } + tryAppendChild(ctx, skipConfig, child, ret); }); } catch (IOException e) { @@ -198,6 +203,30 @@ private static TreeMap> findFilesToTestInRoot(Path roo return ret; } + /// Checks if a file path is within a build output directory. + /// + /// Build output directories include: + /// - `obj/` and `bin/` (C#/.NET) + /// - `target/` (Java/Maven) + /// - `build/` (Gradle, general) + /// - `dist/` (JavaScript/TypeScript) + /// - `node_modules/` (JavaScript/TypeScript) + /// - `__pycache__/` and `.pytest_cache/` (Python) + /// + /// @param path the file path to check + /// @return true if the path contains any build output directory + private static boolean isInBuildOutputDirectory(Path path) { + val pathStr = path.toString(); + return pathStr.contains("/obj/") || pathStr.contains("\\obj\\") + || pathStr.contains("/bin/") || pathStr.contains("\\bin\\") + || pathStr.contains("/target/") || pathStr.contains("\\target\\") + || pathStr.contains("/build/") || pathStr.contains("\\build\\") + || pathStr.contains("/dist/") || pathStr.contains("\\dist\\") + || pathStr.contains("/node_modules/") || pathStr.contains("\\node_modules\\") + || pathStr.contains("/__pycache__/") || pathStr.contains("\\__pycache__\\") + || pathStr.contains("/.pytest_cache/") || pathStr.contains("\\.pytest_cache\\"); + } + private static void tryAppendChild(TestCtx ctx, SkipConfig skipConfig, Path child, TreeMap> ret) { val fileName = child.getFileName().toString(); From 90e4fdb9fa3a7077bf51bf4709bc5eba547ec5dc Mon Sep 17 00:00:00 2001 From: skedwards88 Date: Fri, 20 Feb 2026 15:03:18 -0700 Subject: [PATCH 2/3] bob edits --- .../docs/core/planner/TestPlanBuilder.java | 118 +++++++++++++----- 1 file changed, 88 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java index 7d25a3b..0473cb2 100644 --- a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java +++ b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java @@ -19,6 +19,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static com.dtsx.docs.lib.Constants.META_FILE; @@ -63,10 +66,13 @@ public static TestPlan buildPlan(TestCtx ctx) { CliLogger.println(true, "@|bold Building test plan...|@"); CliLogger.println(true, "@!->!@ Found " + testRoots.size() + " test roots"); + // Build gitignore predicate once at the start + val gitignorePredicate = buildGitignorePredicate(ctx.examplesFolder()); + val builder = new Builder(); for (val rootPath : testRoots) { - mkTestRoot(ctx, rootPath).ifPresent(builder::addRoot); + mkTestRoot(ctx, rootPath, gitignorePredicate).ifPresent(builder::addRoot); } val plan = builder.build(ctx.maxFixtureInstances()); @@ -123,6 +129,79 @@ private static List findTestRoots(Path examplesFolder) { } } + /// Builds a predicate from the .gitignore file in the examples folder. + /// + /// Reads the .gitignore file (if it exists) and converts each pattern into a predicate + /// that tests whether a path should be ignored. Supports glob patterns like: + /// - `**/bin/*` - matches bin directory at any depth + /// - `**/obj/*` - matches obj directory at any depth + /// - `target/*` - matches target directory at root level + /// + /// @param examplesFolder the examples folder to look for .gitignore + /// @return a predicate that returns true if a path should be ignored + private static Predicate buildGitignorePredicate(Path examplesFolder) { + val gitignorePath = examplesFolder.resolve(".gitignore"); + + if (!Files.exists(gitignorePath)) { + // No .gitignore file, don't ignore anything + return path -> false; + } + + try { + val patterns = Files.readAllLines(gitignorePath).stream() + .map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#")) + .map(TestPlanBuilder::gitignorePatternToRegex) + .map(Pattern::compile) + .collect(Collectors.toList()); + + return path -> { + val pathStr = path.toString(); + return patterns.stream().anyMatch(pattern -> pattern.matcher(pathStr).find()); + }; + } catch (IOException e) { + CliLogger.println(false, "@|yellow Warning: Failed to read .gitignore file, ignoring it|@"); + return path -> false; + } + } + + /// Converts a gitignore pattern to a regex pattern. + /// + /// Handles common gitignore patterns: + /// - `**` matches any number of directories + /// - `*` matches any characters except directory separator + /// - Literal directory separators + /// + /// @param gitignorePattern the gitignore pattern (e.g., "**/bin/*") + /// @return a regex pattern string + private static String gitignorePatternToRegex(String gitignorePattern) { + // Escape special regex characters except * and / + String regex = gitignorePattern + .replace("\\", "\\\\") + .replace(".", "\\.") + .replace("+", "\\+") + .replace("?", "\\?") + .replace("|", "\\|") + .replace("(", "\\(") + .replace(")", "\\)") + .replace("[", "\\[") + .replace("]", "\\]") + .replace("{", "\\{") + .replace("}", "\\}") + .replace("^", "\\^") + .replace("$", "\\$"); + + // Convert gitignore wildcards to regex + regex = regex.replace("**/", "(.*/)?"); // ** matches any number of directories + regex = regex.replace("**", ".*"); // ** at end matches anything + regex = regex.replace("*", "[^/\\\\]*"); // * matches anything except path separator + + // Handle both forward and backward slashes + regex = regex.replace("/", "[/\\\\]"); + + return regex; + } + /// Creates a test root from a directory containing a `meta.yml` file. /// /// Steps: @@ -135,11 +214,12 @@ private static List findTestRoots(Path examplesFolder) { /// /// @param rootPath path to the test root directory /// @param ctx the verifier context + /// @param gitignorePredicate predicate to test if paths should be ignored /// @return a pair of (base fixture, test root), or empty if skipped or no example files found - private static Optional> mkTestRoot(TestCtx ctx, Path rootPath) { + private static Optional> mkTestRoot(TestCtx ctx, Path rootPath, Predicate gitignorePredicate) { val meta = MetaYmlParser.parseMetaYml(ctx, rootPath.resolve(META_FILE)); - val filesToTest = findFilesToTestInRoot(rootPath, ctx, meta.skipConfig()); + val filesToTest = findFilesToTestInRoot(rootPath, ctx, meta.skipConfig(), gitignorePredicate); if (filesToTest.isEmpty()) { return Optional.empty(); @@ -164,6 +244,7 @@ private static Optional> mkTestRoot(TestCtx ctx, Path /// Finds all example files in a test root for the configured client languages. /// /// Searches for files named `example.` where `` matches a configured language extension. + /// Files matching patterns in .gitignore are excluded. /// /// Example: /// ``` @@ -178,9 +259,10 @@ private static Optional> mkTestRoot(TestCtx ctx, Path /// @param root the test root directory to search /// @param ctx the verifier context containing language configuration /// @param skipConfig the skip configuration to check if any languages should be skipped + /// @param gitignorePredicate predicate to test if paths should be ignored based on .gitignore /// @return map of client languages to their example file paths /// @throws PlanException if traversal fails - private static TreeMap> findFilesToTestInRoot(Path root, TestCtx ctx, SkipConfig skipConfig) { + private static TreeMap> findFilesToTestInRoot(Path root, TestCtx ctx, SkipConfig skipConfig, Predicate gitignorePredicate) { val ret = new TreeMap>(); try (val children = Files.walk(root).skip(1)) { @@ -189,8 +271,8 @@ private static TreeMap> findFilesToTestInRoot(Path roo return; } - // Skip files in build output directories (obj/, bin/, target/, etc.) - if (isInBuildOutputDirectory(child)) { + // Skip files matching .gitignore patterns + if (gitignorePredicate.test(child)) { return; } @@ -203,30 +285,6 @@ private static TreeMap> findFilesToTestInRoot(Path roo return ret; } - /// Checks if a file path is within a build output directory. - /// - /// Build output directories include: - /// - `obj/` and `bin/` (C#/.NET) - /// - `target/` (Java/Maven) - /// - `build/` (Gradle, general) - /// - `dist/` (JavaScript/TypeScript) - /// - `node_modules/` (JavaScript/TypeScript) - /// - `__pycache__/` and `.pytest_cache/` (Python) - /// - /// @param path the file path to check - /// @return true if the path contains any build output directory - private static boolean isInBuildOutputDirectory(Path path) { - val pathStr = path.toString(); - return pathStr.contains("/obj/") || pathStr.contains("\\obj\\") - || pathStr.contains("/bin/") || pathStr.contains("\\bin\\") - || pathStr.contains("/target/") || pathStr.contains("\\target\\") - || pathStr.contains("/build/") || pathStr.contains("\\build\\") - || pathStr.contains("/dist/") || pathStr.contains("\\dist\\") - || pathStr.contains("/node_modules/") || pathStr.contains("\\node_modules\\") - || pathStr.contains("/__pycache__/") || pathStr.contains("\\__pycache__\\") - || pathStr.contains("/.pytest_cache/") || pathStr.contains("\\.pytest_cache\\"); - } - private static void tryAppendChild(TestCtx ctx, SkipConfig skipConfig, Path child, TreeMap> ret) { val fileName = child.getFileName().toString(); From 140d7144a7ba15d83f38967552a31b6665f01f02 Mon Sep 17 00:00:00 2001 From: toptobes Date: Fri, 20 Feb 2026 15:00:05 -0800 Subject: [PATCH 3/3] potentially fix bobs code --- .../docs/core/planner/TestPlanBuilder.java | 120 +++++++++--------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java index 0473cb2..5b991f1 100644 --- a/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java +++ b/src/main/java/com/dtsx/docs/core/planner/TestPlanBuilder.java @@ -12,12 +12,15 @@ import com.dtsx.docs.core.runner.tests.strategies.test.CompilesTestStrategy; import com.dtsx.docs.core.runner.tests.strategies.test.SnapshotTestStrategy; import com.dtsx.docs.lib.CliLogger; +import lombok.SneakyThrows; import lombok.val; import org.apache.commons.lang3.tuple.Pair; import java.io.IOException; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.*; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -66,10 +69,8 @@ public static TestPlan buildPlan(TestCtx ctx) { CliLogger.println(true, "@|bold Building test plan...|@"); CliLogger.println(true, "@!->!@ Found " + testRoots.size() + " test roots"); - // Build gitignore predicate once at the start - val gitignorePredicate = buildGitignorePredicate(ctx.examplesFolder()); - val builder = new Builder(); + val gitignorePredicate = buildGitignorePredicate(ctx.examplesFolder()); for (val rootPath : testRoots) { mkTestRoot(ctx, rootPath, gitignorePredicate).ifPresent(builder::addRoot); @@ -137,72 +138,70 @@ private static List findTestRoots(Path examplesFolder) { /// - `**/obj/*` - matches obj directory at any depth /// - `target/*` - matches target directory at root level /// - /// @param examplesFolder the examples folder to look for .gitignore /// @return a predicate that returns true if a path should be ignored - private static Predicate buildGitignorePredicate(Path examplesFolder) { - val gitignorePath = examplesFolder.resolve(".gitignore"); + @SneakyThrows + private static Predicate buildGitignorePredicate(Path rootFolder) { + val gitignorePath = rootFolder.resolve(".gitignore"); if (!Files.exists(gitignorePath)) { - // No .gitignore file, don't ignore anything - return path -> false; + return _ -> false; } - try { - val patterns = Files.readAllLines(gitignorePath).stream() - .map(String::trim) - .filter(line -> !line.isEmpty() && !line.startsWith("#")) - .map(TestPlanBuilder::gitignorePatternToRegex) - .map(Pattern::compile) - .collect(Collectors.toList()); - - return path -> { - val pathStr = path.toString(); - return patterns.stream().anyMatch(pattern -> pattern.matcher(pathStr).find()); - }; - } catch (IOException e) { - CliLogger.println(false, "@|yellow Warning: Failed to read .gitignore file, ignoring it|@"); - return path -> false; + val lines = Files.readAllLines(gitignorePath); + + val fs = FileSystems.getDefault(); + + val excludeMatchers = new ArrayList(); + val includeMatchers = new ArrayList(); + + for (var line : lines) { + line = line.trim(); + + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + val negated = line.startsWith("!"); + + if (negated) { + line = line.substring(1); + } + + if (line.endsWith("/")) { + line = line + "**"; + } + + if (!line.startsWith("/")) { + line = "**/" + line; + } else { + line = line.substring(1); + } + + val matcher = fs.getPathMatcher("glob:" + line); + + if (negated) { + includeMatchers.add(matcher); + } else { + excludeMatchers.add(matcher); + } } - } - /// Converts a gitignore pattern to a regex pattern. - /// - /// Handles common gitignore patterns: - /// - `**` matches any number of directories - /// - `*` matches any characters except directory separator - /// - Literal directory separators - /// - /// @param gitignorePattern the gitignore pattern (e.g., "**/bin/*") - /// @return a regex pattern string - private static String gitignorePatternToRegex(String gitignorePattern) { - // Escape special regex characters except * and / - String regex = gitignorePattern - .replace("\\", "\\\\") - .replace(".", "\\.") - .replace("+", "\\+") - .replace("?", "\\?") - .replace("|", "\\|") - .replace("(", "\\(") - .replace(")", "\\)") - .replace("[", "\\[") - .replace("]", "\\]") - .replace("{", "\\{") - .replace("}", "\\}") - .replace("^", "\\^") - .replace("$", "\\$"); - - // Convert gitignore wildcards to regex - regex = regex.replace("**/", "(.*/)?"); // ** matches any number of directories - regex = regex.replace("**", ".*"); // ** at end matches anything - regex = regex.replace("*", "[^/\\\\]*"); // * matches anything except path separator - - // Handle both forward and backward slashes - regex = regex.replace("/", "[/\\\\]"); - - return regex; + return (path) -> { + val relative = rootFolder.relativize(path); + var ignored = false; + + for (val matcher : excludeMatchers) { + ignored = ignored || matcher.matches(relative); + } + + for (val matcher : includeMatchers) { + ignored = ignored && !matcher.matches(relative); + } + + return ignored; + }; } - /// Creates a test root from a directory containing a `meta.yml` file. /// /// Steps: /// 1. Parse `meta.yml` configuration @@ -271,7 +270,6 @@ private static TreeMap> findFilesToTestInRoot(Path roo return; } - // Skip files matching .gitignore patterns if (gitignorePredicate.test(child)) { return; }