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;
15 import com.google.common.collect.ImmutableClassToInstanceMap;
16 import com.google.common.collect.Maps;
19 import java.util.AbstractMap;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Date;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map.Entry;
28 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
29 import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
30 import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
31 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
32 import org.opendaylight.restconf.Draft17;
33 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
34 import org.opendaylight.restconf.handlers.SchemaContextHandler;
35 import org.opendaylight.restconf.utils.RestconfConstants;
36 import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
42 import org.opendaylight.yangtools.yang.model.api.Module;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
45 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
46 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
47 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
49 class RestconfModulesServiceTestUtils {
50 static final String MOUNT_POINT = "mount-point-1:cont/" + RestconfConstants.MOUNT + "/";
51 static final String NOT_REGISTERED_MOUNT_POINT = "mount-point-1:listA/" + RestconfConstants.MOUNT + "/";
53 static final String TEST_MODULE = "module1/2014-01-01";
54 static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
56 static final String TEST_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
57 + "module1-behind-mount-point/2014-02-03";
59 static final String NOT_EXISTING_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
60 + NOT_EXISTING_MODULE;
62 static final String TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT = NOT_REGISTERED_MOUNT_POINT
63 + "module1-behind-mount-point/2014-02-03";
65 // allowed leafs of list of modules
66 static final List<String> ALLOWED_KEYWORDS = Arrays.asList(
67 RestconfMappingNodeConstants.NAME, RestconfMappingNodeConstants.REVISION,
68 RestconfMappingNodeConstants.NAMESPACE, RestconfMappingNodeConstants.FEATURE);
70 static final String MODULES_PATH = "/modules";
71 static final String MODULES_WITHOUT_RESTCONF_MODULE_PATH = "/modules/modules-without-restconf-module";
72 static final String MOUNT_POINTS_PATH = "/modules/mount-points";
73 static final String MODULES_BEHIND_MOUNT_POINT_PATH = "/modules/modules-behind-mount-point";
75 static final String CUSTOM_RESTCONF_MODULES_PATH =
76 "/modules/restconf-module-testing/";
77 static final String CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH =
78 "/modules/restconf-module-testing-mount-point/";
80 private RestconfModulesServiceTestUtils() { throw new UnsupportedOperationException("Util class"); }
83 * Get all expected modules supported by the server
84 * @return <code>Set</code> of expected modules
87 static final Set<Module> getExpectedModules() throws Exception {
88 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).getModules();
92 * Get all expected modules behind mount point
93 * @return <code>Set</code> of expected modules
96 static final Set<Module> getExpectedModulesBehindMountPoint() throws Exception {
97 return TestRestconfUtils.loadSchemaContext(MODULES_BEHIND_MOUNT_POINT_PATH).getModules();
101 * Verify if correct modules were loaded into Restconf module by comparison with expected modules.
102 * @param expectedModules Expected modules
103 * @param loadedModules Loaded modules into Restconf module
105 static final void verifyModules(final Set<Module> expectedModules, final Set<TestModule> loadedModules) {
106 final Set<TestModule> expectedModulesTransformed = new HashSet<>();
107 expectedModules.forEach((x) -> expectedModulesTransformed.add(
108 new TestModule(x.getName(), x.getNamespace(), x.getRevision())));
109 assertEquals("Loaded modules are not as expected", expectedModulesTransformed, loadedModules);
113 * Verify id correct module was loaded into Restconf module by comparison of loaded and expected values of name,
114 * namespace, revision and features.
115 * @param expectedName Expected name
116 * @param expectedNamespace Expected namespace
117 * @param expectedRevision Expected revision
118 * @param expectedFeatures Expected features
119 * @param loadedModuleEntries Loaded values
121 static final void verifyModule(final String expectedName, final String expectedNamespace,
122 final String expectedRevision, final Set<Object> expectedFeatures,
123 final Iterator loadedModuleEntries) {
124 while (loadedModuleEntries.hasNext()) {
125 final Entry e = ((AbstractMap.SimpleImmutableEntry) loadedModuleEntries.next());
126 final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
128 assertTrue("Not allowed keyword", ALLOWED_KEYWORDS.contains(key));
131 case RestconfMappingNodeConstants.NAME:
132 assertEquals("Not correct module was found",
133 expectedName, ((LeafNode) e.getValue()).getValue());
135 case RestconfMappingNodeConstants.NAMESPACE:
136 assertEquals("Not correct module was found",
137 expectedNamespace, ((LeafNode) e.getValue()).getValue());
139 case RestconfMappingNodeConstants.REVISION:
140 assertEquals("Not correct module was found",
141 expectedRevision, ((LeafNode) e.getValue()).getValue());
143 case RestconfMappingNodeConstants.FEATURE:
144 assertEquals("Not correct module was found",
145 expectedFeatures, ((LeafSetNode) e.getValue()).getValue());
152 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
153 * module and testing modules supported by the server.
154 * @return <code>RestconfModulesServiceImpl</code>
157 static final RestconfModulesServiceImpl setupNormal() throws Exception {
158 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
159 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(MODULES_PATH));
160 return new RestconfModulesServiceImpl(schemaContextHandler, null);
164 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> containing correct Restconf
165 * module and testing modules behind mount point.
166 * @return <code>RestconfModulesServiceImpl</code>
169 static final RestconfModulesServiceImpl setupNormalMountPoint() throws Exception {
170 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
172 final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
173 yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
174 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
176 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
177 when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
179 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
183 * Mock <code>SchemaContext</code> to load custom (modified) Restconf module.
184 * @param restconfModuleName Path of custom Restconf module
185 * @return <code>RestconfModulesServiceImpl</code>
188 static final RestconfModulesServiceImpl setupCustomRestconfModule(final String restconfModuleName)
190 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
191 final SchemaContext schemaContext = mock(SchemaContext.class);
192 when(schemaContextHandler.get()).thenReturn(schemaContext);
194 when(schemaContext.findModuleByNamespaceAndRevision(any(URI.class), any(Date.class))).thenAnswer(invocation -> {
195 final Object[] args = invocation.getArguments();
196 if ((args[0] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getNamespace())
197 && (args[1] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) {
198 return parseCustomRestconfSource(restconfModuleName).findModuleByName(
199 restconfModuleName, (Date) args[1]);
201 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByNamespaceAndRevision(
202 (URI) args[0], (Date) args[1]);
206 when(schemaContext.findModuleByName(any(String.class), any(Date.class))).thenAnswer(invocation -> {
207 final Object[] args = invocation.getArguments();
208 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByName(
209 (String) args[0], (Date) args[1]);
212 return new RestconfModulesServiceImpl(schemaContextHandler, null);
216 * Mock <code>SchemaContext</code> to load custom (modified) Restconf module and prepare mount point.
217 * @param restconfModuleName
218 * @return <code>RestconfModulesServiceImpl</code>
221 static final RestconfModulesServiceImpl setupCustomRestconfModuleMountPoint(
222 final String restconfModuleName)throws Exception {
223 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
224 when(schemaContextHandler.get()).thenReturn(
225 parseCustomRestconfSourceMountPoint(restconfModuleName));
227 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
228 final DOMMountPointService mountPointService = getMountPointService();
229 when(mountPointServiceHandler.get()).thenReturn(mountPointService);
231 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
235 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module.
236 * @return <code>RestconfModulesServiceImpl</code>
239 static final RestconfModulesServiceImpl setupMissingRestconfModule() throws Exception {
240 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
241 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
242 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
244 return new RestconfModulesServiceImpl(schemaContextHandler, null);
248 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module and
250 * @return <code>RestconfModulesServiceImpl</code>
253 static final RestconfModulesServiceImpl setupMissingRestconfModuleMountPoint() throws Exception {
254 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
255 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.loadSchemaContext(
256 MODULES_WITHOUT_RESTCONF_MODULE_PATH));
258 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
259 when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
261 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
265 * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> with testing modules and mount
266 * points but <code>DOMMountPointServiceHandler</code> will contain <code>null</code> reference to
267 * <code>DOMMountPointService</code>.
268 * @return <code>RestconfModulesServiceImpl</code>
271 static final RestconfModulesServiceImpl setupNullMountPointService() throws Exception {
272 final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
274 final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
275 yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
277 when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
279 final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
280 when(mountPointServiceHandler.get()).thenReturn(null);
282 return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
286 * Create <code>DOMMountPointService</code> with one registered <code>SimpleDOMMountPoint</code>.
287 * @return <code>DOMMountPointService</code>
290 private static DOMMountPointService getMountPointService() throws Exception {
291 final DOMMountPointService mountPointService = new DOMMountPointServiceImpl();
292 ((DOMMountPointServiceImpl) mountPointService).registerMountPoint(
293 SimpleDOMMountPoint.create(
294 YangInstanceIdentifier.builder().node(
295 QName.create("mount:point:1", "2016-01-01", "cont")).build(),
296 ImmutableClassToInstanceMap.copyOf(Maps.newHashMap()),
297 TestRestconfUtils.loadSchemaContext(
298 MODULES_BEHIND_MOUNT_POINT_PATH)));
300 return mountPointService;
304 * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
305 * <code>restconfName</code> its dependencies and one testing module.
306 * @param restconfName File name of custom Restconf module
307 * @return <code>SchemaContext</code> containing custom Restconf module
309 private static SchemaContext parseCustomRestconfSource(final String restconfName) throws Exception {
310 final String restconf = TestRestconfUtils.class.getResource(
311 CUSTOM_RESTCONF_MODULES_PATH + restconfName + ".yang").getPath();
312 final String yangTypes = TestRestconfUtils.class.getResource(
313 CUSTOM_RESTCONF_MODULES_PATH + "ietf-yang-types.yang").getPath();
314 final String inetTypes = TestRestconfUtils.class.getResource(
315 CUSTOM_RESTCONF_MODULES_PATH + "ietf-inet-types.yang").getPath();
316 final String testModule = TestRestconfUtils.class.getResource(
317 MODULES_PATH + "/module1.yang").getPath();
319 final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
320 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
321 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
322 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
323 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(testModule), testModule)));
325 return reactor.buildEffective();
329 * Parse custom sources for creating <code>SchemaContext</code> containing Restconf module specified by
330 * <code>restconfName</code> its dependencies and one mount point.
331 * @param restconfName File name of custom Restconf module
332 * @return <code>SchemaContext</code> containing custom Restconf module with one mount point
334 private static SchemaContext parseCustomRestconfSourceMountPoint(final String restconfName) throws Exception {
335 final String restconf = TestRestconfUtils.class.getResource(
336 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH
337 + restconfName + "/" + restconfName + ".yang").getPath();
338 final String yangTypes = TestRestconfUtils.class.getResource(
339 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-yang-types.yang").getPath();
340 final String inetTypes = TestRestconfUtils.class.getResource(
341 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "ietf-inet-types.yang").getPath();
342 final String mountPoint = TestRestconfUtils.class.getResource(
343 CUSTOM_RESTCONF_MODULES_MOUNT_POINT_PATH + "mount-point-1.yang").getPath();
345 final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
346 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(restconf), restconf)));
347 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(yangTypes), yangTypes)));
348 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(inetTypes), inetTypes)));
349 reactor.addSource(new YangStatementSourceImpl(new NamedFileInputStream(new File(mountPoint), mountPoint)));
351 return reactor.buildEffective();
356 * Module representation containing name, namespace and revision for easier comparison of modules.
358 static final class TestModule {
360 private String namespace;
361 private String revision;
365 TestModule(final String name, final URI namespace, final Date revision) {
367 this.namespace = namespace.toString();
368 this.revision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
375 void setName(final String name) {
379 String getNamespace() {
380 return this.namespace;
383 void setNamespace(final String namespace) {
384 this.namespace = namespace;
387 String getRevision() {
388 return this.revision;
391 void setRevision(final String revision) {
392 this.revision = revision;
396 public boolean equals(final Object o) {
400 if ((o == null) || (getClass() != o.getClass())) {
404 final TestModule that = (TestModule) o;
406 if (this.name != null ? !this.name.equals(that.name) : that.name != null) {
409 if (this.namespace != null ? !this.namespace.equals(that.namespace) : that.namespace != null) {
412 return this.revision != null ? this.revision.equals(that.revision) : that.revision == null;
417 public int hashCode() {
418 int result = this.name != null ? this.name.hashCode() : 0;
419 result = (31 * result) + (this.namespace != null ? this.namespace.hashCode() : 0);
420 result = (31 * result) + (this.revision != null ? this.revision.hashCode() : 0);