Migrate OSGI compendium reference
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / test / java / org / opendaylight / controller / md / sal / binding / test / AbstractDataTreeChangeListenerTest.java
1 /*
2  * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.md.sal.binding.test;
9
10 import static org.junit.Assert.fail;
11
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.controller.md.sal.binding.api.DataObjectModification.ModificationType;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.yangtools.yang.binding.DataObject;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
30
31 /**
32  * Abstract base that provides a DTCL for verification.
33  *
34  * @author Thomas Pantelis
35  */
36 @Deprecated
37 public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBrokerTest {
38     protected static final class TestListener<T extends DataObject> implements DataTreeChangeListener<T> {
39
40         private final List<DataTreeModification<T>> accumulatedChanges = new ArrayList<>();
41         private final SettableFuture<Collection<DataTreeModification<T>>> future = SettableFuture.create();
42         private final Function<DataTreeModification<T>, Boolean>[] matchers;
43         private final int expChangeCount;
44
45         private TestListener(Function<DataTreeModification<T>, Boolean>[] matchers) {
46             this.expChangeCount = matchers.length;
47             this.matchers = matchers;
48         }
49
50         @Override
51         public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
52             synchronized (accumulatedChanges) {
53                 accumulatedChanges.addAll(changes);
54                 if (expChangeCount == accumulatedChanges.size()) {
55                     future.set(new ArrayList<>(accumulatedChanges));
56                 }
57             }
58         }
59
60         public Collection<DataTreeModification<T>> changes() {
61             try {
62                 final Collection<DataTreeModification<T>> changes = future.get(5, TimeUnit.SECONDS);
63                 Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
64                 return changes;
65             } catch (InterruptedException | TimeoutException | ExecutionException e) {
66                 throw new AssertionError(String.format(
67                     "Data tree change notifications not received. Expected: %s. Actual: %s - %s",
68                         expChangeCount, accumulatedChanges.size(), accumulatedChanges), e);
69             }
70         }
71
72         public void verify() {
73             Collection<DataTreeModification<T>> changes = new ArrayList<>(changes());
74             Iterator<DataTreeModification<T>> iter = changes.iterator();
75             while (iter.hasNext()) {
76                 DataTreeModification<T> dataTreeModification = iter.next();
77                 for (Function<DataTreeModification<T>, Boolean> matcher: matchers) {
78                     if (matcher.apply(dataTreeModification)) {
79                         iter.remove();
80                         break;
81                     }
82                 }
83             }
84
85             if (!changes.isEmpty()) {
86                 DataTreeModification<T> mod = changes.iterator().next();
87                 fail(String.format("Received unexpected notification: type: %s, path: %s, before: %s, after: %s",
88                         mod.getRootNode().getModificationType(), mod.getRootPath().getRootIdentifier(),
89                         mod.getRootNode().getDataBefore(), mod.getRootNode().getDataAfter()));
90             }
91         }
92
93         public boolean hasChanges() {
94             synchronized (accumulatedChanges) {
95                 return !accumulatedChanges.isEmpty();
96             }
97         }
98     }
99
100     protected AbstractDataTreeChangeListenerTest() {
101         super(true);
102     }
103
104     @SafeVarargs
105     protected final <T extends DataObject> TestListener<T> createListener(final LogicalDatastoreType store,
106             final InstanceIdentifier<T> path, Function<DataTreeModification<T>, Boolean>... matchers) {
107         TestListener<T> listener = new TestListener<>(matchers);
108         getDataBroker().registerDataTreeChangeListener(new DataTreeIdentifier<>(store, path), listener);
109         return listener;
110     }
111
112     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(
113             ModificationType type, InstanceIdentifier<T> path, Function<T, Boolean> checkDataBefore,
114             Function<T, Boolean> checkDataAfter) {
115         return modification -> type == modification.getRootNode().getModificationType()
116                 && path.equals(modification.getRootPath().getRootIdentifier())
117                 && checkDataBefore.apply(modification.getRootNode().getDataBefore())
118                 && checkDataAfter.apply(modification.getRootNode().getDataAfter());
119     }
120
121     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(
122             ModificationType type, InstanceIdentifier<T> path, T expDataBefore, T expDataAfter) {
123         return match(type, path, dataBefore -> Objects.equals(expDataBefore, dataBefore),
124             (Function<T, Boolean>) dataAfter -> Objects.equals(expDataAfter, dataAfter));
125     }
126
127     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> added(
128             InstanceIdentifier<T> path, T data) {
129         return match(ModificationType.WRITE, path, null, data);
130     }
131
132     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> replaced(
133             InstanceIdentifier<T> path, T dataBefore, T dataAfter) {
134         return match(ModificationType.WRITE, path, dataBefore, dataAfter);
135     }
136
137     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> deleted(
138             InstanceIdentifier<T> path, T dataBefore) {
139         return match(ModificationType.DELETE, path, dataBefore, null);
140     }
141
142     public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> subtreeModified(
143             InstanceIdentifier<T> path, T dataBefore, T dataAfter) {
144         return match(ModificationType.SUBTREE_MODIFIED, path, dataBefore, dataAfter);
145     }
146 }