Jonathan Hart
Committed by Gerrit Code Review

Unit tests for route store.

Change-Id: Ia711c497bb7d0751d692c2461c884ddc5287a2ef
...@@ -20,6 +20,8 @@ import org.onlab.packet.IpAddress; ...@@ -20,6 +20,8 @@ import org.onlab.packet.IpAddress;
20 import org.onlab.packet.IpPrefix; 20 import org.onlab.packet.IpPrefix;
21 import org.onlab.packet.MacAddress; 21 import org.onlab.packet.MacAddress;
22 22
23 +import java.util.Objects;
24 +
23 import static com.google.common.base.MoreObjects.toStringHelper; 25 import static com.google.common.base.MoreObjects.toStringHelper;
24 26
25 /** 27 /**
...@@ -84,6 +86,28 @@ public class ResolvedRoute { ...@@ -84,6 +86,28 @@ public class ResolvedRoute {
84 } 86 }
85 87
86 @Override 88 @Override
89 + public int hashCode() {
90 + return Objects.hash(prefix, nextHop, nextHopMac);
91 + }
92 +
93 + @Override
94 + public boolean equals(Object other) {
95 + if (this == other) {
96 + return true;
97 + }
98 +
99 + if (!(other instanceof ResolvedRoute)) {
100 + return false;
101 + }
102 +
103 + ResolvedRoute that = (ResolvedRoute) other;
104 +
105 + return Objects.equals(this.prefix, that.prefix) &&
106 + Objects.equals(this.nextHop, that.nextHop) &&
107 + Objects.equals(this.nextHopMac, that.nextHopMac);
108 + }
109 +
110 + @Override
87 public String toString() { 111 public String toString() {
88 return toStringHelper(this) 112 return toStringHelper(this)
89 .add("prefix", prefix) 113 .add("prefix", prefix)
......
...@@ -18,6 +18,8 @@ package org.onosproject.incubator.net.routing; ...@@ -18,6 +18,8 @@ package org.onosproject.incubator.net.routing;
18 18
19 import org.onosproject.event.AbstractEvent; 19 import org.onosproject.event.AbstractEvent;
20 20
21 +import java.util.Objects;
22 +
21 /** 23 /**
22 * Describes an event about a route. 24 * Describes an event about a route.
23 */ 25 */
...@@ -65,4 +67,24 @@ public class RouteEvent extends AbstractEvent<RouteEvent.Type, ResolvedRoute> { ...@@ -65,4 +67,24 @@ public class RouteEvent extends AbstractEvent<RouteEvent.Type, ResolvedRoute> {
65 super(type, subject, time); 67 super(type, subject, time);
66 } 68 }
67 69
70 + @Override
71 + public int hashCode() {
72 + return Objects.hash(subject(), type());
73 + }
74 +
75 + @Override
76 + public boolean equals(Object other) {
77 + if (this == other) {
78 + return true;
79 + }
80 +
81 + if (!(other instanceof RouteEvent)) {
82 + return false;
83 + }
84 +
85 + RouteEvent that = (RouteEvent) other;
86 +
87 + return Objects.equals(this.subject(), that.subject()) &&
88 + Objects.equals(this.type(), that.type());
89 + }
68 } 90 }
......
1 +/*
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 +
17 +package org.onosproject.incubator.net.routing.impl;
18 +
19 +import org.onosproject.incubator.net.routing.RouteEvent;
20 +
21 +/**
22 + * Queues updates for a route listener to ensure they are received in the
23 + * correct order.
24 + */
25 +interface ListenerQueue {
26 +
27 + /**
28 + * Posts an event to the listener.
29 + *
30 + * @param event event
31 + */
32 + void post(RouteEvent event);
33 +
34 + /**
35 + * Initiates event delivery to the listener.
36 + */
37 + void start();
38 +
39 + /**
40 + * Halts event delivery to the listener.
41 + */
42 + void stop();
43 +}
...@@ -114,7 +114,7 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -114,7 +114,7 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
114 public void addListener(RouteListener listener) { 114 public void addListener(RouteListener listener) {
115 synchronized (this) { 115 synchronized (this) {
116 log.debug("Synchronizing current routes to new listener"); 116 log.debug("Synchronizing current routes to new listener");
117 - ListenerQueue l = new ListenerQueue(listener); 117 + ListenerQueue l = createListenerQueue(listener);
118 routeStore.getRouteTables().forEach(table -> { 118 routeStore.getRouteTables().forEach(table -> {
119 Collection<Route> routes = routeStore.getRoutes(table); 119 Collection<Route> routes = routeStore.getRoutes(table);
120 if (routes != null) { 120 if (routes != null) {
...@@ -234,10 +234,19 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -234,10 +234,19 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
234 } 234 }
235 235
236 /** 236 /**
237 - * Queues updates for a route listener to ensure they are received in the 237 + * Creates a new listener queue.
238 - * correct order. 238 + *
239 + * @param listener route listener
240 + * @return listener queue
239 */ 241 */
240 - private class ListenerQueue { 242 + ListenerQueue createListenerQueue(RouteListener listener) {
243 + return new DefaultListenerQueue(listener);
244 + }
245 +
246 + /**
247 + * Default route listener queue.
248 + */
249 + private class DefaultListenerQueue implements ListenerQueue {
241 250
242 private final ExecutorService executorService; 251 private final ExecutorService executorService;
243 private final BlockingQueue<RouteEvent> queue; 252 private final BlockingQueue<RouteEvent> queue;
...@@ -248,31 +257,23 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -248,31 +257,23 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
248 * 257 *
249 * @param listener route listener to queue updates for 258 * @param listener route listener to queue updates for
250 */ 259 */
251 - public ListenerQueue(RouteListener listener) { 260 + public DefaultListenerQueue(RouteListener listener) {
252 this.listener = listener; 261 this.listener = listener;
253 queue = new LinkedBlockingQueue<>(); 262 queue = new LinkedBlockingQueue<>();
254 executorService = newSingleThreadExecutor(threadFactory); 263 executorService = newSingleThreadExecutor(threadFactory);
255 } 264 }
256 265
257 - /** 266 + @Override
258 - * Posts and event to the listener.
259 - *
260 - * @param event event
261 - */
262 public void post(RouteEvent event) { 267 public void post(RouteEvent event) {
263 queue.add(event); 268 queue.add(event);
264 } 269 }
265 270
266 - /** 271 + @Override
267 - * Initiates event delivery to the listener.
268 - */
269 public void start() { 272 public void start() {
270 executorService.execute(this::poll); 273 executorService.execute(this::poll);
271 } 274 }
272 275
273 - /** 276 + @Override
274 - * Halts event delivery to the listener.
275 - */
276 public void stop() { 277 public void stop() {
277 executorService.shutdown(); 278 executorService.shutdown();
278 } 279 }
......
1 +/*
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 +
17 +package org.onosproject.incubator.net.routing.impl;
18 +
19 +import com.google.common.collect.Sets;
20 +import org.junit.Before;
21 +import org.junit.Test;
22 +import org.onlab.packet.Ip4Address;
23 +import org.onlab.packet.Ip4Prefix;
24 +import org.onlab.packet.Ip6Address;
25 +import org.onlab.packet.Ip6Prefix;
26 +import org.onlab.packet.IpAddress;
27 +import org.onlab.packet.IpPrefix;
28 +import org.onlab.packet.MacAddress;
29 +import org.onlab.packet.VlanId;
30 +import org.onosproject.incubator.net.routing.ResolvedRoute;
31 +import org.onosproject.incubator.net.routing.Route;
32 +import org.onosproject.incubator.net.routing.RouteEvent;
33 +import org.onosproject.incubator.net.routing.RouteListener;
34 +import org.onosproject.incubator.store.routing.impl.LocalRouteStore;
35 +import org.onosproject.net.ConnectPoint;
36 +import org.onosproject.net.DefaultHost;
37 +import org.onosproject.net.DeviceId;
38 +import org.onosproject.net.Host;
39 +import org.onosproject.net.HostId;
40 +import org.onosproject.net.HostLocation;
41 +import org.onosproject.net.PortNumber;
42 +import org.onosproject.net.host.HostEvent;
43 +import org.onosproject.net.host.HostListener;
44 +import org.onosproject.net.host.HostService;
45 +import org.onosproject.net.host.HostServiceAdapter;
46 +import org.onosproject.net.provider.ProviderId;
47 +
48 +import java.util.Collections;
49 +
50 +import static org.easymock.EasyMock.anyObject;
51 +import static org.easymock.EasyMock.createMock;
52 +import static org.easymock.EasyMock.expect;
53 +import static org.easymock.EasyMock.expectLastCall;
54 +import static org.easymock.EasyMock.replay;
55 +import static org.easymock.EasyMock.reset;
56 +import static org.easymock.EasyMock.verify;
57 +
58 +/**
59 + * Unit tests for the route manager.
60 + */
61 +public class RouteManagerTest {
62 +
63 + private static final ConnectPoint CP1 = new ConnectPoint(
64 + DeviceId.deviceId("of:0000000000000001"),
65 + PortNumber.portNumber(1));
66 +
67 + private static final IpPrefix V4_PREFIX1 = Ip4Prefix.valueOf("1.1.1.0/24");
68 + private static final IpPrefix V6_PREFIX1 = Ip6Prefix.valueOf("4000::/64");
69 +
70 + private static final IpAddress V4_NEXT_HOP1 = Ip4Address.valueOf("192.168.10.1");
71 + private static final IpAddress V4_NEXT_HOP2 = Ip4Address.valueOf("192.168.20.1");
72 + private static final IpAddress V6_NEXT_HOP1 = Ip6Address.valueOf("1000::1");
73 + private static final IpAddress V6_NEXT_HOP2 = Ip6Address.valueOf("2000::1");
74 +
75 + private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01");
76 + private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02");
77 + private static final MacAddress MAC3 = MacAddress.valueOf("00:00:00:00:00:03");
78 + private static final MacAddress MAC4 = MacAddress.valueOf("00:00:00:00:00:04");
79 +
80 + private HostService hostService;
81 +
82 + private RouteListener routeListener;
83 + private HostListener hostListener;
84 +
85 + private RouteManager routeManager;
86 +
87 + @Before
88 + public void setUp() throws Exception {
89 + setUpHostService();
90 +
91 + routeListener = createMock(RouteListener.class);
92 +
93 + routeManager = new TestRouteManager();
94 + routeManager.hostService = hostService;
95 +
96 + LocalRouteStore routeStore = new LocalRouteStore();
97 + routeStore.activate();
98 + routeManager.routeStore = routeStore;
99 + routeManager.activate();
100 +
101 + routeManager.addListener(routeListener);
102 + }
103 +
104 + /**
105 + * Sets up the host service with details of some hosts.
106 + */
107 + private void setUpHostService() {
108 + hostService = createMock(HostService.class);
109 +
110 + hostService.addListener(anyObject(HostListener.class));
111 + expectLastCall().andDelegateTo(new TestHostService()).anyTimes();
112 +
113 + Host host1 = createHost(MAC1, V4_NEXT_HOP1);
114 + expectHost(host1);
115 +
116 + Host host2 = createHost(MAC2, V4_NEXT_HOP2);
117 + expectHost(host2);
118 +
119 + Host host3 = createHost(MAC3, V6_NEXT_HOP1);
120 + expectHost(host3);
121 +
122 + Host host4 = createHost(MAC4, V6_NEXT_HOP2);
123 + expectHost(host4);
124 +
125 + replay(hostService);
126 + }
127 +
128 + /**
129 + * Sets expectations on the host service for a given host.
130 + *
131 + * @param host host
132 + */
133 + private void expectHost(Host host) {
134 + // Assume the host only has one IP address
135 + IpAddress ip = host.ipAddresses().iterator().next();
136 +
137 + expect(hostService.getHostsByIp(ip))
138 + .andReturn(Sets.newHashSet(host)).anyTimes();
139 + hostService.startMonitoringIp(ip);
140 + expectLastCall().anyTimes();
141 + }
142 +
143 + /**
144 + * Creates a host with the given parameters.
145 + *
146 + * @param macAddress MAC address
147 + * @param ipAddress IP address
148 + * @return new host
149 + */
150 + private Host createHost(MacAddress macAddress, IpAddress ipAddress) {
151 + return new DefaultHost(ProviderId.NONE, HostId.NONE, macAddress,
152 + VlanId.NONE, new HostLocation(CP1, 1),
153 + Sets.newHashSet(ipAddress));
154 + }
155 +
156 + /**
157 + * Adds a route to the route service without expecting any specific events
158 + * to be sent to the route listeners.
159 + *
160 + * @param route route to add
161 + */
162 + private void addRoute(Route route) {
163 + reset(routeListener);
164 +
165 + routeListener.event(anyObject(RouteEvent.class));
166 + expectLastCall().once();
167 + replay(routeListener);
168 +
169 + routeManager.update(Collections.singleton(route));
170 +
171 + reset(routeListener);
172 + }
173 +
174 + /**
175 + * Tests adding routes to the route manager.
176 + */
177 + @Test
178 + public void testIpv4RouteAdd() {
179 + Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
180 + ResolvedRoute resolvedRoute = new ResolvedRoute(route, MAC1);
181 +
182 + testRouteAdd(route, resolvedRoute);
183 +
184 + route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
185 + resolvedRoute = new ResolvedRoute(route, MAC3);
186 +
187 + testRouteAdd(route, resolvedRoute);
188 + }
189 +
190 + /**
191 + * Tests adding a new route and verifies that the correct event was sent
192 + * to the route listener.
193 + *
194 + * @param route route to add
195 + * @param resolvedRoute resolved route that should be sent to the route
196 + * listener
197 + */
198 + private void testRouteAdd(Route route, ResolvedRoute resolvedRoute) {
199 + reset(routeListener);
200 +
201 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, resolvedRoute));
202 +
203 + replay(routeListener);
204 +
205 + routeManager.update(Collections.singleton(route));
206 +
207 + verify(routeListener);
208 + }
209 +
210 + /**
211 + * Tests updating routes in the route manager.
212 + */
213 + @Test
214 + public void testRouteUpdate() {
215 + Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
216 + Route updatedRoute = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP2);
217 + ResolvedRoute updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC2);
218 +
219 + testRouteUpdated(route, updatedRoute, updatedResolvedRoute);
220 +
221 + route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
222 + updatedRoute = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP2);
223 + updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC4);
224 +
225 + testRouteUpdated(route, updatedRoute, updatedResolvedRoute);
226 + }
227 +
228 + /**
229 + * Tests updating a route and verifies that the correct events are sent to
230 + * the route listener.
231 + *
232 + * @param original original route
233 + * @param updated updated route
234 + * @param updatedResolvedRoute resolved route that is expected to be sent to
235 + * the routelistener
236 + */
237 + private void testRouteUpdated(Route original, Route updated,
238 + ResolvedRoute updatedResolvedRoute) {
239 + // First add the original route
240 + addRoute(original);
241 +
242 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(original, null)));
243 + expectLastCall().once();
244 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, updatedResolvedRoute));
245 + expectLastCall().once();
246 +
247 + replay(routeListener);
248 +
249 + routeManager.update(Collections.singleton(updated));
250 +
251 + verify(routeListener);
252 + }
253 +
254 + /**
255 + * Tests deleting routes from the route manager.
256 + */
257 + @Test
258 + public void testIpv4RouteDelete() {
259 + Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
260 +
261 + testDelete(route);
262 +
263 + route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
264 +
265 + testDelete(route);
266 + }
267 +
268 + /**
269 + * Tests deleting a route and verifies that the correct event is sent to
270 + * the route listener.
271 + *
272 + * @param route route to delete
273 + */
274 + private void testDelete(Route route) {
275 + addRoute(route);
276 +
277 + RouteEvent withdrawRouteEvent = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED,
278 + new ResolvedRoute(route, null));
279 +
280 + reset(routeListener);
281 + routeListener.event(withdrawRouteEvent);
282 +
283 + replay(routeListener);
284 +
285 + routeManager.withdraw(Collections.singleton(route));
286 +
287 + verify(routeListener);
288 + }
289 +
290 + /**
291 + * Tests adding a route entry with asynchronous HostService replies.
292 + */
293 + @Test
294 + public void testAsyncRouteAdd() {
295 + Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
296 +
297 + // Host service will reply with no hosts when asked
298 + reset(hostService);
299 + expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
300 + Collections.emptySet()).anyTimes();
301 + hostService.startMonitoringIp(V4_NEXT_HOP1);
302 + replay(hostService);
303 +
304 + // Initially when we add the route, no route event will be sent because
305 + // the host is not known
306 + replay(routeListener);
307 +
308 + routeManager.update(Collections.singleton(route));
309 +
310 + verify(routeListener);
311 +
312 + // Now when we send the event, we expect the FIB update to be sent
313 + reset(routeListener);
314 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED,
315 + new ResolvedRoute(route, MAC1)));
316 + replay(routeListener);
317 +
318 + // Send in the host event
319 + Host host = createHost(MAC1, V4_NEXT_HOP1);
320 + hostListener.event(new HostEvent(HostEvent.Type.HOST_ADDED, host));
321 +
322 + verify(routeListener);
323 + }
324 +
325 + /**
326 + * Test host service that stores a reference to the host listener.
327 + */
328 + private class TestHostService extends HostServiceAdapter {
329 + @Override
330 + public void addListener(HostListener listener) {
331 + hostListener = listener;
332 + }
333 + }
334 +
335 + /**
336 + * Test route manager that extends the real route manager and injects a test
337 + * listener queue instead of the real listener queue.
338 + */
339 + private static class TestRouteManager extends RouteManager {
340 + @Override
341 + ListenerQueue createListenerQueue(RouteListener listener) {
342 + return new TestListenerQueue(listener);
343 + }
344 + }
345 +
346 + /**
347 + * Test listener queue that simply passes route events straight through to
348 + * the route listener on the caller's thread.
349 + */
350 + private static class TestListenerQueue implements ListenerQueue {
351 +
352 + private final RouteListener listener;
353 +
354 + public TestListenerQueue(RouteListener listener) {
355 + this.listener = listener;
356 + }
357 +
358 + @Override
359 + public void post(RouteEvent event) {
360 + listener.event(event);
361 + }
362 +
363 + @Override
364 + public void start() {
365 + }
366 +
367 + @Override
368 + public void stop() {
369 + }
370 + }
371 +
372 +}
...@@ -66,7 +66,7 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -66,7 +66,7 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
66 private Map<IpAddress, MacAddress> nextHops = new ConcurrentHashMap<>(); 66 private Map<IpAddress, MacAddress> nextHops = new ConcurrentHashMap<>();
67 67
68 @Activate 68 @Activate
69 - protected void activate() { 69 + public void activate() {
70 routeTables = new ConcurrentHashMap<>(); 70 routeTables = new ConcurrentHashMap<>();
71 71
72 routeTables.put(IPV4, new RouteTable()); 72 routeTables.put(IPV4, new RouteTable());
......