From cb9ede64de8b5633d1896f0973f2da9835e0f876 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Mon, 28 Aug 2017 18:38:50 +0200 Subject: [PATCH] Bug 9060: TracingBroker printOpenTransactions This method is intended to be used from a Karaf CLI command in the next change (and maybe JMX or something else like that later), which can be invoked during future automated testing to detect Tx leaks during CSIT. This is one of a serious of commits which is part of a solution I'm proposing in order to be able to detect OOM issues such as Bug 9034, based on using the mdsal-trace DataBroker. Change-Id: I682700bef9644834e8b4ca36b21729f021a76bf0 Signed-off-by: Michael Vorburger --- .../sal/trace/api/TracingDOMDataBroker.java | 8 +++ .../impl/AbstractCloseTracked.java | 7 ++- .../trace/closetracker/impl/CloseTracked.java | 5 +- .../impl/CloseTrackedRegistry.java | 43 ++++++++++---- .../impl/CloseTrackedRegistryReportEntry.java | 52 +++++++++++++++++ .../closetracker/impl/CloseTrackedTrait.java | 9 ++- .../md/sal/trace/dom/impl/TracingBroker.java | 45 +++++++++++++- .../dom/impl/TracingReadWriteTransaction.java | 7 ++- .../dom/impl/TracingTransactionChain.java | 20 +++++-- .../dom/impl/TracingWriteTransaction.java | 7 ++- .../impl/tests/CloseTrackedRegistryTest.java | 34 ++++++----- .../impl/tests/TracingBrokerTest.java | 58 +++++++++++++++++++ 12 files changed, 261 insertions(+), 34 deletions(-) create mode 100644 opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistryReportEntry.java create mode 100644 opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java diff --git a/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java b/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java index 14d30b123e..34d5eb4a2a 100644 --- a/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java +++ b/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.md.sal.trace.api; +import java.io.PrintStream; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; /** @@ -14,4 +15,11 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; */ public interface TracingDOMDataBroker extends DOMDataBroker { + /** + * Prints a human-readable "report" of all opened but not closed transactions, + * including transactions chains and transactions opened by them, onto the printStream. + * @return true if there were any open transactions, false if none + */ + boolean printOpenTransactions(PrintStream printStream); + } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java index aaafeb4bec..785e2ef6ae 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java @@ -19,7 +19,7 @@ public abstract class AbstractCloseTracked> im private final CloseTrackedTrait closeTracker; protected AbstractCloseTracked(CloseTrackedRegistry transactionChainRegistry) { - this.closeTracker = new CloseTrackedTrait<>(transactionChainRegistry); + this.closeTracker = new CloseTrackedTrait<>(transactionChainRegistry, this); } protected void removeFromTrackedRegistry() { @@ -30,4 +30,9 @@ public abstract class AbstractCloseTracked> im public @Nullable StackTraceElement[] getAllocationContextStackTrace() { return closeTracker.getAllocationContextStackTrace(); } + + @Override + public final CloseTracked getRealCloseTracked() { + return this; + } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java index f5fd341b55..926c8b3b8e 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java @@ -10,9 +10,9 @@ package org.opendaylight.controller.md.sal.trace.closetracker.impl; import javax.annotation.Nullable; /** - * Object which can track where it has been created, and if it has been correctly "closed". + * Object which can track where something has been created, and if it has been correctly "closed". * - *

Includes preserving the context of the call stack which created this object, and the instant it was created. + *

Includes preserving the context of the call stack which created an object, and the instant it was created. * * @author Michael Vorburger.ch */ @@ -20,4 +20,5 @@ public interface CloseTracked> { @Nullable StackTraceElement[] getAllocationContextStackTrace(); + CloseTracked getRealCloseTracked(); } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java index 7689831644..4e60839630 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java @@ -7,6 +7,9 @@ */ package org.opendaylight.controller.md.sal.trace.closetracker.impl; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; + import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -25,9 +28,8 @@ import javax.annotation.concurrent.ThreadSafe; @ThreadSafe public class CloseTrackedRegistry> { - // unused OK for now, at least we'll be able to see this in HPROF heap dumps and know what is which - private final @SuppressWarnings("unused") Object anchor; - private final @SuppressWarnings("unused") String createDescription; + private final Object anchor; + private final String createDescription; private final Set> tracked = new ConcurrentSkipListSet<>( (o1, o2) -> Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2))); @@ -58,6 +60,14 @@ public class CloseTrackedRegistry> { return isDebugContextEnabled; } + public Object getAnchor() { + return anchor; + } + + public String getCreateDescription() { + return createDescription; + } + // package protected, not public; only CloseTrackedTrait invokes this void add(CloseTracked closeTracked) { tracked.add(closeTracked); @@ -72,20 +82,33 @@ public class CloseTrackedRegistry> { * Creates and returns a "report" of (currently) tracked but not (yet) closed * instances. * - * @return Map where key is the StackTraceElement[] identifying a unique - * allocation contexts (or an empty List if debugContextEnabled is false), - * and value is the number of open instances created at that point. + * @return Set of CloseTrackedRegistryReportEntry, of which each the stack trace + * element identifies a unique allocation context (or an empty List if + * debugContextEnabled is false), and value is the number of open + * instances created at that place in the code. */ - public Map, Long> getAllUnique() { - Map,Long> mapToReturn = new HashMap<>(); + public Set> getAllUnique() { + Map, Long> map = new HashMap<>(); Set> copyOfTracked = new HashSet<>(tracked); for (CloseTracked closeTracked : copyOfTracked) { final StackTraceElement[] stackTraceArray = closeTracked.getAllocationContextStackTrace(); List stackTraceElements = stackTraceArray != null ? Arrays.asList(stackTraceArray) : Collections.emptyList(); - mapToReturn.merge(stackTraceElements, 1L, (oldValue, value) -> oldValue + 1); + map.merge(stackTraceElements, 1L, (oldValue, value) -> oldValue + 1); } - return mapToReturn; + + Set> report = new HashSet<>(); + map.forEach((stackTraceElements, number) -> { + copyOfTracked.stream().filter(closeTracked -> { + StackTraceElement[] closeTrackedStackTraceArray = closeTracked.getAllocationContextStackTrace(); + List closeTrackedStackTraceElements = + closeTrackedStackTraceArray != null ? asList(closeTrackedStackTraceArray) : emptyList(); + return closeTrackedStackTraceElements.equals(stackTraceElements); + }).findAny().ifPresent(exampleCloseTracked -> { + report.add(new CloseTrackedRegistryReportEntry<>(exampleCloseTracked, number, stackTraceElements)); + }); + }); + return report; } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistryReportEntry.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistryReportEntry.java new file mode 100644 index 0000000000..1681eb8d1a --- /dev/null +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistryReportEntry.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Red Hat, 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.controller.md.sal.trace.closetracker.impl; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +/** + * Element of a "report" created by a {@link CloseTrackedRegistry}. + * + * @author Michael Vorburger.ch + */ +public class CloseTrackedRegistryReportEntry> { + + private final CloseTracked exampleCloseTracked; + private final long numberAddedNotRemoved; + private final List stackTraceElements; + + public CloseTrackedRegistryReportEntry(CloseTracked exampleCloseTracked, long numberAddedNotRemoved, + List stackTraceElements) { + super(); + this.exampleCloseTracked = requireNonNull(exampleCloseTracked, "closeTracked"); + this.numberAddedNotRemoved = requireNonNull(numberAddedNotRemoved, "numberAddedNotRemoved"); + this.stackTraceElements = requireNonNull(stackTraceElements, "stackTraceElements"); + } + + public long getNumberAddedNotRemoved() { + return numberAddedNotRemoved; + } + + public CloseTracked getExampleCloseTracked() { + return exampleCloseTracked; + } + + public List getStackTraceElements() { + return stackTraceElements; + } + + @Override + public String toString() { + return "CloseTrackedRegistryReportEntry [numberAddedNotRemoved=" + numberAddedNotRemoved + ", closeTracked=" + + exampleCloseTracked + ", stackTraceElements.size=" + stackTraceElements.size() + "]"; + } + + +} diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java index b846c042d1..b1b41319e5 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java @@ -24,13 +24,15 @@ public class CloseTrackedTrait> implements CloseTracke private final @Nullable Throwable allocationContext; private final CloseTrackedRegistry closeTrackedRegistry; + private final CloseTracked realCloseTracked; - public CloseTrackedTrait(CloseTrackedRegistry transactionChainRegistry) { + public CloseTrackedTrait(CloseTrackedRegistry transactionChainRegistry, CloseTracked realCloseTracked) { if (transactionChainRegistry.isDebugContextEnabled()) { this.allocationContext = new Throwable("allocated at"); } else { this.allocationContext = null; } + this.realCloseTracked = Objects.requireNonNull(realCloseTracked, "realCloseTracked"); this.closeTrackedRegistry = Objects.requireNonNull(transactionChainRegistry, "transactionChainRegistry"); this.closeTrackedRegistry.add(this); } @@ -44,4 +46,9 @@ public class CloseTrackedTrait> implements CloseTracke closeTrackedRegistry.remove(this); } + @Override + public CloseTracked getRealCloseTracked() { + return realCloseTracked; + } + } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java index c01c1d9cd3..b08e19ace7 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java @@ -7,13 +7,13 @@ */ package org.opendaylight.controller.md.sal.trace.dom.impl; +import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; - import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; @@ -356,4 +356,47 @@ public class TracingBroker implements TracingDOMDataBroker { return res; } + + public boolean printOpenTransactions(PrintStream ps) { + if (transactionChainsRegistry.getAllUnique().isEmpty() + && readOnlyTransactionsRegistry.getAllUnique().isEmpty() + && writeTransactionsRegistry.getAllUnique().isEmpty() + && readWriteTransactionsRegistry.getAllUnique().isEmpty()) { + + return false; + } + + ps.println(getClass().getSimpleName() + " found not yet (or never..) closed transaction[chain]s"); + ps.println(); + printRegistryOpenTransactions(readOnlyTransactionsRegistry, ps, ""); + printRegistryOpenTransactions(writeTransactionsRegistry, ps, ""); + printRegistryOpenTransactions(readWriteTransactionsRegistry, ps, ""); + + // Now print details for each non-closed TransactionChain + // incl. in turn each ones own read/Write[Only]TransactionsRegistry + ps.println(transactionChainsRegistry.getCreateDescription()); + transactionChainsRegistry.getAllUnique().forEach(entry -> { + ps.println(" " + entry.getNumberAddedNotRemoved() + "x TransactionChains opened, which are not closed:"); + entry.getStackTraceElements().forEach(line -> ps.println(" " + line)); + @SuppressWarnings("resource") + TracingTransactionChain txChain = (TracingTransactionChain) entry + .getExampleCloseTracked().getRealCloseTracked(); + printRegistryOpenTransactions(txChain.getReadOnlyTransactionsRegistry(), ps, " "); + printRegistryOpenTransactions(txChain.getWriteTransactionsRegistry(), ps, " "); + printRegistryOpenTransactions(txChain.getReadWriteTransactionsRegistry(), ps, " "); + }); + ps.println(); + + return true; + } + + private void printRegistryOpenTransactions(CloseTrackedRegistry registry, PrintStream ps, String indent) { + ps.println(indent + registry.getCreateDescription()); + registry.getAllUnique().forEach(entry -> { + ps.println(indent + " " + entry.getNumberAddedNotRemoved() + + "x transactions opened here, which are not closed:"); + entry.getStackTraceElements().forEach(line -> ps.print(" " + line)); + }); + ps.println(); + } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java index fe57c8108d..091e2a45f7 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java @@ -33,7 +33,7 @@ class TracingReadWriteTransaction TracingReadWriteTransaction(DOMDataReadWriteTransaction delegate, TracingBroker tracingBroker, CloseTrackedRegistry readWriteTransactionsRegistry) { super(delegate, tracingBroker); - this.closeTracker = new CloseTrackedTrait<>(readWriteTransactionsRegistry); + this.closeTracker = new CloseTrackedTrait<>(readWriteTransactionsRegistry, this); this.delegate = Objects.requireNonNull(delegate); } @@ -70,4 +70,9 @@ class TracingReadWriteTransaction public StackTraceElement[] getAllocationContextStackTrace() { return closeTracker.getAllocationContextStackTrace(); } + + @Override + public CloseTracked getRealCloseTracked() { + return this; + } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java index c8f4539cc7..ac6aaa8bdb 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java @@ -29,10 +29,11 @@ class TracingTransactionChain extends AbstractCloseTracked(this, "newReadOnlyTransaction", isDebugging); - this.writeTransactionsRegistry = new CloseTrackedRegistry<>(this, "newWriteOnlyTransaction", isDebugging); - this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, "newReadWriteTransaction", isDebugging); + final boolean isDebug = transactionChainsRegistry.isDebugContextEnabled(); + String pf = "TransactionChain_" + toString(); + this.readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(this, pf + "newReadOnlyTransaction", isDebug); + this.writeTransactionsRegistry = new CloseTrackedRegistry<>(this, pf + "newWriteOnlyTransaction", isDebug); + this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, pf + "newReadWriteTransaction", isDebug); } @Override @@ -60,4 +61,15 @@ class TracingTransactionChain extends AbstractCloseTracked getReadOnlyTransactionsRegistry() { + return readOnlyTransactionsRegistry; + } + + public CloseTrackedRegistry getReadWriteTransactionsRegistry() { + return readWriteTransactionsRegistry; + } + + public CloseTrackedRegistry getWriteTransactionsRegistry() { + return writeTransactionsRegistry; + } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java index 7e98e40e2a..5cd0d631d3 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java @@ -25,7 +25,7 @@ class TracingWriteTransaction extends AbstractTracingWriteTransaction TracingWriteTransaction(DOMDataWriteTransaction delegate, TracingBroker tracingBroker, CloseTrackedRegistry writeTransactionsRegistry) { super(delegate, tracingBroker); - this.closeTracker = new CloseTrackedTrait<>(writeTransactionsRegistry); + this.closeTracker = new CloseTrackedTrait<>(writeTransactionsRegistry, this); } @Override @@ -51,4 +51,9 @@ class TracingWriteTransaction extends AbstractTracingWriteTransaction return closeTracker.getAllocationContextStackTrace(); } + @Override + public CloseTracked getRealCloseTracked() { + return this; + } + } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/CloseTrackedRegistryTest.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/CloseTrackedRegistryTest.java index ba34cbf28c..a3a8b495bc 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/CloseTrackedRegistryTest.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/CloseTrackedRegistryTest.java @@ -10,12 +10,12 @@ package org.opendaylight.controller.md.sal.trace.closetracker.impl.tests; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import java.util.List; -import java.util.Map; +import java.util.Set; import java.util.function.Predicate; import org.junit.Test; import org.opendaylight.controller.md.sal.trace.closetracker.impl.AbstractCloseTracked; import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry; +import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistryReportEntry; public class CloseTrackedRegistryTest { @@ -43,18 +43,21 @@ public class CloseTrackedRegistryTest { @SuppressWarnings({ "resource", "unused" }) SomethingClosable forgotToCloseOnce = new SomethingClosable(registry); - Map, Long> uniqueNonClosed = registry.getAllUnique(); - assertThat(uniqueNonClosed.values()).containsExactly(100L, 1L); - uniqueNonClosed.forEach((stackTraceElements, occurrences) -> { - if (occurrences == 100) { - assertThatIterableContains(stackTraceElements, + Set> uniqueNonClosed = registry.getAllUnique(); + assertThat(uniqueNonClosed).hasSize(2); + assertThatIterableContains(uniqueNonClosed, entry -> + entry.getNumberAddedNotRemoved() == 100 || entry.getNumberAddedNotRemoved() == 1); + uniqueNonClosed.forEach(entry -> { + if (entry.getNumberAddedNotRemoved() == 100) { + assertThatIterableContains(entry.getStackTraceElements(), element -> element.getMethodName().equals("someOtherMethodWhichDoesNotClose")); - } else { // occurrences == 1 - assertThatIterableContains(stackTraceElements, + } else if (entry.getNumberAddedNotRemoved() == 1) { + assertThatIterableContains(entry.getStackTraceElements(), element -> element.getMethodName().equals("testDuplicateAllocationContexts")); + } else { + fail("Unexpected number of added, not removed: " + entry.getNumberAddedNotRemoved()); } }); - // one of the two has a StackTraceElement containing } // Something like this really should be in Google Truth... @@ -80,8 +83,13 @@ public class CloseTrackedRegistryTest { SomethingClosable forgotToCloseOnce = new SomethingClosable(debugContextDisabledRegistry); - assertThat(debugContextDisabledRegistry.getAllUnique()).hasSize(1); - assertThat(debugContextDisabledRegistry.getAllUnique().values().iterator().next()).isEqualTo(1); - assertThat(debugContextDisabledRegistry.getAllUnique().keySet().iterator().next()).isEmpty(); + Set> + closeRegistryReport = debugContextDisabledRegistry.getAllUnique(); + assertThat(closeRegistryReport).hasSize(1); + + CloseTrackedRegistryReportEntry + closeRegistryReportEntry1 = closeRegistryReport.iterator().next(); + assertThat(closeRegistryReportEntry1.getNumberAddedNotRemoved()).isEqualTo(1); + assertThat(closeRegistryReportEntry1.getStackTraceElements()).isEmpty(); } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java new file mode 100644 index 0000000000..0324986dbd --- /dev/null +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Red Hat, 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.controller.md.sal.trace.closetracker.impl.tests; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.Test; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; +import org.opendaylight.controller.md.sal.trace.dom.impl.TracingBroker; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.Config; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.ConfigBuilder; + +/** + * Test of {@link TracingBroker}. + * + * @author Michael Vorburger.ch + */ +public class TracingBrokerTest { + + @Test + @SuppressWarnings({ "resource", "unused" }) // Finding resource leaks is the point of this test + public void testPrintOpenTransactions() { + DOMDataBroker domDataBroker = mock(DOMDataBroker.class, RETURNS_DEEP_STUBS); + Config config = new ConfigBuilder().setTransactionDebugContextEnabled(true).build(); + BindingNormalizedNodeSerializer codec = mock(BindingNormalizedNodeSerializer.class); + TracingBroker tracingBroker = new TracingBroker(domDataBroker, config, codec); + + DOMTransactionChain txChain = tracingBroker.createTransactionChain(null); + DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + boolean printReturnValue = tracingBroker.printOpenTransactions(ps); + String output = new String(baos.toByteArray(), UTF_8); + + assertThat(printReturnValue).isTrue(); + assertThat(output).contains("testPrintOpenTransactions(TracingBrokerTest.java:41)"); // in a stack trace + + // We don't do any verify/times on the mocks, + // because the main point of the test is just to verify that + // printOpenTransactions runs through without any exceptions + // (e.g. it used to have a ClassCastException). + } + +} -- 2.36.6