From 1ab29ec8a91e845a59a2990ca2fe17166111a458 Mon Sep 17 00:00:00 2001 From: Dominic Lunanuova Date: Tue, 13 Feb 2018 22:44:20 +0000 Subject: [PATCH] Automated test coverage code Model tests Service tests Change-Id: If25ffd98f2a7e0b567252669f1fe55f736e26e36 Issue-ID: DMAAP-103 Signed-off-by: Dominic Lunanuova --- .gitignore | 4 + etc/dmaapbc.properties | 201 +++++++++++++++++++++ .../org/onap/dmaap/dbcapi/aaf/AafConnection.java | 8 + .../org/onap/dmaap/dbcapi/model/DRNodeTest.java | 86 +++++++++ .../org/onap/dmaap/dbcapi/model/DRPubTest.java | 90 +++++++++ .../org/onap/dmaap/dbcapi/model/DRSubTest.java | 108 +++++++++++ .../onap/dmaap/dbcapi/model/DcaeLocationTest.java | 94 ++++++++++ .../org/onap/dmaap/dbcapi/model/DmaapTest.java | 106 +++++++++++ .../java/org/onap/dmaap/dbcapi/model/FeedTest.java | 79 ++++++++ .../onap/dmaap/dbcapi/model/JUnitTestSuite.java | 40 ++++ .../org/onap/dmaap/dbcapi/model/MRClientTest.java | 91 ++++++++++ .../onap/dmaap/dbcapi/model/MirrorMakerTest.java | 72 ++++++++ .../onap/dmaap/dbcapi/model/ReflectionHarness.java | 175 ++++++++++++++++++ .../org/onap/dmaap/dbcapi/model/TestRunner.java | 36 ++++ .../org/onap/dmaap/dbcapi/model/TopicTest.java | 79 ++++++++ .../dmaap/dbcapi/service/TopicServiceTest.java | 100 ++++++++++ 16 files changed, 1369 insertions(+) create mode 100644 .gitignore create mode 100644 etc/dmaapbc.properties create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/ReflectionHarness.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java create mode 100644 src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01c0d9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +debug-logs/ +logs/ +target/ + diff --git a/etc/dmaapbc.properties b/etc/dmaapbc.properties new file mode 100644 index 0000000..39918a4 --- /dev/null +++ b/etc/dmaapbc.properties @@ -0,0 +1,201 @@ +# +# Configuration parameters fixed at startup for the DMaaP Bus Controller +# +# +# URI to retrieve dynamic DR configuration +# +ProvisioningURI: /internal/prov +# +# Allow http access to API +# +HttpAllowed: true +# +# The port number for http as seen within the server +# +IntHttpPort: 8080 +# +# The port number for https as seen within the server +# Set to 0 if no certificate is available yet... +# +IntHttpsPort: 0 +# +# The external port number for https taking port mapping into account +# +ExtHttpsPort: 0 +# +# The type of keystore for https +# +KeyStoreType: jks +# +# The path to the keystore for https +# +KeyStoreFile: etc/keystore +# +# The password for the https keystore +# +KeyStorePassword: changeit +# +# The password for the private key in the https keystore +# +KeyPassword: changeit +# +# The type of truststore for https +# +TrustStoreType: jks +# +# The path to the truststore for https +# +TrustStoreFile: ${DMAAPBC_TSTOREFILE} +# +# The password for the https truststore +# +TrustStorePassword: changeit +# +# The path to the file used to trigger an orderly shutdown +# +QuiesceFile: etc/SHUTDOWN +# +# Enable postgress +# +UsePGSQL: false +# +# The host for postgres access +# +DB.host: none +# +# For postgres access +# +DB.cred: none +# +# Name of this environment +# +DmaapName: onap-cit +# +# Name of DR prov server +# +DR.provhost: dmaap-drps +# +# handling of feed delete +# DeleteOnDR - means use the DR API to DELETE a feed. (default for backwards compatibility) +# SimulateDelete - means preserve the feed on DR (after cleaning it up), and mark as DELETED in DBCL. Better for cfy environments +Feed.deleteHandling: DeleteOnDR + +################################################################################ +# MR Related Properties: +# +# Value of the CNAME DNS entry which resolves to the primary central MR cluster (when there are more than one central clusters). +# if there is only one MR cluster in an environment, set this to the DNS name for that cluster +# +MR.CentralCname: notSet.onap.org +# +# MR Client Delete Level thoroughness: +# 0 = don't delete +# 1 = delete from persistent store +# 2 = delete from persistent store (DB) and authorization store (AAF) +MR.ClientDeleteLevel: 1 +# +# MR Topic Factory Namespace +# +MR.TopicFactoryNS: org.onap.dcae.dmaap.topicFactory +# +# MR TopicMgr Role +MR.TopicMgrRole: org.onap.dmaapBC.TopicMgr + +# MR topic name style +MR.topicStyle: FQTN_LEGACY_FORMAT + +# MR topic ProjectID +MR.projectID: 23456 +# +# end of MR Related Properties +################################################################################ + +# +# The Role and credentials of the MirrorMaker Provisioner. This is used by DMaaP Bus Controller to pub to the provisioning topic +# Not part of 1701 +# +MM.ProvRole: org.onap.dmaapBC.MMprov.prov +MM.ProvUserMechId: idNotSet@namespaceNotSet +MM.ProvUserPwd: pwdNotSet +# +# The Role of the MirrorMaker Agent. This is used by MM to sub to provisioning topic +# +MM.AgentRole: org.onap.dmaapBC.MMagent.agent +################# +# AAF Properties: +# +# regarding password encryption: +# In the dependencies that Maven retrieves (e.g., under dcae_dmaapbc/target/deps/ is a jar file cadi-core-version.jar. Generate the key file with: +# +# java \u2013jar wherever/cadi-core-*.jar keygen keyfilename +# chmod 400 keyfilename +# +# To encrypt a key: +# +# java \u2013jar wherever/cadi-core-*.jar digest password-to-encrypt keyfilename +# +# This will generate a string. Put \u201Cenc:\u201D on the front of the string, and put the result in this properties file. +# +# Location of the Codec Keyfile which is used to decrypt passwords in this properties file before they are passed to AAF +# +# REF: https://wiki.domain.notset.com/display/cadi/CADI+Deployment +# +CredentialCodecKeyfile: etc/LocalKey +# +# This overrides the Class used for Decryption. +# This allows for a plugin encryption/decryption method if needed. +# Call this Class for decryption at runtime. +#AafDecryption.Class: com.company.proprietaryDecryptor + +# +# This overrides the Class used for API Permission check. +# This allows for a plugin policy check, if needed +#ApiPermission.Class: com.company.policy.DecisionPolicy + +# +# URL of AAF environment to use. +# +aaf.URL: https://authentication.simpledemo.onap.org:8095/proxy/ +# +# TopicMgr mechid@namespace +# +aaf.TopicMgrUser: idNotSet@namespaceNotSet +# +# TopicMgr password +# +aaf.TopicMgrPassword: pwdNotSet +# +# Bus Controller Namespace Admin mechid@namespace +# +aaf.AdminUser: idNotSet@namespaceNotSet +# +# Bus Controller Namespace Admin password +# +aaf.AdminPassword: pwdNotSet +# +# endof AAF Properties +################# +################# +# PolicyEngine Properties +# +# Flag to turn on/off Authentication +UsePE: false +# +# Argument to decisionAttributes.put("AAF_ENVIRONMENT", X); +# where X is: TEST= UAT, PROD = PROD, DEVL = TEST +# +PeAafEnvironment: DEVL +PeAafUrl.DEVL: https://aafdev.onap.org:8095/proxy/ +PeAafUrl.TEST: https://aafist..onap.org:8095/proxy/ +PeAafUrl.PROD: https://aafprod.onap.org:8095/proxy/ + +# +# Name of PolicyEngineApi properties file +PolicyEngineProperties: config/PolicyEngineApi.properties +# +# Namespace for URI values for API used to create AAF permissions +# e.g. if ApiNamespace is X.Y..dmaapBC.api then for URI /topics we create an AAF perm X.Y..dmaapBC.api.topics +ApiNamespace: org.onap.dmaapBC.api +# +# endof PolicyEngineProperties +################# diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java index b7c02dd..2e91ca5 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java @@ -67,6 +67,10 @@ public class AafConnection extends BaseLoggingClass { uc.setInstanceFollowRedirects(false); logger.info( "successful connect to " + pURL ); return(true); + } catch ( UnknownHostException uhe ) { + errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION, pURL, uhe.getMessage() ); + uhe.printStackTrace(); + return(false); } catch (Exception e) { errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_ERROR, pURL, e.getMessage() ); e.printStackTrace(); @@ -136,6 +140,10 @@ public class AafConnection extends BaseLoggingClass { } } catch ( SSLHandshakeException she ) { errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL); + } catch ( UnknownHostException uhe ) { + errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION, pURL, uhe.getMessage() ); + rc = 500; + return rc; } try { rc = uc.getResponseCode(); diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java new file mode 100644 index 0000000..ce23656 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java @@ -0,0 +1,86 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +public class DRNodeTest { + String f, d, h, v; + + @Before + public void setUp() throws Exception { + v = "1"; + f = "node01.onap.org"; + h = "zlpdrns01.cloud.onap.org"; + d = "central-onap"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testDRNodeClassDefaultConstructor() { + + DR_Node t = new DR_Node(); + + assertTrue( t.getFqdn() == null ); + assertTrue( t.getDcaeLocationName() == null ); + assertTrue( t.getHostName() == null ); + assertTrue( t.getVersion() == null ); + + } + + @Test + public void testDRNodeClassConstructor() { + + DR_Node t = new DR_Node( f, d, h, v ); + + assertTrue( f.equals( t.getFqdn() )); + assertTrue( d.equals( t.getDcaeLocationName() )); + assertTrue( h.equals( t.getHostName() )); + assertTrue( v.equals( t.getVersion() )); + + } + + @Test + public void testDRNodeClassSetters() { + + DR_Node t = new DR_Node(); + + t.setFqdn( f ); + assertTrue( f.equals( t.getFqdn() )); + t.setDcaeLocationName( d ); + assertTrue( d.equals( t.getDcaeLocationName() )); + t.setHostName( h ); + assertTrue( h.equals( t.getHostName() )); + t.setVersion( v ); + assertTrue( v.equals( t.getVersion() )); + + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java new file mode 100644 index 0000000..191f364 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java @@ -0,0 +1,90 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +public class DRPubTest { + String d, un, up, f, p; + + @Before + public void setUp() throws Exception { + d = "central-onap"; + un = "user1"; + up = "secretW0rd"; + f = "234"; + p = "678"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testDRPubClassDefaultConstructor() { + + DR_Pub t = new DR_Pub(); + + assertTrue( t.getDcaeLocationName() == null ); + assertTrue( t.getUsername() == null ); + assertTrue( t.getUserpwd() == null ); + assertTrue( t.getFeedId() == null ); + assertTrue( t.getPubId() == null ); + + } + + @Test + public void testDRPubClassConstructor() { + + DR_Pub t = new DR_Pub( d, un, up, f, p ); + + assertTrue( d.equals( t.getDcaeLocationName() )); + assertTrue( un.equals( t.getUsername() )); + assertTrue( up.equals( t.getUserpwd() )); + assertTrue( f.equals( t.getFeedId() )); + assertTrue( p.equals( t.getPubId() )); + } + + @Test + public void testDRPubClassSetters() { + + DR_Pub t = new DR_Pub(); + + t.setDcaeLocationName( d ); + assertTrue( d.equals( t.getDcaeLocationName() )); + t.setUsername( un ); + assertTrue( un.equals( t.getUsername() )); + t.setUserpwd( up ); + assertTrue( up.equals( t.getUserpwd() )); + t.setFeedId( f ); + assertTrue( f.equals( t.getFeedId() )); + t.setPubId( p ); + assertTrue( p.equals( t.getPubId() )); + + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java new file mode 100644 index 0000000..251d975 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java @@ -0,0 +1,108 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +public class DRSubTest { + String d, un, up, f, du, lu, s, o; + Boolean u100, susp; + + @Before + public void setUp() throws Exception { + d = "central-onap"; + un = "user1"; + up = "secretW0rd"; + f = "234"; + s = "sub123"; + du = "sub.server.onap.org:8443/deliver/here"; + lu = "https://drps.onap.org:8443/sublog/123"; + u100 = true; + susp = false; + o = "joe"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testDRSubClassDefaultConstructor() { + + DR_Sub t = new DR_Sub(); + + assertTrue( t.getDcaeLocationName() == null ); + assertTrue( t.getUsername() == null ); + assertTrue( t.getUserpwd() == null ); + assertTrue( t.getFeedId() == null ); + assertTrue( t.getDeliveryURL() == null ); + assertTrue( t.getLogURL() == null ); + assertTrue( t.getSubId() == null ); + assertTrue( ! t.isUse100() ); + assertTrue( ! t.isSuspended() ); + assertTrue( t.getOwner() == null ); + + } + + @Test + public void testDRSubClassConstructor() { + + DR_Sub t = new DR_Sub( d, un, up, f, du, lu, u100 ); + + assertTrue( d.equals( t.getDcaeLocationName() )); + assertTrue( un.equals( t.getUsername() )); + assertTrue( up.equals( t.getUserpwd() )); + assertTrue( f.equals( t.getFeedId() )); + assertTrue( du.equals( t.getDeliveryURL() ) ); + assertTrue( lu.equals( t.getLogURL() ) ); + assertTrue( t.isUse100() ); + assertTrue( ! t.isSuspended() ); + } + + @Test + public void testDRSubClassSetters() { + + DR_Sub t = new DR_Sub(); + + t.setDcaeLocationName( d ); + assertTrue( d.equals( t.getDcaeLocationName() )); + t.setUsername( un ); + assertTrue( un.equals( t.getUsername() )); + t.setUserpwd( up ); + assertTrue( up.equals( t.getUserpwd() )); + t.setFeedId( f ); + assertTrue( f.equals( t.getFeedId() )); + t.setSubId( s ); + assertTrue( s.equals( t.getSubId() )); + t.setDeliveryURL( du ); + assertTrue( du.equals( t.getDeliveryURL() ) ); + t.setLogURL( lu ); + assertTrue( lu.equals( t.getLogURL() ) ); + + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java new file mode 100644 index 0000000..6652bb0 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +public class DcaeLocationTest { + String c, dl, dln, osz, s, edge; + + @Before + public void setUp() throws Exception { + c = "ABCDE888NJ"; + dl = "central-node"; + edge = "local-node"; + dln = "hollywood"; + osz = "california"; + s = "10.10.10.1"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testDcaeLocationDefaultConstructor() { + + DcaeLocation t = new DcaeLocation(); + + assertTrue( t.getClli() == null ); + assertTrue( t.getDcaeLayer() == null ); + assertTrue( t.getDcaeLocationName() == null ); + assertTrue( t.getOpenStackAvailabilityZone() == null ); + assertTrue( t.getSubnet() == null ); + + } + + @Test + public void testDcaeLocationClassConstructor() { + + DcaeLocation t = new DcaeLocation( c, dl, dln, osz, s ); + + assertTrue( c.equals( t.getClli() )); + assertTrue( dl.equals( t.getDcaeLayer() )); + assertTrue( dln.equals( t.getDcaeLocationName() )); + assertTrue( osz.equals( t.getOpenStackAvailabilityZone() )); + assertTrue( s.equals( t.getSubnet() )); + } + + @Test + public void testDmaapClassSetters() { + + DcaeLocation t = new DcaeLocation(); + + t.setClli( c ); + assertTrue( c.equals( t.getClli() )); + t.setDcaeLayer( dl ); + assertTrue( dl.equals( t.getDcaeLayer() )); + assertTrue( t.isCentral() ); + t.setDcaeLayer( edge ); + assertTrue( edge.equals( t.getDcaeLayer() )); + assertTrue( t.isLocal() ); + t.setDcaeLocationName( dln ); + assertTrue( dln.equals( t.getDcaeLocationName() )); + t.setOpenStackAvailabilityZone( osz ); + assertTrue( osz.equals( t.getOpenStackAvailabilityZone() )); + t.setSubnet( s ); + assertTrue( s.equals( t.getSubnet() )); + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java new file mode 100644 index 0000000..fb83fe9 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java @@ -0,0 +1,106 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +public class DmaapTest { + String ver, tnr, dn, dpu, lu, bat, nk, ako; + + @Before + public void setUp() throws Exception { + ver = "1"; + tnr = "org.onap.dmaap"; + dn = "onap"; + dpu = "https://drps.dmaap.onap.org:8081"; + lu = "http://drps.dmaap.onap.org:8080/feedlog"; + bat = "org.onap.dcae.dmaap.MM_AGENT_TOPIC"; + nk = "foo"; + ako = "bar"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testDmaapClassDefaultConstructor() { + + Dmaap t = new Dmaap(); + + assertTrue( t.getVersion() == null ); + assertTrue( t.getTopicNsRoot() == null ); + assertTrue( t.getDmaapName() == null ); + assertTrue( t.getDrProvUrl() == null ); + assertTrue( t.getLoggingUrl() == null ); + assertTrue( t.getBridgeAdminTopic() == null ); + assertTrue( t.getNodeKey() == null ); + assertTrue( t.getAccessKeyOwner() == null ); + + } + + @Test + public void testDmaapClassConstructor() { + + Dmaap t = new Dmaap( ver, tnr, dn, dpu, lu, bat, nk, ako ); + + assertTrue( ver.equals( t.getVersion() )); + assertTrue( tnr.equals( t.getTopicNsRoot() )); + assertTrue( dn.equals( t.getDmaapName() )); + assertTrue( dpu.equals( t.getDrProvUrl() )); + assertTrue( lu.equals( t.getLoggingUrl() )); + assertTrue( bat.equals( t.getBridgeAdminTopic() )); + assertTrue( nk.equals( t.getNodeKey() )); + assertTrue( ako.equals( t.getAccessKeyOwner() )); + + } + + @Test + public void testDmaapClassSetters() { + + Dmaap t = new Dmaap(); + + t.setVersion( ver ); + assertTrue( ver.equals( t.getVersion() )); + t.setTopicNsRoot( tnr ); + assertTrue( tnr.equals( t.getTopicNsRoot() )); + t.setDmaapName( dn ); + assertTrue( dn.equals( t.getDmaapName() )); + t.setDrProvUrl( dpu ); + assertTrue( dpu.equals( t.getDrProvUrl() )); + t.setLoggingUrl( lu ); + assertTrue( lu.equals( t.getLoggingUrl() )); + t.setBridgeAdminTopic( bat ); + assertTrue( bat.equals( t.getBridgeAdminTopic() )); + t.setNodeKey( nk ); + assertTrue( nk.equals( t.getNodeKey() )); + t.setAccessKeyOwner( ako ); + assertTrue( ako.equals( t.getAccessKeyOwner() )); + + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java new file mode 100644 index 0000000..cc8b259 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java @@ -0,0 +1,79 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +public class FeedTest { + + private static final String fmt = "%24s: %s%n"; + + ReflectionHarness rh = new ReflectionHarness(); + + String n, v, d, o, a; + + @Before + public void setUp() throws Exception { + n = "Chicken Feed"; + v = "1.0"; + d = "A daily helping of chicken eating metrics"; + o = "ab123"; + a = "Unrestricted"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void test1() { + + + rh.reflect( "org.onap.dmaap.dbcapi.model.Feed", "get", null ); + + } + + @Test + public void test2() { + Feed t = new Feed( n, v, d, o, a ); + + + assertTrue( n.equals( t.getFeedName() )); + assertTrue( v.equals( t.getFeedVersion() )); + assertTrue( d.equals( t.getFeedDescription() )); + assertTrue( o.equals( t.getOwner() )); + assertTrue( a.equals( t.getAsprClassification() ) ); + assertTrue( ! t.isSuspended() ); + } + + @Test + public void test3() { + + String v = "Validate"; + rh.reflect( "org.onap.dmaap.dbcapi.model.Feed", "set", v ); + } + +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java b/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java new file mode 100644 index 0000000..1c8edc0 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dmaap.dbcapi.model; + +import junit.framework.TestSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.apache.log4j.Logger; + +@RunWith(Suite.class) +@SuiteClasses({ DmaapTest.class + }) +public class JUnitTestSuite { + private static final Logger LOGGER = Logger.getLogger(JUnitTestSuite.class); + public static void main(String[] args) { + LOGGER.info("Running the test suite"); + + TestSuite tstSuite = new TestSuite(); + LOGGER.info("Total Test Counts " + tstSuite.countTestCases()); + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java new file mode 100644 index 0000000..dd57348 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java @@ -0,0 +1,91 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +public class MRClientTest { + + private static final String fmt = "%24s: %s%n"; + + ReflectionHarness rh = new ReflectionHarness(); + + String d, t, f, c, m; + + @Before + public void setUp() throws Exception { + d = "central-onap"; + t = "org.onap.dmaap.interestingTopic"; + f = "mrc.onap.org:3904/events/org.onap.dmaap.interestingTopic"; + c = "publisher"; + m = "m12345"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void test1() { + + // can't use simple reflection to test for null since null constructor initializes some fields. + //rh.reflect( "org.onap.dmaap.dbcapi.model.MR_Client", "get", null ); + // so brute force instead... + String[] a = { "put", "view" }; + MR_Client m = new MR_Client(); + + + assertTrue( null == m.getDcaeLocationName() ); + assertTrue( null == m.getFqtn() ); + assertTrue( null == m.getClientRole() ); + assertTrue( null == m.getAction() ); + + } + + @Test + public void test2() { + String[] a = { "put", "view" }; + MR_Client m = new MR_Client( d, f, c, a ); + + + assertTrue( d.equals( m.getDcaeLocationName() )); + assertTrue( f.equals( m.getFqtn() )); + assertTrue( c.equals( m.getClientRole() )); + String[] ma = m.getAction(); + assertTrue( a.length == ma.length ); + for( int i = 0; i < a.length; i++ ) { + assertTrue( a[i].equals( ma[i] ) ); + } + } + + @Test + public void test3() { + + String v = "Validate"; + rh.reflect( "org.onap.dmaap.dbcapi.model.MR_Client", "set", v ); + } + +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java new file mode 100644 index 0000000..1949820 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +public class MirrorMakerTest { + + private static final String fmt = "%24s: %s%n"; + + ReflectionHarness rh = new ReflectionHarness(); + + String s, t; + + @Before + public void setUp() throws Exception { + s = "source"; + t = "target"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void test1() { + + + rh.reflect( "org.onap.dmaap.dbcapi.model.MirrorMaker", "get", null ); + + } + + @Test + public void test2() { + MirrorMaker m = new MirrorMaker( s, t ); + + + assertTrue( s.equals( m.getSourceCluster() )); + assertTrue( t.equals( m.getTargetCluster() )); + } + + @Test + public void test3() { + + String v = "Validate"; + rh.reflect( "org.onap.dmaap.dbcapi.model.MirrorMaker", "set", v ); + } + +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/ReflectionHarness.java b/src/test/java/org/onap/dmaap/dbcapi/model/ReflectionHarness.java new file mode 100644 index 0000000..76167ce --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/ReflectionHarness.java @@ -0,0 +1,175 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static java.lang.System.out; +import static java.lang.System.err; + +public class ReflectionHarness { + private static final String fmt = "%24s: %s%n"; + + + // following 2 functions taken from: http://tutorials.jenkov.com/java-reflection/getters-setters.html + public static boolean isGetter(Method method){ + if(!method.getName().startsWith("get")) return false; + if(method.getParameterTypes().length != 0) return false; + if(void.class.equals(method.getReturnType())) return false; + return true; + } + + public static boolean isSetter(Method method){ + if(!method.getName().startsWith("set")) return false; + if(method.getParameterTypes().length != 1) return false; + return true; + } + + private void testGetter( Class c, Method m, Class[] pType, String val ) { + //out.format( fmt, "testGetter: Method Name", m.getName() ); + Class retType = m.getReturnType(); + //out.format( fmt, "testGetter: Return Type ", retType ); + //out.format( fmt, "testGetter: val ", (val != null)?val:"null" ); + assertTrue( pType.length == 0 ); + + try { + Object t = c.newInstance(); + + try { + m.setAccessible(true); + Object o = m.invoke( t ); + + if( retType.equals( Class.forName( "java.lang.String" ) ) ) { + if ( val == null ) { + //out.format( fmt, "testGetter: expected null, got ", (o != null)?o:"null" ); + assert( o == null ); + } else { + //out.format( fmt, "testGetter: expected val, got ", (o != null)?o:"null" ); + assert( o.equals( val ) ); + } + } else { + out.format( fmt, "testGetter: " + m.getName() + " untested retType", retType ); + + } + + } catch (InvocationTargetException e ) { + Throwable cause = e.getCause(); + err.format( "%s() returned %x%n", m.getName(), cause.getMessage() ); + } + + } catch (ClassNotFoundException nfe ){ + nfe.printStackTrace(); + } catch (IllegalArgumentException ae ) { + ae.printStackTrace(); + } catch (InstantiationException ie ) { + ie.printStackTrace(); + } catch (IllegalAccessException iae ) { + iae.printStackTrace(); + } + } + + private void testSetter( Class c, Method m, Class[] pType ) { + //out.format( fmt, "testSetter: Method Name", m.getName() ); + Class retType = m.getReturnType(); + //out.format( fmt, "testSetter: Return Type ", retType ); + //out.format( fmt, "testSetter: val ", (val != null)?val:"null" ); + assertTrue( pType.length == 1 ); + + try { + Object t = c.newInstance(); + + try { + m.setAccessible(true); + //out.format( fmt, "testSetter: " + m.getName() + " to try pType", pType[0] ); + if ( pType[0].equals( Class.forName( "java.lang.String" ) ) ) { + String val = "Validate123"; + Object o = m.invoke( t, val ); + } else if ( pType[0].equals( boolean.class ) ) { // note primitive class notation + boolean b = true; + Object o = m.invoke( t, b ); + } else { + out.format( fmt, "testSetter: " + m.getName() + " untested pType", pType[0] ); + } + + } catch (InvocationTargetException e ) { + Throwable cause = e.getCause(); + err.format( "%s() returned %x%n", m.getName(), cause.getMessage() ); + } + + } catch (ClassNotFoundException nfe ){ + nfe.printStackTrace(); + } catch (IllegalArgumentException ae ) { + ae.printStackTrace(); + } catch (InstantiationException ie ) { + ie.printStackTrace(); + } catch (IllegalAccessException iae ) { + iae.printStackTrace(); + } + } + + public void reflect(String... args) { + try { + Class c = Class.forName(args[0]); + Method[] allMethods = c.getDeclaredMethods(); + String methodPrefix = args[1]; + for (Method m : allMethods) { + if (!m.getName().startsWith(methodPrefix)) { + continue; + } + //out.format("%s%n", m.toGenericString()); + + //out.format(fmt, "ReturnType", m.getReturnType()); + //out.format(fmt, "GenericReturnType", m.getGenericReturnType()); + + Class[] pType = m.getParameterTypes(); + Type[] gpType = m.getGenericParameterTypes(); + for (int i = 0; i < pType.length; i++) { + //out.format(fmt,"ParameterType", pType[i]); + //out.format(fmt,"GenericParameterType", gpType[i]); + } + if ( isGetter( m ) ) { + testGetter( c, m, pType , args[2]); + } else if ( isSetter( m ) ) { + testSetter( c, m, pType ); + } + + Class[] xType = m.getExceptionTypes(); + Type[] gxType = m.getGenericExceptionTypes(); + for (int i = 0; i < xType.length; i++) { + //out.format(fmt,"ExceptionType", xType[i]); + //out.format(fmt,"GenericExceptionType", gxType[i]); + } + } + + // production code should handle these exceptions more gracefully + } catch (ClassNotFoundException x) { + x.printStackTrace(); + } + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java b/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java new file mode 100644 index 0000000..fcab3e1 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.apache.log4j.Logger; +public class TestRunner { + private static final Logger LOGGER = Logger.getLogger(TestRunner.class); + public static void main(String[] args) { + + Result result = JUnitCore.runClasses(JUnitTestSuite.class); + for (Failure failure : result.getFailures()) { + LOGGER.info(failure.toString()); + + } + LOGGER.info(result.wasSuccessful()); + } +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java b/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java new file mode 100644 index 0000000..1f8b3b2 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java @@ -0,0 +1,79 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.model; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +public class TopicTest { + + private static final String fmt = "%24s: %s%n"; + + ReflectionHarness rh = new ReflectionHarness(); + + String f, t, d, e, o; + + @Before + public void setUp() throws Exception { + f = "org.onap.dmaap.interestingTopic"; + t = "interestingTopic"; + d = "A so very interesting topic"; + e = "Yes"; + o = "m12345"; + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void test1() { + + + rh.reflect( "org.onap.dmaap.dbcapi.model.Topic", "get", null ); + + } + + @Test + public void test2() { + String[] a = { "put", "view" }; + Topic obj = new Topic( f, t, d, e, o ); + + + assertTrue( f.equals( obj.getFqtn() )); + assertTrue( t.equals( obj.getTopicName() )); + assertTrue( d.equals( obj.getTopicDescription() )); + assertTrue( e.equals( obj.getTnxEnabled() )); + assertTrue( o.equals( obj.getOwner() )); + } + + @Test + public void test3() { + + String v = "Validate"; + rh.reflect( "org.onap.dmaap.dbcapi.model.Topic", "set", v ); + } + +} diff --git a/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java b/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java new file mode 100644 index 0000000..0d39350 --- /dev/null +++ b/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java @@ -0,0 +1,100 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dmaap.dbcapi.service; +import org.onap.dmaap.dbcapi.model.*; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import java.util.List; + +public class TopicServiceTest { + + private static final String fmt = "%24s: %s%n"; + + ReflectionHarness rh = new ReflectionHarness(); + + TopicService ts; + + @Before + public void setUp() throws Exception { + ts = new TopicService(); + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void test1() { + + + rh.reflect( "org.onap.dmaap.dbcapi.service.TopicService", "get", null ); + + } + + @Test + public void test2() { + String v = "Validate"; + rh.reflect( "org.onap.dmaap.dbcapi.service.TopicService", "set", v ); + + } + + @Test + public void test3() { + Topic topic = new Topic(); + ApiError err = new ApiError(); + topic.setTopicName( "test3" ); + topic.setFqtnStyle( FqtnType.Validator("none") ); + topic.getFqtn(); + Topic nTopic = ts.addTopic( topic, err ); + if ( nTopic != null ) { + assertTrue( nTopic.getTopicName().equals( topic.getTopicName() )); + } + + } + + @Test + public void test4() { + List l = ts.getAllTopics(); + + } + +/* + @Test + public void test5() { + Topic topic = new Topic(); + ApiError err = new ApiError(); + topic.setTopicName( "test5" ); + topic.setFqtnStyle( FqtnType.Validator("none") ); + topic.setReplicationCase( ReplicationType.Validator("none") ); + topic.getFqtn(); + Topic nTopic = ts.updateTopic( topic, err ); + if ( nTopic != null ) { + assertTrue( nTopic.getTopicName().equals( topic.getTopicName() )); + } + + } +*/ + +} -- 2.16.6