/* * Copyright (c) 2022 PANTHEON.tech s.r.o. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.generator.impl; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.in; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.junit.Test; import org.opendaylight.mdsal.binding.model.api.GeneratedType; import org.opendaylight.mdsal.binding.model.api.MethodSignature; import org.opendaylight.mdsal.binding.model.api.Type; import org.opendaylight.mdsal.binding.model.ri.BaseYangTypes; import org.opendaylight.mdsal.binding.model.ri.BindingTypes; import org.opendaylight.mdsal.binding.model.ri.Types; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; public class Mdsal675Test { private static final String PACKAGE = "org.opendaylight.yang.gen.v1.urn.test.yang.data.demo.norev."; private static final String PACKAGE2 = "org.opendaylight.yang.gen.v1.urn.test.yang.data.naming.norev."; private static final String MODULE_CLASS_NAME = PACKAGE + "YangDataDemoData"; private static final String ROOT_CONTAINER_CLASS_NAME = PACKAGE + "RootContainer"; private static final Map INTERFACE_METHODS = Map.of("implementedInterface", Types.typeForClass(Class.class)); @Test public void yangDataGen() { final List allGenTypes = DefaultBindingGenerator.generateFor( YangParserTestUtils.parseYangResources(Mdsal675Test.class, "/yang-data-models/ietf-restconf.yang", "/yang-data-models/yang-data-demo.yang")); assertNotNull(allGenTypes); assertEquals(29, allGenTypes.size()); final Map genTypesMap = allGenTypes.stream() .collect(ImmutableMap.toImmutableMap(type -> type.getIdentifier().toString(), type -> type)); // ensure generated yang-data classes contain getters for inner structure types // yang-data > container assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithContainer"), assertGenType(genTypesMap, PACKAGE + "yang.data.with.container.ContainerFromYangData"), List.of("getContainerFromYangData", "nonnullContainerFromYangData")); // yang-data > list assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithList"), Types.listTypeFor(assertGenType(genTypesMap, PACKAGE + "yang.data.with.list.ListFromYangData")), List.of("getListFromYangData", "nonnullListFromYangData")); // yang-data > leaf assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithLeaf"), BaseYangTypes.STRING_TYPE, List.of("getLeafFromYangData", "requireLeafFromYangData")); // yang-data > leaf-list assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafList"), Types.setTypeFor(BaseYangTypes.STRING_TYPE), List.of("getLeafListFromYangData", "requireLeafListFromYangData")); // yang-data > anydata assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithAnydata"), assertGenType(genTypesMap, PACKAGE + "yang.data.with.anydata.AnydataFromYangData"), List.of("getAnydataFromYangData", "requireAnydataFromYangData")); // yang-data > anyxml assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithAnyxml"), assertGenType(genTypesMap, PACKAGE + "yang.data.with.anyxml.AnyxmlFromYangData"), List.of("getAnyxmlFromYangData", "requireAnyxmlFromYangData")); // ensure generated yang-data classes extending inner group so group content is reachable // yang-data > uses > group > container assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithContainerFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForContainer"), assertGenType(genTypesMap, PACKAGE + "grp._for.container.ContainerFromGroup"), List.of("getContainerFromGroup", "nonnullContainerFromGroup")); // yang-data > uses > group > list assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithListFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForList"), Types.listTypeFor(assertGenType(genTypesMap, PACKAGE + "grp._for.list.ListFromGroup")), List.of("getListFromGroup", "nonnullListFromGroup") ); // yang-data > uses > group > leaf assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForLeaf"), BaseYangTypes.UINT32_TYPE, List.of("getLeafFromGroup", "requireLeafFromGroup")); // yang-data > uses > group > leaf-list assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithLeafListFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForLeafList"), Types.setTypeFor(BaseYangTypes.UINT32_TYPE), List.of("getLeafListFromGroup", "requireLeafListFromGroup")); // yang-data > uses > group > anydata assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithAnydataFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForAnydata"), assertGenType(genTypesMap, PACKAGE + "grp._for.anydata.AnydataFromGroup"), List.of("getAnydataFromGroup", "requireAnydataFromGroup")); // yang-data > uses > group > anyxml assertYangDataGenType( assertGenType(genTypesMap, PACKAGE + "YangDataWithAnyxmlFromGroup"), assertGenType(genTypesMap, PACKAGE + "GrpForAnyxml"), assertGenType(genTypesMap, PACKAGE + "grp._for.anyxml.AnyxmlFromGroup"), List.of("getAnyxmlFromGroup", "requireAnyxmlFromGroup")); // ensure module class has only getter for root container final GeneratedType moduleType = assertGenType(genTypesMap, MODULE_CLASS_NAME); assertNotNull(moduleType.getMethodDefinitions()); assertEquals(List.of("getRootContainer"), moduleType.getMethodDefinitions().stream().map(MethodSignature::getName) .filter(methodName -> methodName.startsWith("get")).toList()); // ensure yang-data at non-top level is ignored (no getters in parent container) GeneratedType rootContainerType = assertGenType(genTypesMap, ROOT_CONTAINER_CLASS_NAME); assertNotNull(rootContainerType.getMethodDefinitions()); assertTrue(rootContainerType.getMethodDefinitions().stream() .filter(method -> method.getName().startsWith("get")) .findFirst().isEmpty()); } @Test public void yangDataNamingStrategy() { final List allGenTypes = DefaultBindingGenerator.generateFor( YangParserTestUtils.parseYangResources(Mdsal675Test.class, "/yang-data-models/ietf-restconf.yang", "/yang-data-models/yang-data-naming.yang")); assertNotNull(allGenTypes); assertEquals(22, allGenTypes.size()); final Set genTypeNames = allGenTypes.stream().map(type -> type.getIdentifier().toString()).collect(Collectors.toSet()); // template name is not compliant to YANG identifier -> char encoding used, name starts with $ char // a) latin1, but not a valid Java identifier assertTrue(genTypeNames.contains(PACKAGE2 + "$ľaľa$20$ho$2C$$20$papľuha$2C$$20$ogrcal$20$mi$20$krpce$21$")); // b) cyrillic, but a valid Java identifier assertTrue(genTypeNames.contains(PACKAGE2 + "привет")); // template name is compliant to yang identifier -> camel-case used assertTrue(genTypeNames.contains(PACKAGE2 + "IdentifierCompliantName")); // name collision with a typedef assertTrue(genTypeNames.contains(PACKAGE2 + "Collision1$T")); assertTrue(genTypeNames.contains(PACKAGE2 + "collision1.Collision1")); assertTrue(genTypeNames.contains(PACKAGE2 + "Collision1$YD")); // name collision with top level container assertTrue(genTypeNames.contains(PACKAGE2 + "Collision2")); assertTrue(genTypeNames.contains(PACKAGE2 + "Collision2$YD")); // name collision with group used assertTrue(genTypeNames.contains(PACKAGE2 + "Collision3$G")); assertTrue(genTypeNames.contains(PACKAGE2 + "Collision3$YD")); // rc:yang-data .-/# assertTrue(genTypeNames.contains(PACKAGE2 + "$$2E$$2D$$2F$$23$")); assertTrue(genTypeNames.contains(PACKAGE2 + "$$2e$$2d$$2f$$23$$.Foo")); // rc:yang-data -./# assertTrue(genTypeNames.contains(PACKAGE2 + "$$2D$$2E$$2F$$23$")); assertTrue(genTypeNames.contains(PACKAGE2 + "$$2d$$2e$$2f$$23$$.Foo")); } private static GeneratedType assertGenType(final Map genTypesMap, final String className) { final var ret = genTypesMap.get(className); assertNotNull("no type generated: " + className, ret); return ret; } private static void assertYangDataGenType(final GeneratedType yangDataType, final Type contentType, final List getterMethods) { assertImplements(yangDataType, BindingTypes.yangData(yangDataType)); INTERFACE_METHODS.forEach((name, type) -> assertHasMethod(yangDataType, name, type)); for (final String methodName : getterMethods) { assertHasMethod(yangDataType, methodName, contentType); } } private static void assertYangDataGenType(final GeneratedType yangDataType, final GeneratedType groupType, final Type contentType, final List getterMethods) { assertImplements(yangDataType, BindingTypes.yangData(yangDataType)); assertImplements(yangDataType, groupType); INTERFACE_METHODS.forEach((name, type) -> assertHasMethod(yangDataType, name, type)); for (final String methodName : getterMethods) { assertHasMethod(groupType, methodName, contentType); } } private static void assertHasMethod(final GeneratedType genType, final String methodName, final Type returnType) { assertTrue("no expected method " + methodName + " returning " + returnType, genType.getMethodDefinitions().stream().anyMatch( method -> methodName.equals(method.getName()) && returnType.equals(method.getReturnType()))); } private static void assertImplements(final GeneratedType genType, final Type implementedType) { assertThat(implementedType, in(genType.getImplements())); } }