blob: eb520001763fa6bd4f2cd37af64f6e9254210003 [file] [log] [blame]
Madan Jampani79924fa2016-09-13 13:57:03 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.store.primitives.resources.impl;
18
19import static org.junit.Assert.assertArrayEquals;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
Madan Jampani79924fa2016-09-13 13:57:03 -070022import static org.junit.Assert.assertNull;
23import static org.junit.Assert.assertTrue;
24import static org.junit.Assert.fail;
Madan Jampani98094222016-09-15 21:12:46 -070025import io.atomix.AtomixClient;
Madan Jampani79924fa2016-09-13 13:57:03 -070026import io.atomix.resource.ResourceType;
27
28import java.util.Map;
29import java.util.UUID;
30import java.util.concurrent.ArrayBlockingQueue;
31import java.util.concurrent.BlockingQueue;
32
33import org.junit.AfterClass;
34import org.junit.BeforeClass;
Madan Jampani79924fa2016-09-13 13:57:03 -070035import org.junit.Test;
36import org.onosproject.store.service.DocumentPath;
37import org.onosproject.store.service.DocumentTreeEvent;
38import org.onosproject.store.service.DocumentTreeListener;
39import org.onosproject.store.service.IllegalDocumentModificationException;
40import org.onosproject.store.service.NoSuchDocumentPathException;
41import org.onosproject.store.service.Versioned;
42
43import com.google.common.base.Throwables;
44
45/**
46 * Unit tests for {@link AtomixDocumentTree}.
47 */
48public class AtomixDocumentTreeTest extends AtomixTestBase {
49 @BeforeClass
50 public static void preTestSetup() throws Throwable {
51 createCopycatServers(3);
52 }
53
54 @AfterClass
55 public static void postTestCleanup() throws Exception {
56 clearTests();
57 }
58 @Override
59 protected ResourceType resourceType() {
60 return new ResourceType(AtomixDocumentTree.class);
61 }
62 /**
63 * Tests queries (get and getChildren).
64 */
65 @Test
66 public void testQueries() throws Throwable {
67 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
68 AtomixDocumentTree.class).join();
69 Versioned<byte[]> root = tree.get(DocumentPath.from("root")).join();
70 assertEquals(1, root.version());
71 assertNull(root.value());
72 }
73
74 /**
75 * Tests create.
76 */
77 @Test
78 public void testCreate() throws Throwable {
79 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
80 AtomixDocumentTree.class).join();
81 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
82 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
83 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
84 Versioned<byte[]> a = tree.get(DocumentPath.from("root.a")).join();
85 assertArrayEquals("a".getBytes(), a.value());
86
87 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
88 assertArrayEquals("ab".getBytes(), ab.value());
89
90 Versioned<byte[]> ac = tree.get(DocumentPath.from("root.a.c")).join();
91 assertArrayEquals("ac".getBytes(), ac.value());
92 }
93
94 /**
Madan Jampani86983282016-09-15 14:55:48 -070095 * Tests recursive create.
96 */
97 @Test
98 public void testRecursiveCreate() throws Throwable {
99 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
100 AtomixDocumentTree.class).join();
101 tree.createRecursive(DocumentPath.from("root.a.b.c"), "abc".getBytes()).join();
102 Versioned<byte[]> a = tree.get(DocumentPath.from("root.a")).join();
103 assertArrayEquals(new byte[0], a.value());
104
105 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
106 assertArrayEquals(new byte[0], ab.value());
107
108 Versioned<byte[]> abc = tree.get(DocumentPath.from("root.a.b.c")).join();
109 assertArrayEquals("abc".getBytes(), abc.value());
110 }
111
112 /**
Madan Jampani79924fa2016-09-13 13:57:03 -0700113 * Tests set.
114 */
115 @Test
116 public void testSet() throws Throwable {
117 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
118 AtomixDocumentTree.class).join();
119 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
120 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
121 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
122
123 tree.set(DocumentPath.from("root.a.d"), "ad".getBytes()).join();
124 Versioned<byte[]> ad = tree.get(DocumentPath.from("root.a.d")).join();
125 assertArrayEquals("ad".getBytes(), ad.value());
126
127 tree.set(DocumentPath.from("root.a"), "newA".getBytes()).join();
128 Versioned<byte[]> newA = tree.get(DocumentPath.from("root.a")).join();
129 assertArrayEquals("newA".getBytes(), newA.value());
130
131 tree.set(DocumentPath.from("root.a.b"), "newAB".getBytes()).join();
132 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
133 assertArrayEquals("newAB".getBytes(), newAB.value());
134 }
135
136 /**
137 * Tests replace if version matches.
138 */
139 @Test
140 public void testReplaceVersion() throws Throwable {
141 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
142 AtomixDocumentTree.class).join();
143 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
144 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
145 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
146
147 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
148 assertTrue(tree.replace(DocumentPath.from("root.a.b"), "newAB".getBytes(), ab.version()).join());
149 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
150 assertArrayEquals("newAB".getBytes(), newAB.value());
151
152 assertFalse(tree.replace(DocumentPath.from("root.a.b"), "newestAB".getBytes(), ab.version()).join());
153 assertArrayEquals("newAB".getBytes(), tree.get(DocumentPath.from("root.a.b")).join().value());
154
155 assertFalse(tree.replace(DocumentPath.from("root.a.d"), "foo".getBytes(), 1).join());
156 }
157
158 /**
159 * Tests replace if value matches.
160 */
161 @Test
162 public void testReplaceValue() throws Throwable {
163 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
164 AtomixDocumentTree.class).join();
165 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
166 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
167 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
168
169 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
170 assertTrue(tree.replace(DocumentPath.from("root.a.b"), "newAB".getBytes(), ab.value()).join());
171 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
172 assertArrayEquals("newAB".getBytes(), newAB.value());
173
174 assertFalse(tree.replace(DocumentPath.from("root.a.b"), "newestAB".getBytes(), ab.value()).join());
175 assertArrayEquals("newAB".getBytes(), tree.get(DocumentPath.from("root.a.b")).join().value());
176
177 assertFalse(tree.replace(DocumentPath.from("root.a.d"), "bar".getBytes(), "foo".getBytes()).join());
178 }
179
180 /**
181 * Tests remove.
182 */
183 @Test
184 public void testRemove() throws Throwable {
185 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
186 AtomixDocumentTree.class).join();
187 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
188 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
189 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
190
191 Versioned<byte[]> ab = tree.removeNode(DocumentPath.from("root.a.b")).join();
192 assertArrayEquals("ab".getBytes(), ab.value());
193 assertNull(tree.get(DocumentPath.from("root.a.b")).join());
194
195 Versioned<byte[]> ac = tree.removeNode(DocumentPath.from("root.a.c")).join();
196 assertArrayEquals("ac".getBytes(), ac.value());
197 assertNull(tree.get(DocumentPath.from("root.a.c")).join());
198
199 Versioned<byte[]> a = tree.removeNode(DocumentPath.from("root.a")).join();
200 assertArrayEquals("a".getBytes(), a.value());
201 assertNull(tree.get(DocumentPath.from("root.a")).join());
202 }
203
204 /**
205 * Tests invalid removes.
206 */
207 @Test
208 public void testRemoveFailures() throws Throwable {
209 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
210 AtomixDocumentTree.class).join();
211 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
212 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
213 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
214
215 try {
216 tree.removeNode(DocumentPath.from("root")).join();
217 fail();
218 } catch (Exception e) {
219 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
220 }
221
222 try {
223 tree.removeNode(DocumentPath.from("root.a")).join();
224 fail();
225 } catch (Exception e) {
226 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
227 }
228
229 try {
230 tree.removeNode(DocumentPath.from("root.d")).join();
231 fail();
232 } catch (Exception e) {
233 assertTrue(Throwables.getRootCause(e) instanceof NoSuchDocumentPathException);
234 }
235 }
236
237 /**
238 * Tests invalid create.
239 */
240 @Test
241 public void testCreateFailures() throws Throwable {
242 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
243 AtomixDocumentTree.class).join();
244 try {
245 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
246 fail();
247 } catch (Exception e) {
248 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
249 }
250 }
251
252 /**
253 * Tests invalid set.
254 */
255 @Test
256 public void testSetFailures() throws Throwable {
257 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
258 AtomixDocumentTree.class).join();
259 try {
260 tree.set(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
261 fail();
262 } catch (Exception e) {
263 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
264 }
265 }
266
267 /**
268 * Tests getChildren.
269 */
270 @Test
271 public void testGetChildren() throws Throwable {
272 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
273 AtomixDocumentTree.class).join();
274 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
275 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
276 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
277
278 Map<String, Versioned<byte[]>> rootChildren = tree.getChildren(DocumentPath.from("root")).join();
279 assertEquals(1, rootChildren.size());
280 Versioned<byte[]> a = rootChildren.get("a");
281 assertArrayEquals("a".getBytes(), a.value());
282
283 Map<String, Versioned<byte[]>> children = tree.getChildren(DocumentPath.from("root.a")).join();
284 assertEquals(2, children.size());
285 Versioned<byte[]> ab = children.get("b");
286 assertArrayEquals("ab".getBytes(), ab.value());
287 Versioned<byte[]> ac = children.get("c");
288 assertArrayEquals("ac".getBytes(), ac.value());
289
290 assertEquals(0, tree.getChildren(DocumentPath.from("root.a.b")).join().size());
291 assertEquals(0, tree.getChildren(DocumentPath.from("root.a.c")).join().size());
292 }
293
294 /**
295 * Tests destroy.
296 */
297 @Test
298 public void testClear() {
299 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
300 AtomixDocumentTree.class).join();
301 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
302 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
303 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
304
305 tree.destroy().join();
306 assertEquals(0, tree.getChildren(DocumentPath.from("root")).join().size());
307 }
308
309 /**
310 * Tests listeners.
311 */
312 @Test
Madan Jampani79924fa2016-09-13 13:57:03 -0700313 public void testNotifications() throws Exception {
314 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
315 AtomixDocumentTree.class).join();
316 TestEventListener listener = new TestEventListener();
317
318 // add listener; create a node in the tree and verify an CREATED event is received.
319 tree.addListener(listener).thenCompose(v -> tree.set(DocumentPath.from("root.a"), "a".getBytes())).join();
320 DocumentTreeEvent<byte[]> event = listener.event();
Madan Jampani79924fa2016-09-13 13:57:03 -0700321 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
Madan Jampanicdbf6772016-09-15 15:41:34 -0700322 assertFalse(event.oldValue().isPresent());
Madan Jampani79924fa2016-09-13 13:57:03 -0700323 assertArrayEquals("a".getBytes(), event.newValue().get().value());
Madan Jampanicdbf6772016-09-15 15:41:34 -0700324 // update a node in the tree and verify an UPDATED event is received.
325 tree.set(DocumentPath.from("root.a"), "newA".getBytes()).join();
326 event = listener.event();
327 assertEquals(DocumentTreeEvent.Type.UPDATED, event.type());
328 assertArrayEquals("newA".getBytes(), event.newValue().get().value());
329 assertArrayEquals("a".getBytes(), event.oldValue().get().value());
330 // remove a node in the tree and verify an REMOVED event is received.
331 tree.removeNode(DocumentPath.from("root.a")).join();
332 event = listener.event();
333 assertEquals(DocumentTreeEvent.Type.DELETED, event.type());
334 assertFalse(event.newValue().isPresent());
335 assertArrayEquals("newA".getBytes(), event.oldValue().get().value());
336 // recursively create a node and verify CREATED events for all intermediate nodes.
337 tree.createRecursive(DocumentPath.from("root.x.y"), "xy".getBytes()).join();
338 event = listener.event();
339 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
340 assertEquals(DocumentPath.from("root.x"), event.path());
341 event = listener.event();
342 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
343 assertEquals(DocumentPath.from("root.x.y"), event.path());
344 assertArrayEquals("xy".getBytes(), event.newValue().get().value());
Madan Jampani79924fa2016-09-13 13:57:03 -0700345 }
346
Madan Jampani98094222016-09-15 21:12:46 -0700347 @Test
348 public void testFilteredNotifications() throws Throwable {
349 AtomixClient client1 = createAtomixClient();
350 AtomixClient client2 = createAtomixClient();
351
352 String treeName = UUID.randomUUID().toString();
353 AtomixDocumentTree tree1 = client1.getResource(treeName, AtomixDocumentTree.class).join();
354 AtomixDocumentTree tree2 = client2.getResource(treeName, AtomixDocumentTree.class).join();
355
356 TestEventListener listener1a = new TestEventListener(3);
357 TestEventListener listener1ab = new TestEventListener(2);
358 TestEventListener listener2abc = new TestEventListener(1);
359
360 tree1.addListener(DocumentPath.from("root.a"), listener1a).join();
361 tree1.addListener(DocumentPath.from("root.a.b"), listener1ab).join();
362 tree2.addListener(DocumentPath.from("root.a.b.c"), listener2abc).join();
363
364 tree1.createRecursive(DocumentPath.from("root.a.b.c"), "abc".getBytes()).join();
365 DocumentTreeEvent<byte[]> event = listener1a.event();
366 assertEquals(DocumentPath.from("root.a"), event.path());
367 event = listener1a.event();
368 assertEquals(DocumentPath.from("root.a.b"), event.path());
369 event = listener1a.event();
370 assertEquals(DocumentPath.from("root.a.b.c"), event.path());
371 event = listener1ab.event();
372 assertEquals(DocumentPath.from("root.a.b"), event.path());
373 event = listener1ab.event();
374 assertEquals(DocumentPath.from("root.a.b.c"), event.path());
375 event = listener2abc.event();
376 assertEquals(DocumentPath.from("root.a.b.c"), event.path());
377 }
378
Madan Jampani79924fa2016-09-13 13:57:03 -0700379 private static class TestEventListener implements DocumentTreeListener<byte[]> {
380
Madan Jampani98094222016-09-15 21:12:46 -0700381 private final BlockingQueue<DocumentTreeEvent<byte[]>> queue;
382
383 public TestEventListener() {
384 this(1);
385 }
386
387 public TestEventListener(int maxEvents) {
388 queue = new ArrayBlockingQueue<>(maxEvents);
389 }
Madan Jampani79924fa2016-09-13 13:57:03 -0700390
391 @Override
392 public void event(DocumentTreeEvent<byte[]> event) {
Madan Jampani98094222016-09-15 21:12:46 -0700393
Madan Jampani79924fa2016-09-13 13:57:03 -0700394 try {
395 queue.put(event);
396 } catch (InterruptedException e) {
397 Throwables.propagate(e);
398 }
399 }
400
Madan Jampani79924fa2016-09-13 13:57:03 -0700401 public DocumentTreeEvent<byte[]> event() throws InterruptedException {
402 return queue.take();
403 }
404 }
405}