Bug 9060: TracingBroker printOpenTransactions 73/62373/7
authorMichael Vorburger <vorburger@redhat.com>
Mon, 28 Aug 2017 16:38:50 +0000 (18:38 +0200)
committerTom Pantelis <tompantelis@gmail.com>
Wed, 30 Aug 2017 15:56:44 +0000 (15:56 +0000)
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 <vorburger@redhat.com>
12 files changed:
opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistryReportEntry.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/CloseTrackedRegistryTest.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java [new file with mode: 0644]

index 14d30b1..34d5eb4 100644 (file)
@@ -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);
+
 }
index aaafeb4..785e2ef 100644 (file)
@@ -19,7 +19,7 @@ public abstract class AbstractCloseTracked<T extends AbstractCloseTracked<T>> im
     private final CloseTrackedTrait<T> closeTracker;
 
     protected AbstractCloseTracked(CloseTrackedRegistry<T> transactionChainRegistry) {
-        this.closeTracker = new CloseTrackedTrait<>(transactionChainRegistry);
+        this.closeTracker = new CloseTrackedTrait<>(transactionChainRegistry, this);
     }
 
     protected void removeFromTrackedRegistry() {
@@ -30,4 +30,9 @@ public abstract class AbstractCloseTracked<T extends AbstractCloseTracked<T>> im
     public @Nullable StackTraceElement[] getAllocationContextStackTrace() {
         return closeTracker.getAllocationContextStackTrace();
     }
+
+    @Override
+    public final CloseTracked<T> getRealCloseTracked() {
+        return this;
+    }
 }
index f5fd341..926c8b3 100644 (file)
@@ -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".
  *
- * <p>Includes preserving the context of the call stack which created this object, and the instant it was created.
+ * <p>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<T extends CloseTracked<T>> {
 
     @Nullable StackTraceElement[] getAllocationContextStackTrace();
 
+    CloseTracked<T> getRealCloseTracked();
 }
index 7689831..4e60839 100644 (file)
@@ -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<T extends CloseTracked<T>> {
 
-    // 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<CloseTracked<T>> tracked = new ConcurrentSkipListSet<>(
         (o1, o2) -> Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2)));
@@ -58,6 +60,14 @@ public class CloseTrackedRegistry<T extends CloseTracked<T>> {
         return isDebugContextEnabled;
     }
 
+    public Object getAnchor() {
+        return anchor;
+    }
+
+    public String getCreateDescription() {
+        return createDescription;
+    }
+
     // package protected, not public; only CloseTrackedTrait invokes this
     void add(CloseTracked<T> closeTracked) {
         tracked.add(closeTracked);
@@ -72,20 +82,33 @@ public class CloseTrackedRegistry<T extends CloseTracked<T>> {
      * 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<List<StackTraceElement>, Long> getAllUnique() {
-        Map<List<StackTraceElement>,Long> mapToReturn = new HashMap<>();
+    public Set<CloseTrackedRegistryReportEntry<T>> getAllUnique() {
+        Map<List<StackTraceElement>, Long> map = new HashMap<>();
         Set<CloseTracked<T>> copyOfTracked = new HashSet<>(tracked);
         for (CloseTracked<T> closeTracked : copyOfTracked) {
             final StackTraceElement[] stackTraceArray = closeTracked.getAllocationContextStackTrace();
             List<StackTraceElement> 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<CloseTrackedRegistryReportEntry<T>> report = new HashSet<>();
+        map.forEach((stackTraceElements, number) -> {
+            copyOfTracked.stream().filter(closeTracked -> {
+                StackTraceElement[] closeTrackedStackTraceArray = closeTracked.getAllocationContextStackTrace();
+                List<StackTraceElement> 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 (file)
index 0000000..1681eb8
--- /dev/null
@@ -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<T extends CloseTracked<T>> {
+
+    private final CloseTracked<T> exampleCloseTracked;
+    private final long numberAddedNotRemoved;
+    private final List<StackTraceElement> stackTraceElements;
+
+    public CloseTrackedRegistryReportEntry(CloseTracked<T> exampleCloseTracked, long numberAddedNotRemoved,
+            List<StackTraceElement> stackTraceElements) {
+        super();
+        this.exampleCloseTracked = requireNonNull(exampleCloseTracked, "closeTracked");
+        this.numberAddedNotRemoved = requireNonNull(numberAddedNotRemoved, "numberAddedNotRemoved");
+        this.stackTraceElements = requireNonNull(stackTraceElements, "stackTraceElements");
+    }
+
+    public long getNumberAddedNotRemoved() {
+        return numberAddedNotRemoved;
+    }
+
+    public CloseTracked<T> getExampleCloseTracked() {
+        return exampleCloseTracked;
+    }
+
+    public List<StackTraceElement> getStackTraceElements() {
+        return stackTraceElements;
+    }
+
+    @Override
+    public String toString() {
+        return "CloseTrackedRegistryReportEntry [numberAddedNotRemoved=" + numberAddedNotRemoved + ", closeTracked="
+                + exampleCloseTracked + ", stackTraceElements.size=" + stackTraceElements.size() + "]";
+    }
+
+
+}
index b846c04..b1b4131 100644 (file)
@@ -24,13 +24,15 @@ public class CloseTrackedTrait<T extends CloseTracked<T>> implements CloseTracke
 
     private final @Nullable Throwable allocationContext;
     private final CloseTrackedRegistry<T> closeTrackedRegistry;
+    private final CloseTracked<T> realCloseTracked;
 
-    public CloseTrackedTrait(CloseTrackedRegistry<T> transactionChainRegistry) {
+    public CloseTrackedTrait(CloseTrackedRegistry<T> transactionChainRegistry, CloseTracked<T> 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<T extends CloseTracked<T>> implements CloseTracke
         closeTrackedRegistry.remove(this);
     }
 
+    @Override
+    public CloseTracked<T> getRealCloseTracked() {
+        return realCloseTracked;
+    }
+
 }
index c01c1d9..b08e19a 100644 (file)
@@ -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();
+    }
 }
index fe57c81..091e2a4 100644 (file)
@@ -33,7 +33,7 @@ class TracingReadWriteTransaction
     TracingReadWriteTransaction(DOMDataReadWriteTransaction delegate, TracingBroker tracingBroker,
             CloseTrackedRegistry<TracingReadWriteTransaction> 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<TracingReadWriteTransaction> getRealCloseTracked() {
+        return this;
+    }
 }
index c8f4539..ac6aaa8 100644 (file)
@@ -29,10 +29,11 @@ class TracingTransactionChain extends AbstractCloseTracked<TracingTransactionCha
         this.delegate = Objects.requireNonNull(delegate);
         this.tracingBroker = Objects.requireNonNull(tracingBroker);
 
-        final boolean isDebugging = transactionChainsRegistry.isDebugContextEnabled();
-        this.readOnlyTransactionsRegistry  = new CloseTrackedRegistry<>(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<TracingTransactionCha
         super.removeFromTrackedRegistry();
     }
 
+    public CloseTrackedRegistry<TracingReadOnlyTransaction> getReadOnlyTransactionsRegistry() {
+        return readOnlyTransactionsRegistry;
+    }
+
+    public CloseTrackedRegistry<TracingReadWriteTransaction> getReadWriteTransactionsRegistry() {
+        return readWriteTransactionsRegistry;
+    }
+
+    public CloseTrackedRegistry<TracingWriteTransaction> getWriteTransactionsRegistry() {
+        return writeTransactionsRegistry;
+    }
 }
index 7e98e40..5cd0d63 100644 (file)
@@ -25,7 +25,7 @@ class TracingWriteTransaction extends AbstractTracingWriteTransaction
     TracingWriteTransaction(DOMDataWriteTransaction delegate, TracingBroker tracingBroker,
             CloseTrackedRegistry<TracingWriteTransaction> 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<TracingWriteTransaction> getRealCloseTracked() {
+        return this;
+    }
+
 }
index ba34cbf..a3a8b49 100644 (file)
@@ -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<List<StackTraceElement>, Long> uniqueNonClosed = registry.getAllUnique();
-        assertThat(uniqueNonClosed.values()).containsExactly(100L, 1L);
-        uniqueNonClosed.forEach((stackTraceElements, occurrences) -> {
-            if (occurrences == 100) {
-                assertThatIterableContains(stackTraceElements,
+        Set<CloseTrackedRegistryReportEntry<SomethingClosable>> 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<CloseTrackedRegistryReportEntry<SomethingClosable>>
+            closeRegistryReport = debugContextDisabledRegistry.getAllUnique();
+        assertThat(closeRegistryReport).hasSize(1);
+
+        CloseTrackedRegistryReportEntry<SomethingClosable>
+            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 (file)
index 0000000..0324986
--- /dev/null
@@ -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).
+    }
+
+}

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.