This is an automated email from the ASF dual-hosted git repository. amashenkov pushed a commit to branch ignite-25575 in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit 77ba180005f84e97a094d36ad971db306099f86f Author: amashenkov <amashen...@apache.org> AuthorDate: Thu Jun 26 17:34:52 2025 +0300 Naive tree validator --- .../compatibility/framework/ConfigNode.java | 26 ++++++++-- .../framework/ConfigurationTreeComparator.java | 58 ++++++++++++++++++++-- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigNode.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigNode.java index 862378e6622..2b715abc327 100644 --- a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigNode.java +++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigNode.java @@ -62,10 +62,14 @@ public class ConfigNode { Map<String, String> attrs = new LinkedHashMap<>(); attrs.put(Attributes.NAME, rootName); attrs.put(Attributes.CLASS, className.getCanonicalName()); - attrs.put("TYPE", type.toString()); - attrs.put("INTERNAL", String.valueOf(internal)); + attrs.put(Attributes.KIND, type.toString()); - return new ConfigNode(null, attrs, EnumSet.of(Flags.IS_ROOT)); + EnumSet<Flags> flags = EnumSet.of(Flags.IS_ROOT); + if (internal) { + flags.add(Flags.IS_INTERNAL); + } + + return new ConfigNode(null, attrs, flags); } /** @@ -82,6 +86,10 @@ public class ConfigNode { return attributes.get(Attributes.CLASS); } + public String kind() { + return attributes.get(Attributes.KIND); + } + /** * Returns the child nodes of this node. */ @@ -93,6 +101,13 @@ public class ConfigNode { this.parent = parent; } + /** + * Returns the parent node of this node. + */ + public ConfigNode getParent() { + return parent; + } + /** * Returns the child nodes of this node. */ @@ -177,7 +192,8 @@ public class ConfigNode { enum Flags { IS_ROOT(1), IS_VALUE(1 << 1), - IS_DEPRECATED(1 << 2); + IS_DEPRECATED(1 << 2), + IS_INTERNAL(1 << 3); private final int mask; @@ -213,8 +229,8 @@ public class ConfigNode { */ static class Attributes { static String NAME = "name"; + static String KIND = "kind"; static String CLASS = "class"; - static String FLAGS = "flags"; static String ANNOTATIONS = "annotations"; } } diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigurationTreeComparator.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigurationTreeComparator.java index f7acdc17337..6ccacea276c 100644 --- a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigurationTreeComparator.java +++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/compatibility/framework/ConfigurationTreeComparator.java @@ -19,7 +19,11 @@ package org.apache.ignite.internal.configuration.compatibility.framework; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; import java.util.List; +import java.util.Objects; /** * Compares two configuration trees (snapshot and current). @@ -29,11 +33,57 @@ public class ConfigurationTreeComparator { * Validates the current configuration tree is compatible with the snapshot. */ public static void ensureCompatible(List<ConfigNode> snapshot, List<ConfigNode> current) { - // TODO: https://issues.apache.org/jira/browse/IGNITE-25575 - String currentDump = dumpTree(current); - String snapshotDump = dumpTree(snapshot); + TreeValidatorShuttle shuttle = new TreeValidatorShuttle(current); - assertEquals(currentDump, snapshotDump, "Configuration metadata is incompatible"); + for (ConfigNode tree : snapshot) { + tree.accept(shuttle); + } + } + + /** + * Naive tree validator that checks the current configuration tree is compatible with the snapshot. Note: we rely here the tree is + * traversed in the depth-first order. + */ + private static class TreeValidatorShuttle implements ConfigShuttle { + private final List<ConfigNode> roots; + private final Deque<ConfigNode> actualStack = new ArrayDeque<>(); + private final Deque<ConfigNode> snapshotStack = new ArrayDeque<>(); + + TreeValidatorShuttle(List<ConfigNode> roots) { + this.roots = roots; + } + + @Override + public void visit(ConfigNode snapshotNode) { + // Get back to last common parent. + while (!snapshotStack.isEmpty() && snapshotStack.peek() != snapshotNode.getParent()) { + snapshotStack.pop(); + actualStack.pop(); + } + + // If stack is empty, we should search for the root node. + Collection<ConfigNode> candidates = actualStack.isEmpty() ? roots : actualStack.peek().childNodes(); + + ConfigNode node = find(candidates, snapshotNode); + snapshotStack.push(snapshotNode); + actualStack.push(node); + } + + private ConfigNode find(Collection<ConfigNode> candidates, ConfigNode node) { + for (ConfigNode cand : candidates) { + if (match(cand, node)) { + return cand; + } + } + + throw new IllegalStateException("No match found for node: " + node + " in candidates: \n" + candidates); + } + + private boolean match(ConfigNode node1, ConfigNode node2) { + return node1.isRoot() == node2.isRoot() + && Objects.equals(node1.kind(), node2.kind()) + && Objects.equals(node1.name(), node2.name()); + } } /**