636ebdeab0f098c4936c8f367763bde9d7cf65aa
[netconf.git] / restconf / sal-rest-connector / src / test / java / org / opendaylight / restconf / rest / services / impl / RestconfModulesServiceTestUtils.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.rest.services.impl;
9
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;
17 import java.io.File;
18 import java.net.URI;
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;
27 import java.util.Set;
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;
48
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 + "/";
52
53     static final String TEST_MODULE = "module1/2014-01-01";
54     static final String NOT_EXISTING_MODULE = "not-existing/2016-01-01";
55
56     static final String TEST_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
57             + "module1-behind-mount-point/2014-02-03";
58
59     static final String NOT_EXISTING_MODULE_BEHIND_MOUNT_POINT = MOUNT_POINT
60             + NOT_EXISTING_MODULE;
61
62     static final String TEST_MODULE_BEHIND_NOT_REGISTERED_MOUNT_POINT = NOT_REGISTERED_MOUNT_POINT
63             + "module1-behind-mount-point/2014-02-03";
64
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);
69
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";
74
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/";
79
80     private RestconfModulesServiceTestUtils() { throw new UnsupportedOperationException("Util class"); }
81
82     /**
83      * Get all expected modules supported by the server
84      * @return <code>Set</code> of expected modules
85      * @throws Exception
86      */
87     static final Set<Module> getExpectedModules() throws Exception {
88         return TestRestconfUtils.loadSchemaContext(MODULES_PATH).getModules();
89     }
90
91     /**
92      * Get all expected modules behind mount point
93      * @return <code>Set</code> of expected modules
94      * @throws Exception
95      */
96     static final Set<Module> getExpectedModulesBehindMountPoint() throws Exception {
97         return TestRestconfUtils.loadSchemaContext(MODULES_BEHIND_MOUNT_POINT_PATH).getModules();
98     }
99
100     /**
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
104      */
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);
110     }
111
112     /**
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
120      */
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();
127
128             assertTrue("Not allowed keyword", ALLOWED_KEYWORDS.contains(key));
129
130             switch (key) {
131                 case RestconfMappingNodeConstants.NAME:
132                     assertEquals("Not correct module was found",
133                             expectedName, ((LeafNode) e.getValue()).getValue());
134                     break;
135                 case RestconfMappingNodeConstants.NAMESPACE:
136                     assertEquals("Not correct module was found",
137                             expectedNamespace, ((LeafNode) e.getValue()).getValue());
138                     break;
139                 case RestconfMappingNodeConstants.REVISION:
140                     assertEquals("Not correct module was found",
141                             expectedRevision, ((LeafNode) e.getValue()).getValue());
142                 break;
143                 case RestconfMappingNodeConstants.FEATURE:
144                     assertEquals("Not correct module was found",
145                             expectedFeatures, ((LeafSetNode) e.getValue()).getValue());
146                     break;
147             }
148         }
149     }
150
151     /**
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>
155      * @throws Exception
156      */
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);
161     }
162
163     /**
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>
167      * @throws Exception
168      */
169     static final RestconfModulesServiceImpl setupNormalMountPoint() throws Exception {
170         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
171
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));
175
176         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
177         when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
178
179         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
180     }
181
182     /**
183      * Mock <code>SchemaContext</code> to load custom (modified) Restconf module.
184      * @param restconfModuleName Path of custom Restconf module
185      * @return <code>RestconfModulesServiceImpl</code>
186      * @throws Exception
187      */
188     static final RestconfModulesServiceImpl setupCustomRestconfModule(final String restconfModuleName)
189             throws Exception {
190         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
191         final SchemaContext schemaContext = mock(SchemaContext.class);
192         when(schemaContextHandler.get()).thenReturn(schemaContext);
193
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]);
200             } else {
201                 return TestRestconfUtils.loadSchemaContext(MODULES_PATH).findModuleByNamespaceAndRevision(
202                         (URI) args[0], (Date) args[1]);
203             }
204         });
205
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]);
210         });
211
212         return new RestconfModulesServiceImpl(schemaContextHandler, null);
213     }
214
215     /**
216      * Mock <code>SchemaContext</code> to load custom (modified) Restconf module and prepare mount point.
217      * @param restconfModuleName
218      * @return <code>RestconfModulesServiceImpl</code>
219      * @throws Exception
220      */
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));
226
227         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
228         final DOMMountPointService mountPointService = getMountPointService();
229         when(mountPointServiceHandler.get()).thenReturn(mountPointService);
230
231         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
232     }
233
234     /**
235      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module.
236      * @return <code>RestconfModulesServiceImpl</code>
237      * @throws Exception
238      */
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));
243
244         return new RestconfModulesServiceImpl(schemaContextHandler, null);
245     }
246
247     /**
248      * Prepare <code>RestconfModulesServiceImpl</code> with <code>SchemaContext</code> without Restconf module and
249      * mount point.
250      * @return <code>RestconfModulesServiceImpl</code>
251      * @throws Exception
252      */
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));
257
258         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
259         when(mountPointServiceHandler.get()).thenReturn(getMountPointService());
260
261         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
262     }
263
264     /**
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>
269      * @throws Exception
270      */
271     static final RestconfModulesServiceImpl setupNullMountPointService() throws Exception {
272         final SchemaContextHandler schemaContextHandler = mock(SchemaContextHandler.class);
273
274         final Collection<File> yangFiles = TestRestconfUtils.loadFiles(MODULES_PATH);
275         yangFiles.addAll(TestRestconfUtils.loadFiles(MOUNT_POINTS_PATH));
276
277         when(schemaContextHandler.get()).thenReturn(TestRestconfUtils.parseYangSources(yangFiles));
278
279         final DOMMountPointServiceHandler mountPointServiceHandler = mock(DOMMountPointServiceHandler.class);
280         when(mountPointServiceHandler.get()).thenReturn(null);
281
282         return new RestconfModulesServiceImpl(schemaContextHandler, mountPointServiceHandler);
283     }
284
285     /**
286      * Create <code>DOMMountPointService</code> with one registered <code>SimpleDOMMountPoint</code>.
287      * @return <code>DOMMountPointService</code>
288      * @throws Exception
289      */
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)));
299
300         return mountPointService;
301     }
302
303     /**
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
308      */
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();
318
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)));
324
325         return reactor.buildEffective();
326     }
327
328     /**
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
333      */
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();
344
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)));
350
351         return reactor.buildEffective();
352     }
353
354
355     /**
356      * Module representation containing name, namespace and revision for easier comparison of modules.
357      */
358     static final class TestModule {
359         private String name;
360         private String namespace;
361         private String revision;
362
363         TestModule() {}
364
365         TestModule(final String name, final URI namespace, final Date revision) {
366             this.name = name;
367             this.namespace = namespace.toString();
368             this.revision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
369         }
370
371         String getName() {
372             return this.name;
373         }
374
375         void setName(final String name) {
376             this.name = name;
377         }
378
379         String getNamespace() {
380             return this.namespace;
381         }
382
383         void setNamespace(final String namespace) {
384             this.namespace = namespace;
385         }
386
387         String getRevision() {
388             return this.revision;
389         }
390
391         void setRevision(final String revision) {
392             this.revision = revision;
393         }
394
395         @Override
396         public boolean equals(final Object o) {
397             if (this == o) {
398                 return true;
399             }
400             if ((o == null) || (getClass() != o.getClass())) {
401                 return false;
402             }
403
404             final TestModule that = (TestModule) o;
405
406             if (this.name != null ? !this.name.equals(that.name) : that.name != null) {
407                 return false;
408             }
409             if (this.namespace != null ? !this.namespace.equals(that.namespace) : that.namespace != null) {
410                 return false;
411             }
412             return this.revision != null ? this.revision.equals(that.revision) : that.revision == null;
413
414         }
415
416         @Override
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);
421             return result;
422         }
423     }
424 }