2 * Copyright (c) 2018 Inocybe Technologies and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.dom.adapter.test;
10 import static org.junit.Assert.fail;
12 import com.google.common.util.concurrent.SettableFuture;
13 import com.google.common.util.concurrent.Uninterruptibles;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.TimeoutException;
22 import java.util.function.Function;
23 import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
24 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.mdsal.binding.api.DataTreeModification;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.yangtools.yang.binding.DataObject;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 * Abstract base that provides a DTCL for verification.
34 * @author Thomas Pantelis
36 public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBrokerTest {
37 protected static final class TestListener<T extends DataObject> implements DataTreeChangeListener<T> {
38 private final SettableFuture<Collection<DataTreeModification<T>>> future = SettableFuture.create();
39 private final List<DataTreeModification<T>> accumulatedChanges = new ArrayList<>();
40 private final Function<DataTreeModification<T>, Boolean>[] matchers;
41 private final int expChangeCount;
43 private TestListener(final Function<DataTreeModification<T>, Boolean>[] matchers) {
44 this.expChangeCount = matchers.length;
45 this.matchers = matchers;
49 public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
50 synchronized (accumulatedChanges) {
51 accumulatedChanges.addAll(changes);
52 if (expChangeCount == accumulatedChanges.size()) {
53 future.set(new ArrayList<>(accumulatedChanges));
58 public Collection<DataTreeModification<T>> changes() {
60 final Collection<DataTreeModification<T>> changes = future.get(5, TimeUnit.SECONDS);
61 Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
63 } catch (InterruptedException | TimeoutException | ExecutionException e) {
64 throw new AssertionError(String.format(
65 "Data tree change notifications not received. Expected: %s. Actual: %s - %s",
66 expChangeCount, accumulatedChanges.size(), accumulatedChanges), e);
70 public void verify() {
71 Collection<DataTreeModification<T>> changes = new ArrayList<>(changes());
72 Iterator<DataTreeModification<T>> iter = changes.iterator();
73 while (iter.hasNext()) {
74 DataTreeModification<T> dataTreeModification = iter.next();
75 for (Function<DataTreeModification<T>, Boolean> matcher: matchers) {
76 if (matcher.apply(dataTreeModification)) {
83 if (!changes.isEmpty()) {
84 DataTreeModification<T> mod = changes.iterator().next();
85 fail(String.format("Received unexpected notification: type: %s, path: %s, before: %s, after: %s",
86 mod.getRootNode().getModificationType(), mod.getRootPath().getRootIdentifier(),
87 mod.getRootNode().getDataBefore(), mod.getRootNode().getDataAfter()));
91 public boolean hasChanges() {
92 synchronized (accumulatedChanges) {
93 return !accumulatedChanges.isEmpty();
98 protected AbstractDataTreeChangeListenerTest() {
103 protected final <T extends DataObject> TestListener<T> createListener(final LogicalDatastoreType store,
104 final InstanceIdentifier<T> path, final Function<DataTreeModification<T>, Boolean>... matchers) {
105 TestListener<T> listener = new TestListener<>(matchers);
106 getDataBroker().registerDataTreeChangeListener(DataTreeIdentifier.create(store, path), listener);
110 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(
111 final ModificationType type, final InstanceIdentifier<T> path, final Function<T, Boolean> checkDataBefore,
112 final Function<T, Boolean> checkDataAfter) {
113 return modification -> type == modification.getRootNode().getModificationType()
114 && path.equals(modification.getRootPath().getRootIdentifier())
115 && checkDataBefore.apply(modification.getRootNode().getDataBefore())
116 && checkDataAfter.apply(modification.getRootNode().getDataAfter());
119 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(final ModificationType type,
120 final InstanceIdentifier<T> path, final T expDataBefore, final T expDataAfter) {
121 return match(type, path, dataBefore -> Objects.equals(expDataBefore, dataBefore),
122 (Function<T, Boolean>) dataAfter -> Objects.equals(expDataAfter, dataAfter));
125 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> added(
126 final InstanceIdentifier<T> path, final T data) {
127 return match(ModificationType.WRITE, path, null, data);
130 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> replaced(
131 final InstanceIdentifier<T> path, final T dataBefore, final T dataAfter) {
132 return match(ModificationType.WRITE, path, dataBefore, dataAfter);
135 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> deleted(
136 final InstanceIdentifier<T> path, final T dataBefore) {
137 return match(ModificationType.DELETE, path, dataBefore, null);
140 public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> subtreeModified(
141 final InstanceIdentifier<T> path, final T dataBefore, final T dataAfter) {
142 return match(ModificationType.SUBTREE_MODIFIED, path, dataBefore, dataAfter);