blob: 0759e43cabd88c275201b240c3aafecb0e31a1c3 [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;
25import io.atomix.resource.ResourceType;
26
27import java.util.Map;
28import java.util.UUID;
29import java.util.concurrent.ArrayBlockingQueue;
30import java.util.concurrent.BlockingQueue;
31
32import org.junit.AfterClass;
33import org.junit.BeforeClass;
Madan Jampani79924fa2016-09-13 13:57:03 -070034import org.junit.Test;
35import org.onosproject.store.service.DocumentPath;
36import org.onosproject.store.service.DocumentTreeEvent;
37import org.onosproject.store.service.DocumentTreeListener;
38import org.onosproject.store.service.IllegalDocumentModificationException;
39import org.onosproject.store.service.NoSuchDocumentPathException;
40import org.onosproject.store.service.Versioned;
41
42import com.google.common.base.Throwables;
43
44/**
45 * Unit tests for {@link AtomixDocumentTree}.
46 */
47public class AtomixDocumentTreeTest extends AtomixTestBase {
48 @BeforeClass
49 public static void preTestSetup() throws Throwable {
50 createCopycatServers(3);
51 }
52
53 @AfterClass
54 public static void postTestCleanup() throws Exception {
55 clearTests();
56 }
57 @Override
58 protected ResourceType resourceType() {
59 return new ResourceType(AtomixDocumentTree.class);
60 }
61 /**
62 * Tests queries (get and getChildren).
63 */
64 @Test
65 public void testQueries() throws Throwable {
66 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
67 AtomixDocumentTree.class).join();
68 Versioned<byte[]> root = tree.get(DocumentPath.from("root")).join();
69 assertEquals(1, root.version());
70 assertNull(root.value());
71 }
72
73 /**
74 * Tests create.
75 */
76 @Test
77 public void testCreate() throws Throwable {
78 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
79 AtomixDocumentTree.class).join();
80 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
81 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
82 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
83 Versioned<byte[]> a = tree.get(DocumentPath.from("root.a")).join();
84 assertArrayEquals("a".getBytes(), a.value());
85
86 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
87 assertArrayEquals("ab".getBytes(), ab.value());
88
89 Versioned<byte[]> ac = tree.get(DocumentPath.from("root.a.c")).join();
90 assertArrayEquals("ac".getBytes(), ac.value());
91 }
92
93 /**
Madan Jampani86983282016-09-15 14:55:48 -070094 * Tests recursive create.
95 */
96 @Test
97 public void testRecursiveCreate() throws Throwable {
98 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
99 AtomixDocumentTree.class).join();
100 tree.createRecursive(DocumentPath.from("root.a.b.c"), "abc".getBytes()).join();
101 Versioned<byte[]> a = tree.get(DocumentPath.from("root.a")).join();
102 assertArrayEquals(new byte[0], a.value());
103
104 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
105 assertArrayEquals(new byte[0], ab.value());
106
107 Versioned<byte[]> abc = tree.get(DocumentPath.from("root.a.b.c")).join();
108 assertArrayEquals("abc".getBytes(), abc.value());
109 }
110
111 /**
Madan Jampani79924fa2016-09-13 13:57:03 -0700112 * Tests set.
113 */
114 @Test
115 public void testSet() throws Throwable {
116 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
117 AtomixDocumentTree.class).join();
118 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
119 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
120 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
121
122 tree.set(DocumentPath.from("root.a.d"), "ad".getBytes()).join();
123 Versioned<byte[]> ad = tree.get(DocumentPath.from("root.a.d")).join();
124 assertArrayEquals("ad".getBytes(), ad.value());
125
126 tree.set(DocumentPath.from("root.a"), "newA".getBytes()).join();
127 Versioned<byte[]> newA = tree.get(DocumentPath.from("root.a")).join();
128 assertArrayEquals("newA".getBytes(), newA.value());
129
130 tree.set(DocumentPath.from("root.a.b"), "newAB".getBytes()).join();
131 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
132 assertArrayEquals("newAB".getBytes(), newAB.value());
133 }
134
135 /**
136 * Tests replace if version matches.
137 */
138 @Test
139 public void testReplaceVersion() throws Throwable {
140 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
141 AtomixDocumentTree.class).join();
142 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
143 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
144 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
145
146 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
147 assertTrue(tree.replace(DocumentPath.from("root.a.b"), "newAB".getBytes(), ab.version()).join());
148 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
149 assertArrayEquals("newAB".getBytes(), newAB.value());
150
151 assertFalse(tree.replace(DocumentPath.from("root.a.b"), "newestAB".getBytes(), ab.version()).join());
152 assertArrayEquals("newAB".getBytes(), tree.get(DocumentPath.from("root.a.b")).join().value());
153
154 assertFalse(tree.replace(DocumentPath.from("root.a.d"), "foo".getBytes(), 1).join());
155 }
156
157 /**
158 * Tests replace if value matches.
159 */
160 @Test
161 public void testReplaceValue() throws Throwable {
162 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
163 AtomixDocumentTree.class).join();
164 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
165 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
166 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
167
168 Versioned<byte[]> ab = tree.get(DocumentPath.from("root.a.b")).join();
169 assertTrue(tree.replace(DocumentPath.from("root.a.b"), "newAB".getBytes(), ab.value()).join());
170 Versioned<byte[]> newAB = tree.get(DocumentPath.from("root.a.b")).join();
171 assertArrayEquals("newAB".getBytes(), newAB.value());
172
173 assertFalse(tree.replace(DocumentPath.from("root.a.b"), "newestAB".getBytes(), ab.value()).join());
174 assertArrayEquals("newAB".getBytes(), tree.get(DocumentPath.from("root.a.b")).join().value());
175
176 assertFalse(tree.replace(DocumentPath.from("root.a.d"), "bar".getBytes(), "foo".getBytes()).join());
177 }
178
179 /**
180 * Tests remove.
181 */
182 @Test
183 public void testRemove() throws Throwable {
184 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
185 AtomixDocumentTree.class).join();
186 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
187 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
188 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
189
190 Versioned<byte[]> ab = tree.removeNode(DocumentPath.from("root.a.b")).join();
191 assertArrayEquals("ab".getBytes(), ab.value());
192 assertNull(tree.get(DocumentPath.from("root.a.b")).join());
193
194 Versioned<byte[]> ac = tree.removeNode(DocumentPath.from("root.a.c")).join();
195 assertArrayEquals("ac".getBytes(), ac.value());
196 assertNull(tree.get(DocumentPath.from("root.a.c")).join());
197
198 Versioned<byte[]> a = tree.removeNode(DocumentPath.from("root.a")).join();
199 assertArrayEquals("a".getBytes(), a.value());
200 assertNull(tree.get(DocumentPath.from("root.a")).join());
201 }
202
203 /**
204 * Tests invalid removes.
205 */
206 @Test
207 public void testRemoveFailures() throws Throwable {
208 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
209 AtomixDocumentTree.class).join();
210 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
211 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
212 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
213
214 try {
215 tree.removeNode(DocumentPath.from("root")).join();
216 fail();
217 } catch (Exception e) {
218 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
219 }
220
221 try {
222 tree.removeNode(DocumentPath.from("root.a")).join();
223 fail();
224 } catch (Exception e) {
225 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
226 }
227
228 try {
229 tree.removeNode(DocumentPath.from("root.d")).join();
230 fail();
231 } catch (Exception e) {
232 assertTrue(Throwables.getRootCause(e) instanceof NoSuchDocumentPathException);
233 }
234 }
235
236 /**
237 * Tests invalid create.
238 */
239 @Test
240 public void testCreateFailures() throws Throwable {
241 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
242 AtomixDocumentTree.class).join();
243 try {
244 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
245 fail();
246 } catch (Exception e) {
247 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
248 }
249 }
250
251 /**
252 * Tests invalid set.
253 */
254 @Test
255 public void testSetFailures() throws Throwable {
256 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
257 AtomixDocumentTree.class).join();
258 try {
259 tree.set(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
260 fail();
261 } catch (Exception e) {
262 assertTrue(Throwables.getRootCause(e) instanceof IllegalDocumentModificationException);
263 }
264 }
265
266 /**
267 * Tests getChildren.
268 */
269 @Test
270 public void testGetChildren() throws Throwable {
271 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
272 AtomixDocumentTree.class).join();
273 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
274 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
275 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
276
277 Map<String, Versioned<byte[]>> rootChildren = tree.getChildren(DocumentPath.from("root")).join();
278 assertEquals(1, rootChildren.size());
279 Versioned<byte[]> a = rootChildren.get("a");
280 assertArrayEquals("a".getBytes(), a.value());
281
282 Map<String, Versioned<byte[]>> children = tree.getChildren(DocumentPath.from("root.a")).join();
283 assertEquals(2, children.size());
284 Versioned<byte[]> ab = children.get("b");
285 assertArrayEquals("ab".getBytes(), ab.value());
286 Versioned<byte[]> ac = children.get("c");
287 assertArrayEquals("ac".getBytes(), ac.value());
288
289 assertEquals(0, tree.getChildren(DocumentPath.from("root.a.b")).join().size());
290 assertEquals(0, tree.getChildren(DocumentPath.from("root.a.c")).join().size());
291 }
292
293 /**
294 * Tests destroy.
295 */
296 @Test
297 public void testClear() {
298 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
299 AtomixDocumentTree.class).join();
300 tree.create(DocumentPath.from("root.a"), "a".getBytes()).join();
301 tree.create(DocumentPath.from("root.a.b"), "ab".getBytes()).join();
302 tree.create(DocumentPath.from("root.a.c"), "ac".getBytes()).join();
303
304 tree.destroy().join();
305 assertEquals(0, tree.getChildren(DocumentPath.from("root")).join().size());
306 }
307
308 /**
309 * Tests listeners.
310 */
311 @Test
Madan Jampani79924fa2016-09-13 13:57:03 -0700312 public void testNotifications() throws Exception {
313 AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
314 AtomixDocumentTree.class).join();
315 TestEventListener listener = new TestEventListener();
316
317 // add listener; create a node in the tree and verify an CREATED event is received.
318 tree.addListener(listener).thenCompose(v -> tree.set(DocumentPath.from("root.a"), "a".getBytes())).join();
319 DocumentTreeEvent<byte[]> event = listener.event();
Madan Jampani79924fa2016-09-13 13:57:03 -0700320 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
Madan Jampanicdbf6772016-09-15 15:41:34 -0700321 assertFalse(event.oldValue().isPresent());
Madan Jampani79924fa2016-09-13 13:57:03 -0700322 assertArrayEquals("a".getBytes(), event.newValue().get().value());
Madan Jampanicdbf6772016-09-15 15:41:34 -0700323 // update a node in the tree and verify an UPDATED event is received.
324 tree.set(DocumentPath.from("root.a"), "newA".getBytes()).join();
325 event = listener.event();
326 assertEquals(DocumentTreeEvent.Type.UPDATED, event.type());
327 assertArrayEquals("newA".getBytes(), event.newValue().get().value());
328 assertArrayEquals("a".getBytes(), event.oldValue().get().value());
329 // remove a node in the tree and verify an REMOVED event is received.
330 tree.removeNode(DocumentPath.from("root.a")).join();
331 event = listener.event();
332 assertEquals(DocumentTreeEvent.Type.DELETED, event.type());
333 assertFalse(event.newValue().isPresent());
334 assertArrayEquals("newA".getBytes(), event.oldValue().get().value());
335 // recursively create a node and verify CREATED events for all intermediate nodes.
336 tree.createRecursive(DocumentPath.from("root.x.y"), "xy".getBytes()).join();
337 event = listener.event();
338 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
339 assertEquals(DocumentPath.from("root.x"), event.path());
340 event = listener.event();
341 assertEquals(DocumentTreeEvent.Type.CREATED, event.type());
342 assertEquals(DocumentPath.from("root.x.y"), event.path());
343 assertArrayEquals("xy".getBytes(), event.newValue().get().value());
Madan Jampani79924fa2016-09-13 13:57:03 -0700344 }
345
346 private static class TestEventListener implements DocumentTreeListener<byte[]> {
347
348 private final BlockingQueue<DocumentTreeEvent<byte[]>> queue = new ArrayBlockingQueue<>(1);
349
350 @Override
351 public void event(DocumentTreeEvent<byte[]> event) {
352 try {
353 queue.put(event);
354 } catch (InterruptedException e) {
355 Throwables.propagate(e);
356 }
357 }
358
Madan Jampani79924fa2016-09-13 13:57:03 -0700359 public DocumentTreeEvent<byte[]> event() throws InterruptedException {
360 return queue.take();
361 }
362 }
363}