blob: 9f631db273a4a1147531b51d44e98c3ca67bea1d [file] [log] [blame]
pierfa48c6e2019-10-11 18:19:59 +02001/*
2 * Copyright 2019-present Open Networking Foundation
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 */
16package org.onosproject.store.primitives.impl;
17
18import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
21import org.junit.Before;
22import org.junit.Test;
23import org.onosproject.net.DeviceId;
24import org.onosproject.store.serializers.KryoNamespaces;
25import org.onosproject.store.service.AsyncConsistentMultimap;
26import org.onosproject.store.service.AsyncConsistentMultimapAdapter;
27import org.onosproject.store.service.Serializer;
28import org.onosproject.store.service.Versioned;
29
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.Comparator;
33import java.util.HashMap;
34import java.util.List;
35import java.util.Map;
36import java.util.Set;
37import java.util.concurrent.CompletableFuture;
38import java.util.function.Function;
39
40import static org.hamcrest.Matchers.containsInAnyOrder;
41import static org.hamcrest.Matchers.is;
42import static org.junit.Assert.assertThat;
43
44
45public class TranscodingAsyncConsistentMultimapTest {
46
47 private static Serializer serializer;
48 private static AsyncConsistentMultimap<String, DeviceId> transcodingMap;
49 private static AsyncConsistentMultimap<String, byte[]> baseMap;
50
51 private static final String KEY1 = "Key1";
52 private static final String KEY2 = "Key2";
53 private static final String KEY3 = "Key3";
54 private static final String KEY4 = "Key4";
55 private static final DeviceId DEV1 = DeviceId.deviceId("Device1");
56 private static final DeviceId DEV2 = DeviceId.deviceId("Device2");
57 private static final DeviceId DEV3 = DeviceId.deviceId("foo");
58 private static final DeviceId DEV4 = DeviceId.deviceId("bar");
59 private final List<String> allKeys = Lists.newArrayList(KEY1, KEY2,
60 KEY3, KEY4);
61 private final List<DeviceId> allValues = Lists.newArrayList(DEV1, DEV2,
62 DEV3, DEV4);
63
64 @Before
65 public void setUp() throws Exception {
66 // Init base map and serializer
67 serializer = Serializer.using(KryoNamespaces.API);
68 baseMap = new AsyncConsistentMultimapMock();
69 // Create the transcoding map
70 transcodingMap = DistributedPrimitives.newTranscodingMultimap(
71 baseMap,
72 Function.identity(),
73 Function.identity(),
74 serializer::encode,
75 serializer::decode);
76 }
77
78 @Test
79 public void testPutAllRemoveAll() throws Exception {
80 // Init phase
81 assertThat(transcodingMap.size().join(), is(0));
82 assertThat(transcodingMap.isEmpty().join(), is(true));
83 // Test multi put
84 Map<String, Collection<? extends DeviceId>> mapping = Maps.newHashMap();
85 // First build the mappings having each key a different mapping
86 allKeys.forEach(key -> {
87 switch (key) {
88 case KEY1:
89 mapping.put(key, Lists.newArrayList(allValues.subList(0, 1)));
90 break;
91 case KEY2:
92 mapping.put(key, Lists.newArrayList(allValues.subList(0, 2)));
93 break;
94 case KEY3:
95 mapping.put(key, Lists.newArrayList(allValues.subList(0, 3)));
96 break;
97 default:
98 mapping.put(key, Lists.newArrayList(allValues.subList(0, 4)));
99 break;
100 }
101 });
102 // Success
103 assertThat(transcodingMap.putAll(mapping).join(), is(true));
104 // Failure
105 assertThat(transcodingMap.putAll(mapping).join(), is(false));
106 // Verify operation
107 assertThat(transcodingMap.size().join(), is(10));
108 assertThat(transcodingMap.isEmpty().join(), is(false));
109 // verify mapping is ok
110 allKeys.forEach(key -> {
111 switch (key) {
112 case KEY1:
113 assertThat(Lists.newArrayList(Versioned.valueOrNull(transcodingMap.get(key).join())),
114 containsInAnyOrder(allValues.subList(0, 1).toArray()));
115 break;
116 case KEY2:
117 assertThat(Lists.newArrayList(Versioned.valueOrNull(transcodingMap.get(key).join())),
118 containsInAnyOrder(allValues.subList(0, 2).toArray()));
119 break;
120 case KEY3:
121 assertThat(Lists.newArrayList(Versioned.valueOrNull(transcodingMap.get(key).join())),
122 containsInAnyOrder(allValues.subList(0, 3).toArray()));
123 break;
124 default:
125 assertThat(Lists.newArrayList(Versioned.valueOrNull(transcodingMap.get(key).join())),
126 containsInAnyOrder(allValues.subList(0, 4).toArray()));
127 break;
128 }
129 });
130 // Success
131 assertThat(transcodingMap.removeAll(mapping).join(), is(true));
132 // Failure
133 assertThat(transcodingMap.removeAll(mapping).join(), is(false));
134 // Verify operation
135 assertThat(transcodingMap.size().join(), is(0));
136 assertThat(transcodingMap.isEmpty().join(), is(true));
137 }
138
139 // It uses a special internal map for bytes comparison - otherwise the equality cannot be verified
140 public static class AsyncConsistentMultimapMock extends AsyncConsistentMultimapAdapter<String, byte[]> {
141 private final Map<String, Set<byte[]>> baseMap = new HashMap<>();
142 private static final int DEFAULT_CREATION_TIME = 0;
143 private static final int DEFAULT_VERSION = 0;
144
145 AsyncConsistentMultimapMock() { }
146
147 Versioned<Collection<? extends byte[]>> makeVersioned(Collection<? extends byte[]> v) {
148 return new Versioned<>(v, DEFAULT_VERSION, DEFAULT_CREATION_TIME);
149 }
150
151 @Override
152 public CompletableFuture<Integer> size() {
153 return CompletableFuture.completedFuture(baseMap.values().stream()
154 .map(Set::size)
155 .mapToInt(size -> size).sum());
156 }
157
158 @Override
159 public CompletableFuture<Boolean> isEmpty() {
160 return CompletableFuture.completedFuture(baseMap.isEmpty());
161 }
162
163 @Override
164 public CompletableFuture<Boolean> putAll(Map<String, Collection<? extends byte[]>> mapping) {
165 CompletableFuture<Boolean> result = CompletableFuture.completedFuture(false);
166 for (Map.Entry<String, Collection<? extends byte[]>> entry : mapping.entrySet()) {
167 Set<byte[]> values = baseMap.computeIfAbsent(
168 entry.getKey(), k -> Sets.newTreeSet(new ByteArrayComparator()));
169 for (byte[] value : entry.getValue()) {
170 if (values.add(value)) {
171 result = CompletableFuture.completedFuture(true);
172 }
173 }
174 }
175 return result;
176 }
177
178 @Override
179 public CompletableFuture<Versioned<Collection<? extends byte[]>>> get(String key) {
180 return CompletableFuture.completedFuture(makeVersioned(baseMap.get(key)));
181 }
182
183 @Override
184 public CompletableFuture<Boolean> removeAll(Map<String, Collection<? extends byte[]>> mapping) {
185 CompletableFuture<Boolean> result = CompletableFuture.completedFuture(false);
186 for (Map.Entry<String, Collection<? extends byte[]>> entry : mapping.entrySet()) {
187 Set<byte[]> values = baseMap.get(entry.getKey());
188 if (values == null) {
189 return CompletableFuture.completedFuture(false);
190 }
191 for (byte[] value : entry.getValue()) {
192 if (values.remove(value)) {
193 result = CompletableFuture.completedFuture(true);
194 }
195 }
196 if (values.isEmpty()) {
197 baseMap.remove(entry.getKey());
198 }
199 }
200 return result;
201 }
202
203 private static class ByteArrayComparator implements Comparator<byte[]> {
204 @Override
205 public int compare(byte[] o1, byte[] o2) {
206 if (Arrays.equals(o1, o2)) {
207 return 0;
208 } else {
209 for (int i = 0; i < o1.length && i < o2.length; i++) {
210 if (o1[i] < o2[i]) {
211 return -1;
212 } else if (o1[i] > o2[i]) {
213 return 1;
214 }
215 }
216 return o1.length > o2.length ? 1 : -1;
217 }
218 }
219 }
220 }
221
222}