From b6f64a5f14d581ce924f8b2eb54678f788f415ba Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 8 Dec 2023 20:22:56 +0100 Subject: [PATCH] Clean up AbstractRegistrationTree Inline RegistrationTree{Node,Snapshot} and perform general cleanups: - hide snapshot() method so it can only be invoked from subclasses - add @VisibleForTesting annotations - use Map.computeIfAbsent() - use local variable type inference - check result of Collection.remove() on cleanup JIRA: MDSAL-843 Change-Id: I4776be432da92bfe2f81d0551dd0d81184380987 Signed-off-by: Robert Varga --- .../dom/spi/AbstractRegistrationTree.java | 178 ++++++++++++++++-- .../mdsal/dom/spi/RegistrationTreeNode.java | 139 -------------- .../dom/spi/RegistrationTreeSnapshot.java | 39 ---- .../AbstractDOMStoreTreeChangePublisher.java | 11 +- .../dom/spi/AbstractRegistrationTreeTest.java | 6 +- .../dom/spi/RegistrationTreeNodeTest.java | 36 ++-- .../dom/spi/RegistrationTreeSnapshotTest.java | 39 ++-- 7 files changed, 210 insertions(+), 238 deletions(-) delete mode 100644 dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNode.java delete mode 100644 dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshot.java diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTree.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTree.java index be12ad3e8c..3b000ef4b7 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTree.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTree.java @@ -7,10 +7,31 @@ */ package org.opendaylight.mdsal.dom.spi; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.StampedLock; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.concepts.AbstractRegistration; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An abstract tree of registrations. Allows a read-only snapshot to be taken. @@ -18,12 +39,141 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum * @param Type of registered object */ public abstract class AbstractRegistrationTree { - private final RegistrationTreeNode rootNode = new RegistrationTreeNode<>(null, null); - private final Lock writeLock; - private final Lock readLock; + /** + * This is a single node within the registration tree. Note that the data returned from and instance of this class + * is guaranteed to have any relevance or consistency only as long as the {@link Snapshot} instance through which it + * is reached remains unclosed. + * + * @param registration type + */ + protected static final class Node implements Identifiable { + private static final Logger LOG = LoggerFactory.getLogger(Node.class); + + private final Map> children = new HashMap<>(); + private final List registrations = new ArrayList<>(2); + private final List publicRegistrations = Collections.unmodifiableList(registrations); + private final Reference> parent; + private final PathArgument identifier; + + Node(final Node parent, final PathArgument identifier) { + this.parent = new WeakReference<>(parent); + this.identifier = identifier; + } + + @Override + public PathArgument getIdentifier() { + return identifier; + } + + /** + * Return the child matching a {@link PathArgument} specification. + * + * @param arg Child identifier + * @return Child matching exactly, or {@code null}. + */ + public @Nullable Node getExactChild(final @NonNull PathArgument arg) { + return children.get(requireNonNull(arg)); + } + + /** + * Return a collection children which match a {@link PathArgument} specification inexactly. + * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}. + * + * @param arg Child identifier + * @return Collection of children, guaranteed to be non-null. + */ + public @NonNull Collection> getInexactChildren(final @NonNull PathArgument arg) { + requireNonNull(arg); + if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) { + /* + * TODO: This just all-or-nothing wildcards, which we have historically supported. Given + * that the argument is supposed to have all the elements filled out, we could support + * partial wildcards by iterating over the registrations and matching the maps for + * partial matches. + */ + final var child = children.get(new NodeIdentifier(arg.getNodeType())); + return child == null ? List.of() : Collections.singletonList(child); + } + + return List.of(); + } + + public Collection getRegistrations() { + return publicRegistrations; + } + + @VisibleForTesting + @NonNull Node ensureChild(final @NonNull PathArgument child) { + return children.computeIfAbsent(requireNonNull(child), key -> new Node<>(this, key)); + } + + @VisibleForTesting + void addRegistration(final @NonNull T registration) { + registrations.add(requireNonNull(registration)); + LOG.debug("Registration {} added", registration); + } + + @VisibleForTesting + void removeRegistration(final @NonNull T registration) { + if (registrations.remove(requireNonNull(registration))) { + LOG.debug("Registration {} removed", registration); + + // We have been called with the write-lock held, so we can perform some cleanup. + removeThisIfUnused(); + } + } + + private void removeThisIfUnused() { + final var p = parent.get(); + if (p != null && registrations.isEmpty() && children.isEmpty()) { + p.removeChild(identifier); + } + } + + private void removeChild(final PathArgument arg) { + children.remove(arg); + removeThisIfUnused(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("identifier", identifier) + .add("registrations", registrations.size()) + .add("children", children.size()) + .toString(); + } + } + + /** + * A stable read-only snapshot of a {@link AbstractRegistrationTree}. + */ + @NonNullByDefault + protected static final class Snapshot extends AbstractRegistration { + private final Node node; + private final Lock lock; + + Snapshot(final Lock lock, final Node node) { + this.lock = requireNonNull(lock); + this.node = requireNonNull(node); + } + + public Node getRootNode() { + return node; + } + + @Override + protected void removeRegistration() { + lock.unlock(); + } + } + + private final @NonNull Node rootNode = new Node<>(null, null); + private final @NonNull Lock writeLock; + private final @NonNull Lock readLock; protected AbstractRegistrationTree() { - final StampedLock lock = new StampedLock(); + final var lock = new StampedLock(); readLock = lock.asReadLock(); writeLock = lock.asWriteLock(); } @@ -50,12 +200,11 @@ public abstract class AbstractRegistrationTree { * @param path Path to find a node for * @return A registration node for the specified path */ - protected final @NonNull RegistrationTreeNode findNodeFor(final @NonNull Iterable path) { - RegistrationTreeNode walkNode = rootNode; - for (final PathArgument arg : path) { + protected final @NonNull Node findNodeFor(final @NonNull Iterable path) { + var walkNode = rootNode; + for (var arg : path) { walkNode = walkNode.ensureChild(arg); } - return walkNode; } @@ -66,7 +215,7 @@ public abstract class AbstractRegistrationTree { * @param node Tree node * @param registration Registration instance */ - protected final void addRegistration(final @NonNull RegistrationTreeNode node, final @NonNull T registration) { + protected final void addRegistration(final @NonNull Node node, final @NonNull T registration) { node.addRegistration(registration); } @@ -76,7 +225,7 @@ public abstract class AbstractRegistrationTree { * @param node Tree node * @param registration Registration instance */ - protected final void removeRegistration(final @NonNull RegistrationTreeNode node, + protected final void removeRegistration(final @NonNull Node node, final @NonNull T registration) { // Take the write lock writeLock.lock(); @@ -89,14 +238,13 @@ public abstract class AbstractRegistrationTree { } /** - * Obtain a tree snapshot. This snapshot ensures a consistent view of - * registrations. The snapshot should be closed as soon as it is not required, - * because each unclosed instance blocks modification of this tree. + * Obtain a tree snapshot. This snapshot ensures a consistent view of registrations. The snapshot should be closed + * as soon as it is not required, because each unclosed instance blocks modification of this tree. * * @return A snapshot instance. */ - public final @NonNull RegistrationTreeSnapshot takeSnapshot() { + protected final @NonNull Snapshot takeSnapshot() { readLock.lock(); - return new RegistrationTreeSnapshot<>(readLock, rootNode); + return new Snapshot<>(readLock, rootNode); } } diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNode.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNode.java deleted file mode 100644 index f89dce93d6..0000000000 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNode.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.dom.spi; - -import static java.util.Objects.requireNonNull; - -import com.google.common.base.MoreObjects; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.yangtools.concepts.Identifiable; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a single node within the registration tree. Note that the data returned from - * and instance of this class is guaranteed to have any relevance or consistency - * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached - * remains unclosed. - * - * @param registration type - * @author Robert Varga - */ -public final class RegistrationTreeNode implements Identifiable { - private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class); - - private final Map> children = new HashMap<>(); - private final Collection registrations = new ArrayList<>(2); - private final Collection publicRegistrations = Collections.unmodifiableCollection(registrations); - private final Reference> parent; - private final PathArgument identifier; - - RegistrationTreeNode(final RegistrationTreeNode parent, final PathArgument identifier) { - this.parent = new WeakReference<>(parent); - this.identifier = identifier; - } - - @Override - public PathArgument getIdentifier() { - return identifier; - } - - /** - * Return the child matching a {@link PathArgument} specification. - * - * @param arg Child identifier - * @return Child matching exactly, or null. - */ - public RegistrationTreeNode getExactChild(final @NonNull PathArgument arg) { - return children.get(requireNonNull(arg)); - } - - /** - * Return a collection children which match a {@link PathArgument} specification inexactly. - * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}. - * - * @param arg Child identifier - * @return Collection of children, guaranteed to be non-null. - */ - public @NonNull Collection> getInexactChildren(final @NonNull PathArgument arg) { - requireNonNull(arg); - if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) { - /* - * TODO: This just all-or-nothing wildcards, which we have historically supported. Given - * that the argument is supposed to have all the elements filled out, we could support - * partial wildcards by iterating over the registrations and matching the maps for - * partial matches. - */ - final RegistrationTreeNode child = children.get(new NodeIdentifier(arg.getNodeType())); - if (child == null) { - return Collections.emptyList(); - } - - return Collections.singletonList(child); - } - - return Collections.emptyList(); - } - - public Collection getRegistrations() { - return publicRegistrations; - } - - RegistrationTreeNode ensureChild(final @NonNull PathArgument child) { - RegistrationTreeNode potential = children.get(requireNonNull(child)); - if (potential == null) { - potential = new RegistrationTreeNode<>(this, child); - children.put(child, potential); - } - return potential; - } - - void addRegistration(final @NonNull T registration) { - registrations.add(requireNonNull(registration)); - LOG.debug("Registration {} added", registration); - } - - void removeRegistration(final @NonNull T registration) { - registrations.remove(requireNonNull(registration)); - LOG.debug("Registration {} removed", registration); - - // We have been called with the write-lock held, so we can perform some cleanup. - removeThisIfUnused(); - } - - private void removeThisIfUnused() { - final RegistrationTreeNode p = parent.get(); - if (p != null && registrations.isEmpty() && children.isEmpty()) { - p.removeChild(identifier); - } - } - - private void removeChild(final PathArgument arg) { - children.remove(arg); - removeThisIfUnused(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("identifier", identifier) - .add("registrations", registrations.size()) - .add("children", children.size()).toString(); - } -} diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshot.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshot.java deleted file mode 100644 index af33832bc0..0000000000 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshot.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.dom.spi; - -import static java.util.Objects.requireNonNull; - -import java.util.concurrent.locks.Lock; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.opendaylight.yangtools.concepts.AbstractRegistration; - -/** - * A stable read-only snapshot of a {@link AbstractRegistrationTree}. - * - * @author Robert Varga - */ -@NonNullByDefault -public final class RegistrationTreeSnapshot extends AbstractRegistration { - private final RegistrationTreeNode node; - private final Lock lock; - - RegistrationTreeSnapshot(final Lock lock, final RegistrationTreeNode node) { - this.lock = requireNonNull(lock); - this.node = requireNonNull(node); - } - - public RegistrationTreeNode getRootNode() { - return node; - } - - @Override - protected void removeRegistration() { - lock.unlock(); - } -} diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java index d7dbeed744..18c2c70229 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/AbstractDOMStoreTreeChangePublisher.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration; import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree; -import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate; @@ -93,7 +92,7 @@ public abstract class AbstractDOMStoreTreeChangePublisher // Take the write lock takeLock(); try { - final RegistrationTreeNode> node = + final Node> node = findNodeFor(treeId.getPathArguments()); final var reg = new AbstractDOMDataTreeChangeListenerRegistration<>(listener) { @Override @@ -112,19 +111,19 @@ public abstract class AbstractDOMStoreTreeChangePublisher } private void lookupAndNotify(final List args, - final int offset, final RegistrationTreeNode> node, + final int offset, final Node> node, final DataTreeCandidate candidate, final Multimap, DataTreeCandidate> listenerChanges) { if (args.size() != offset) { final PathArgument arg = args.get(offset); - final RegistrationTreeNode> exactChild + final Node> exactChild = node.getExactChild(arg); if (exactChild != null) { lookupAndNotify(args, offset + 1, exactChild, candidate, listenerChanges); } - for (RegistrationTreeNode> c : + for (Node> c : node.getInexactChildren(arg)) { lookupAndNotify(args, offset + 1, c, candidate, listenerChanges); } @@ -134,7 +133,7 @@ public abstract class AbstractDOMStoreTreeChangePublisher } private void notifyNode(final YangInstanceIdentifier path, - final RegistrationTreeNode> regNode, + final Node> regNode, final DataTreeCandidateNode candNode, final Multimap, DataTreeCandidate> listenerChanges) { if (candNode.modificationType() == ModificationType.UNMODIFIED) { diff --git a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTreeTest.java b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTreeTest.java index 9656d7a720..c97f7b52fe 100644 --- a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTreeTest.java +++ b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractRegistrationTreeTest.java @@ -21,9 +21,9 @@ public class AbstractRegistrationTreeTest extends AbstractRegistrationTree registrationTreeNodeParent = new RegistrationTreeNode<>(null, pathArgument); - final RegistrationTreeNode registrationTreeNode = - new RegistrationTreeNode<>(registrationTreeNodeParent, pathArgument); + final Node registrationTreeNodeParent = new Node<>(null, pathArgument); + final Node registrationTreeNode = + new Node<>(registrationTreeNodeParent, pathArgument); final Object registration = new Object(); takeLock(); diff --git a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNodeTest.java b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNodeTest.java index 6e0d10c858..9573743c9d 100644 --- a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNodeTest.java +++ b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeNodeTest.java @@ -7,43 +7,45 @@ */ package org.opendaylight.mdsal.dom.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree.Node; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; -public class RegistrationTreeNodeTest { +class RegistrationTreeNodeTest { @Test - public void basicTest() throws Exception { - final NodeIdentifier pathArgument = new NodeIdentifier(QName.create("", "pathArgument")); - final RegistrationTreeNode registrationTreeNodeParent = new RegistrationTreeNode<>(null, pathArgument); - final RegistrationTreeNode registrationTreeNode = - new RegistrationTreeNode<>(registrationTreeNodeParent, pathArgument); + void basicTest() { + final var pathArgument = new NodeIdentifier(QName.create("", "pathArgument")); + final var registrationTreeNodeParent = new Node<>(null, pathArgument); + final var registrationTreeNode = new Node<>(registrationTreeNodeParent, pathArgument); assertEquals(pathArgument, registrationTreeNode.getIdentifier()); - final Object registration = new Object(); - assertFalse(registrationTreeNode.getRegistrations().contains(registration)); + final var registration = new Object(); + final var registrations = registrationTreeNode.getRegistrations(); + assertEquals(List.of(), registrations); registrationTreeNode.addRegistration(registration); - assertTrue(registrationTreeNode.getRegistrations().contains(registration)); + assertEquals(List.of(registration), registrations); registrationTreeNode.removeRegistration(registration); - assertFalse(registrationTreeNode.getRegistrations().contains(registration)); + assertEquals(List.of(), registrations); registrationTreeNode.removeRegistration(registration); + assertEquals(List.of(), registrations); assertNotNull(registrationTreeNode.ensureChild(pathArgument)); assertNotNull(registrationTreeNode.getExactChild(pathArgument)); - final NodeWithValue nodeWithValue = new NodeWithValue<>(QName.create("", "testNode"), new Object()); + final var nodeWithValue = new NodeWithValue<>(QName.create("", "testNode"), new Object()); assertEquals(List.of(), registrationTreeNode.getInexactChildren(nodeWithValue)); assertEquals(List.of(), registrationTreeNode.getInexactChildren(pathArgument)); - final NodeIdentifier nodeWithoutValue = new NodeIdentifier(QName.create("", "testNode")); + final var nodeWithoutValue = new NodeIdentifier(QName.create("", "testNode")); assertNotNull(registrationTreeNode.ensureChild(nodeWithoutValue)); assertFalse(registrationTreeNode.getInexactChildren(nodeWithValue).isEmpty()); diff --git a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshotTest.java b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshotTest.java index 1903bbf8db..7769779090 100644 --- a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshotTest.java +++ b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/RegistrationTreeSnapshotTest.java @@ -7,31 +7,32 @@ */ package org.opendaylight.mdsal.dom.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import java.util.concurrent.locks.Lock; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree.Node; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -public class RegistrationTreeSnapshotTest { - @Test - public void basicTest() throws Exception { - final Lock lock = mock(Lock.class); - final NodeIdentifier pathArgument = new NodeIdentifier(QName.create("", "pathArgument")); - final RegistrationTreeNode registrationTreeNode = new RegistrationTreeNode<>(null, pathArgument); - final RegistrationTreeSnapshot registrationTreeSnapshot = - new RegistrationTreeSnapshot<>(lock, registrationTreeNode); - - assertNotNull(registrationTreeSnapshot.getRootNode()); - assertEquals(registrationTreeNode, registrationTreeSnapshot.getRootNode()); +@ExtendWith(MockitoExtension.class) +class RegistrationTreeSnapshotTest { + @Mock + private Lock lock; - doNothing().when(lock).unlock(); - registrationTreeSnapshot.close(); - verify(lock).unlock(); + @Test + void basicTest() throws Exception { + final var pathArgument = new NodeIdentifier(QName.create("", "pathArgument")); + final var registrationTreeNode = new Node<>(null, pathArgument); + try (var registrationTreeSnapshot = new AbstractRegistrationTree.Snapshot<>(lock, registrationTreeNode)) { + assertNotNull(registrationTreeSnapshot.getRootNode()); + assertEquals(registrationTreeNode, registrationTreeSnapshot.getRootNode()); + doNothing().when(lock).unlock(); + } } } \ No newline at end of file -- 2.36.6