2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.rest.services.impl;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.when;
16 import com.google.common.collect.ImmutableClassToInstanceMap;
17 import com.google.common.collect.Maps;
20 import java.util.AbstractMap;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map.Entry;
29 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
30 import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
31 import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
32 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
33 import org.opendaylight.restconf.Draft15;
34 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
35 import org.opendaylight.restconf.handlers.SchemaContextHandler;
36 import org.opendaylight.restconf.utils.RestconfConstants;
37 import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
46 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
47 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
48 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
50 class RestconfModulesServiceTestUtils {
51 static final String MOUNT_POINT = "mount-point-1:cont/" + RestconfConstants.MOUNT + "/";
52 static final String NOT_REGISTERED_MOUNT_POINT = "mount-point-1:listA/" + RestconfConstants.MOUNT + "/";
54 static final String TEST_MODULE = "module1/2014-01-01";
55 static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
57 static final String TEST_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
58 + "module1-behind-mount-point/2014-02-03";
60 static final String NOT_EXISTING_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
61 + NOT_EXISTING_MODULE;
63 static final String TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT = NOT_REGISTERED_MOUNT_POINT
64 + "module1-behind-mount-point/2014-02-03";
66 // allowed leafs of list of modules
67 static final List<String> ALLOWED_KEYWORDS = Arrays.asList(
68 RestconfMappingNodeConstants.NAME, RestconfMappingNodeConstants.REVISION,
69 RestconfMappingNodeConstants.NAMESPACE, RestconfMappingNodeConstants.FEATURE);
71 static final String MODULES_PATH = "/modules";
72 static final String MODULES_WITHOUT_RESTCONF_MODULE_PATH = "/modules/modules-without-restconf-module";
73 static final String MOUNT_POINTS_PATH = "/modules/mount-points";
74 static final String MODULES_BEHIND_MOUNT_POINT_PATH = "/modules/modules-behind-mount-point";
76 static final String CUSTOM_RESTCONF_MODULES_PATH =
77 "/modules/restconf-module-testing/";
78 static final String CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH =
79 "/modules/restconf-module-testing-mount-point/";
81 private RestconfModulesServiceTestUtils() { throw new UnsupportedOperationException("Util class"); }
84 * Get all expected modules supported by the server
85 * @return <code>Set</code> of expected modules
88 static final Set<Module> getExpectedModules() throws Exception {
89 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).getModules();
93 * Get all expected modules behind mount point
94 * @return <code>Set</code> of expected modules
97 static final Set<Module> getExpectedModulesBehindMountPoint() throws Exception {
98 return TestRestconfUtils.loadSchemaContext(MODULES_BEHIND_MOUNT_POINT_PATH).getModules();
102 * Verify if correct modules were loaded into Restconf module by comparison with expected modules.
103 * @param expectedModules Expected modules
104 * @param loadedModules Loaded modules into Restconf module
106 static final void verifyModules(final Set<Module> expectedModules, final Set<TestModule> loadedModules) {
107 final Set<TestModule> expectedModulesTransformed = new HashSet<>();
108 expectedModules.forEach((x) -> expectedModulesTransformed.add(
109 new TestModule(x.getName(), x.getNamespace(), x.getRevision())));
110 assertEquals("Loaded modules are not as expected", expectedModulesTransformed, loadedModules);
114 * Verify id correct module was loaded into Restconf module by comparison of loaded and expected values of name,
115 * namespace, revision and features.
116 * @param expectedName Expected name
117 * @param expectedNamespace Expected namespace
118 * @param expectedRevision Expected revision
119 * @param expectedFeatures Expected features
120 * @param loadedModuleEntries Loaded values
122 static final void verifyModule(final String expectedName, final String expectedNamespace,
123 final String expectedRevision, final Set<Object> expectedFeatures,
124 final Iterator loadedModuleEntries) {
125 while (loadedModuleEntries.hasNext()) {
126 final Entry e = ((AbstractMap.SimpleImmutableEntry) loadedModuleEntries.next());
127 final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
129 assertTrue("Not allowed keyword", ALLOWED_KEYWORDS.contains(key));
132 case RestconfMappingNodeConstants.NAME:
133 assertEquals("Not correct module was found",
134 expectedName, ((LeafNode) e.getValue()).getValue());
136 case RestconfMappingNodeConstants.NAMESPACE:
137 assertEquals("Not correct module was found",
138 expectedNamespace, ((LeafNode) e.getValue()).getValue());
140 case RestconfMappingNodeConstants.REVISION:
141 assertEquals("Not correct module was found",
142 expectedRevision, ((LeafNode) e.getValue()).getValue());
144 case RestconfMappingNodeConstants.FEATURE:
145 assertEquals("Not correct module was found",
146 expectedFeatures, ((LeafSetNode) e.getValue()).getValue());
153 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
154 * module and testing modules supported by the server.
155 * @return <code>RestconfModulesServiceImpl</code>
158 static final RestconfModulesServiceImpl setupNormal() throws Exception {
159 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
160 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(MODULES_PATH));
161 return new RestconfModulesServiceImpl(schemaContextHandler, null);
165 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
166 * module and testing modules behind mount point.
167 * @return <code>RestconfModulesServiceImpl</code>
170 static final RestconfModulesServiceImpl setupNormalMountPoint() throws Exception {
171 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
173 final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
174 yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
175 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
177 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
178 when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
180 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
184 * Mock <code>SchemaContext</code> to load custom (modified) Restconf module.
185 * @param restconfModuleName Path of custom Restconf module
186 * @return <code>RestconfModulesServiceImpl</code>
189 static final RestconfModulesServiceImpl setupCustomRestconfModule(final String restconfModuleName)
191 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
192 final SchemaContext schemaContext = mock(SchemaContext.class);
193 when(schemaContextHandler.get()).thenReturn(schemaContext);
195 when(schemaContext.findModuleByNamespaceAndRevision(any(URI.class), any(Date.class))).thenAnswer(invocation -> {
196 final Object[] args = invocation.getArguments();
197 if (args[0] == Draft15.RestconfModule.IETF_RESTCONF_QNAME.getNamespace()
198 && args[1] == Draft15.RestconfModule.IETF_RESTCONF_QNAME.getRevision()) {
199 return parseCustomRestconfSource(restconfModuleName).findModuleByName(
200 restconfModuleName, (Date) args[1]);
202 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByNamespaceAndRevision(
203 (URI) args[0], (Date) args[1]);
207 when(schemaContext.findModuleByName(any(String.class), any(Date.class))).thenAnswer(invocation -> {
208 final Object[] args = invocation.getArguments();
209 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByName(
210 (String) args[0], (Date) args[1]);
213 return new RestconfModulesServiceImpl(schemaContextHandler, null);
217 * Mock <code>SchemaContext</code> to load custom (modified) Restconf module and prepare mount point.
218 * @param restconfModuleName
219 * @return <code>RestconfModulesServiceImpl</code>
222 static final RestconfModulesServiceImpl setupCustomRestconfModuleMountPoint(
223 final String restconfModuleName)throws Exception {
224 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
225 when(schemaContextHandler.get()).thenReturn(
226 parseCustomRestconfSourceMountPoint(restconfModuleName));
228 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
229 final DOMMountPointService mountPointService = getMountPointService();
230 when(mountPointServiceHandler.get()).thenReturn(mountPointService);
232 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
236 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module.
237 * @return <code>RestconfModulesServiceImpl</code>
240 static final RestconfModulesServiceImpl setupMissingRestconfModule() throws Exception {
241 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
242 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
243 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
245 return new RestconfModulesServiceImpl(schemaContextHandler, null);
249 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module and
251 * @return <code>RestconfModulesServiceImpl</code>
254 static final RestconfModulesServiceImpl setupMissingRestconfModuleMountPoint() throws Exception {
255 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
256 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
257 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
259 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
260 when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
262 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
266 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> with testing modules and mount
267 * points but <code>DOMMountPointServiceHandler</code> will contain <code>null</code> reference to
268 * <code>DOMMountPointService</code>.
269 * @return <code>RestconfModulesServiceImpl</code>
272 static final RestconfModulesServiceImpl setupNullMountPointService() throws Exception {
273 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
275 final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
276 yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
278 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
280 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
281 when(mountPointServiceHandler.get()).thenReturn(null);
283 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
287 * Create <code>DOMMountPointService</code> with one registered <code>SimpleDOMMountPoint</code>.
288 * @return <code>DOMMountPointService</code>
291 private static DOMMountPointService getMountPointService() throws Exception {
292 final DOMMountPointService mountPointService = new DOMMountPointServiceImpl();
293 ((DOMMountPointServiceImpl) mountPointService).registerMountPoint(
294 SimpleDOMMountPoint.create(
295 YangInstanceIdentifier.builder().node(
296 QName.create("mount:point:1", "2016-01-01", "cont")).build(),
297 ImmutableClassToInstanceMap.copyOf(Maps.newHashMap()),
298 TestRestconfUtils.loadSchemaContext(
299 MODULES_BEHIND_MOUNT_POINT_PATH)));
301 return mountPointService;
305 * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
306 * <code>restconfName</code> its dependencies and one testing module.
307 * @param restconfName File name of custom Restconf module
308 * @return <code>SchemaContext</code> containing custom Restconf module
310 private static SchemaContext parseCustomRestconfSource(final String restconfName) throws Exception {
311 final String restconf = TestRestconfUtils.class.getResource(
312 CUSTOM_RESTCONF_MODULES_PATH + restconfName + ".yang").getPath();
313 final String yangTypes = TestRestconfUtils.class.getResource(
314 CUSTOM_RESTCONF_MODULES_PATH + "ietf-yang-types.yang").getPath();
315 final String inetTypes = TestRestconfUtils.class.getResource(
316 CUSTOM_RESTCONF_MODULES_PATH + "ietf-inet-types.yang").getPath();
317 final String testModule = TestRestconfUtils.class.getResource(
318 MODULES_PATH + "/module1.yang").getPath();
320 final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
321 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
322 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
323 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
324 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(testModule), testModule)));
326 return reactor.buildEffective();
330 * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
331 * <code>restconfName</code> its dependencies and one mount point.
332 * @param restconfName File name of custom Restconf module
333 * @return <code>SchemaContext</code> containing custom Restconf module with one mount point
335 private static SchemaContext parseCustomRestconfSourceMountPoint(String restconfName) throws Exception {
336 final String restconf = TestRestconfUtils.class.getResource(
337 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH
338 + restconfName + "/" + restconfName + ".yang").getPath();
339 final String yangTypes = TestRestconfUtils.class.getResource(
340 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-yang-types.yang").getPath();
341 final String inetTypes = TestRestconfUtils.class.getResource(
342 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-inet-types.yang").getPath();
343 final String mountPoint = TestRestconfUtils.class.getResource(
344 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "mount-point-1.yang").getPath();
346 final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
347 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
348 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
349 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
350 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(mountPoint), mountPoint)));
352 return reactor.buildEffective();
357 * Module representation containing name, namespace and revision for easier comparison of modules.
359 static final class TestModule {
361 private String namespace;
362 private String revision;
366 TestModule(String name, URI namespace, Date revision) {
368 this.namespace = namespace.toString();
369 this.revision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
376 void setName(String name) {
380 String getNamespace() {
384 void setNamespace(String namespace) {
385 this.namespace = namespace;
388 String getRevision() {
392 void setRevision(String revision) {
393 this.revision = revision;
397 public boolean equals(Object o) {
398 if (this == o) return true;
399 if (o == null || getClass() != o.getClass()) return false;
401 TestModule that = (TestModule) o;
403 if (name != null ? !name.equals(that.name) : that.name != null) return false;
404 if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
405 return revision != null ? revision.equals(that.revision) : that.revision == null;
410 public int hashCode() {
411 int result = name != null ? name.hashCode() : 0;
412 result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
413 result = 31 * result + (revision != null ? revision.hashCode() : 0);