Sling 9915 support for sling annotations test runner (#5)
* SLING-9915 Remove deprecated flags
* SLING-9915 Refactored ServiceGetter.java and made public such that it can be used by AnnotationsProcessor.java
* SLING-9915 Added filter() method to TestReference interface
* Added tests for TestReference(String filter)
* Update AnnotationsProcessor::processTestReference to get the filter passed to the TestReference annotation, and call getService(Class<?> c, String filter) which now uses ServiceGetter
* update package version to 1.2.0 as per bnd-baseline-maven-plugin recommendation
* Moved ServiceGetter.java to org.apache.sling.junit.impl package
* change TestReference second parameter to be called target (not filter)
* When using ServiceGetters, make sure to execute close method after the tests
* Refactored AnnotationsProcessor::getService since ServiceGetter handles edge-cases
* fixes for sonarcloud code smells
* WIP: Started integration tests for @TestReference. Not working.
* WIP: Fix integration tests
* WIP Continued PAX-style test for TestReference annotation
* WIP continued - fix integration tests setup
* Updated README with more detailed instructions for debugging
* Unget services provided by @TestReference for every test executed by a SlingAnnotationsTestRunner instance
* update per review notes 12/2/2020
* updated parent to sling-bundle-parent v40
* removed unused import
Co-authored-by: Bertrand Delacretaz <bdelacretaz@apache.org>
Merging to master based on the PR approval from jsedding and input on the dev email 12/5/2020.
diff --git a/.gitignore b/.gitignore
index 24e5066..947429b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-/target
+target
.idea
.classpath
.metadata
diff --git a/bnd.bnd b/bnd.bnd
index aea6f89..454a071 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -3,6 +3,7 @@
junit.*;version=${junit.version}, \
org.junit.*;version=${junit.version}, \
org.hamcrest.*;version=${hamcrest.version};-split-package:=merge-first
+Sling-Test-Regexp: .*JTest
Import-Package: org.junit.platform.*;resolution:=optional, \
*
-includeresource: @org.jacoco.agent-*.jar!/org/jacoco/agent/rt/IAgent*
diff --git a/pom.xml b/pom.xml
index 0ea7095..952f3f7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,7 +56,67 @@
<excludePackageNames>org.apache.sling.junit.impl;org.apache.sling.junit.impl.*</excludePackageNames>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-invoker-plugin</artifactId>
+ <version>3.2.1</version>
+ <configuration>
+ <debug>false</debug>
+ <projectsDirectory>src/it</projectsDirectory>
+ <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+ <pomIncludes>
+ <pomInclude>*/pom.xml</pomInclude>
+ </pomIncludes>
+ <postBuildHookScript>verify</postBuildHookScript>
+ <localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
+ <settingsFile>src/it/settings.xml</settingsFile>
+ <!-- this causes verbose output, probably good to have for CI builds? -->
+ <streamLogs>true</streamLogs>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy</artifactId>
+ <version>3.0.6</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <goals>
+ <goal>install</goal>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/it/annotations-it/target/**</exclude>
+ <exclude>src/it/annotations-it/README.md</exclude>
+ <exclude>src/it/annotations-it/bnd.bnd</exclude>
+ <exclude>src/it/annotations-it/build.log</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>src/it/annotations-it/target</directory>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+
</plugins>
+
<pluginManagement>
<plugins>
<plugin>
diff --git a/src/it/annotations-it/README.md b/src/it/annotations-it/README.md
new file mode 100644
index 0000000..a25e22f
--- /dev/null
+++ b/src/it/annotations-it/README.md
@@ -0,0 +1,39 @@
+#Integration tests for the server testing annotation @TestReference
+This bundle provides server-side junit test `TestReferenceJITest.java` which can be run with SlingAnnotationsTestRunner via a `POST` request to `/system/sling/junit/org.apache.sling.junit.tests.TestReferenceJTest.html`
+with a debugging breakpoint set with ReferenceIT.java `waitForSling()` method.
+
+Note: If additional tests of this type are needed, name the test class such that it satisfies the configured
+regular expression found in bnd.bnd `Sling-Test-Regexp: .*JITest`
+
+
+##To run or debug the tests from this folder, use:
+
+### Build
+ mvn clean verify -Dannotations.bundle.version=VVV
+
+Where VVV is the version of the junit-core bundle to test.
+
+### Debug
+
+Two separate remote debugger clients may be needed:
+`ReferenceIT.java` is a junit test class which runs outside of Sling,
+which can be debugged using the following commands...
+
+* `cd sling-org-apache-sling-junit-core/src/it/annotations-it`
+* `mvn clean verify -Dannotations.bundle.version=1.1.1-SNAPSHOT -Dmaven.failsafe.debug -Dbnd.baseline.skip`
+* The test will wait for debugger to connect before continuing.
+* Set a breakpoint in `waitForSling` within ReferenceIT.java
+* Connect a debugger client over 5005 (default) or the selected port [1]
+* Make note of the localhost URL and port (which is dynamically selected) by inspecting the `URI url` variable
+
+With the test paused via this debugger, it is now possible to connect a browser to the running (temporary) Sling test instance,
+or connect another debugger to access classes such as `TestReferenceJITest.java` or other classes, which run within Sling.
+* Configure `annotations-it/pom.xml` with the following or equivalent
+```
+ <pax.vm.options>
+ -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5015
+ </pax.vm.options>
+```
+* Reconnect the `ReferenceIT.java` debugger as described above
+
+[1] https://maven.apache.org/surefire/maven-failsafe-plugin/examples/debugging.html
diff --git a/src/it/annotations-it/bnd.bnd b/src/it/annotations-it/bnd.bnd
new file mode 100644
index 0000000..e9c2749
--- /dev/null
+++ b/src/it/annotations-it/bnd.bnd
@@ -0,0 +1 @@
+Sling-Test-Regexp: .*JITest
\ No newline at end of file
diff --git a/src/it/annotations-it/invoker.properties b/src/it/annotations-it/invoker.properties
new file mode 100644
index 0000000..14f8610
--- /dev/null
+++ b/src/it/annotations-it/invoker.properties
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+invoker.goals = clean verify -Dannotations.bundle.version=${project.version} -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dbnd.baseline.skip
+invoker.debug = false
\ No newline at end of file
diff --git a/src/it/annotations-it/pom.xml b/src/it/annotations-it/pom.xml
new file mode 100644
index 0000000..4e08087
--- /dev/null
+++ b/src/it/annotations-it/pom.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+ license agreements. See the NOTICE file distributed with this work for additional
+ information regarding copyright ownership. The ASF licenses this file to
+ you 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling-bundle-parent</artifactId>
+ <version>40</version>
+ <relativePath/>
+ </parent>
+
+ <artifactId>org.apache.sling.junit.annotations.it</artifactId>
+ <version>1.0.0</version>
+ <name>Apache Sling Junit Server Annotations IT</name>
+ <description>
+ Integration tests for the server testing annotation @TestReference
+ </description>
+
+
+ <properties>
+ <sling.java.version>8</sling.java.version>
+ <org.ops4j.pax.exam.version>4.13.4</org.ops4j.pax.exam.version>
+ <junit.version>4.13</junit.version>
+ <hamcrest.version>1.3</hamcrest.version>
+ <!-- additional options that can be passed to Pax before executing the tests -->
+ <pax.vm.options/>
+<!-- See README for Debugging instructions. -->
+<!-- <pax.vm.options>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5015</pax.vm.options>-->
+ <bundle.filename>${basedir}/target/${project.build.finalName}.jar</bundle.filename>
+ <annotations.bundle.version>MUST_BE_SET_BY_INVOKER</annotations.bundle.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>bnd-maven-plugin</artifactId>
+ <version>5.0.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>3.0.0-M5</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <systemPropertyVariables>
+ <bundle.filename>${bundle.filename}</bundle.filename>
+ <pax.vm.options>${pax.vm.options}</pax.vm.options>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <!-- Needed for mavenBundle..versionAsInProject() -->
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>build.log</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <!-- dependency under test -->
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.junit.core</artifactId>
+ <version>${annotations.bundle.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <version>${hamcrest.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- testing dependencies -->
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.servlets.resolver</artifactId>
+ <version>2.5.2</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.clients</artifactId>
+ <version>2.0.8</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.rules</artifactId>
+ <version>1.0.8</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ <version>6.0.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>6.0.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.paxexam</artifactId>
+ <version>3.1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-cm</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-forked</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-wrap</artifactId>
+ <version>2.3.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/MyServiceIT.java b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/MyServiceIT.java
new file mode 100644
index 0000000..54238a9
--- /dev/null
+++ b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/MyServiceIT.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.sling.junit.it;
+
+public interface MyServiceIT {
+ /** @return Name of the Service which is used to discover the Service by the User **/
+ String getName();
+
+ /** @return Description of the Service **/
+ String getDescription();
+}
diff --git a/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/TestReferenceJITest.java b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/TestReferenceJITest.java
new file mode 100644
index 0000000..694a8ba
--- /dev/null
+++ b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/TestReferenceJITest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.sling.junit.it;
+
+import org.apache.sling.junit.annotations.SlingAnnotationsTestRunner;
+import org.apache.sling.junit.annotations.TestReference;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(SlingAnnotationsTestRunner.class)
+public class TestReferenceJITest {
+ @TestReference(target ="(component.name=org.apache.sling.junit.it.impl.MyCoolServiceForTestingIT)")
+ MyServiceIT myCoolService;
+
+ @TestReference(target ="(component.name=org.apache.sling.junit.it.impl.MyLameServiceForTestingIT)")
+ MyServiceIT myLameService;
+
+ @TestReference(target ="(component.name=org.apache.sling.junit.it.impl.MyNonExistingServiceForTestingIT)")
+ MyServiceIT myNullService;
+
+ @TestReference
+ MyServiceIT myService;
+
+ @TestReference
+ ConfigurationAdmin configAdmin;
+
+ @Test
+ public void testOsgiReferences() {
+ assertNotNull(configAdmin);
+ }
+
+ @Test
+ public void testNoTargetReferences() {
+ assertNotNull(myService);
+ }
+
+ @Test
+ public void testNotFoundReference() {
+ assertNull(myNullService);
+ }
+
+ @Test
+ public void testTargetReferences(){
+ assertNotNull(myCoolService);
+ assertNotNull(myLameService);
+ assertEquals("Cool Service", myCoolService.getName());
+ assertEquals("Lame Service", myLameService.getName());
+ assertTrue(myService.getName().equals(myCoolService.getName()) ||
+ myService.getName().equals(myLameService.getName()));
+ }
+}
diff --git a/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyCoolServiceForTestingIT.java b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyCoolServiceForTestingIT.java
new file mode 100644
index 0000000..c0a1d71
--- /dev/null
+++ b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyCoolServiceForTestingIT.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.sling.junit.it.impl;
+
+import org.apache.sling.junit.it.MyServiceIT;
+import org.osgi.service.component.annotations.Component;
+
+@Component(
+ service = MyServiceIT.class
+)
+public class MyCoolServiceForTestingIT implements MyServiceIT {
+
+ /**
+ * @return Name of the Service which is used to discover the Service by the User
+ **/
+ @Override
+ public String getName() {
+ return "Cool Service";
+ }
+
+ /**
+ * @return Description of the Service
+ **/
+ @Override
+ public String getDescription() {
+ return "My Cool Service is for testing @TestReference for in running JUnit tests within Sling";
+ }
+}
diff --git a/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyLameServiceForTestingIT.java b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyLameServiceForTestingIT.java
new file mode 100644
index 0000000..8527660
--- /dev/null
+++ b/src/it/annotations-it/src/main/java/org/apache/sling/junit/it/impl/MyLameServiceForTestingIT.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.sling.junit.it.impl;
+
+import org.apache.sling.junit.it.MyServiceIT;
+import org.osgi.service.component.annotations.Component;
+
+@Component(
+ service = MyServiceIT.class
+)
+public class MyLameServiceForTestingIT implements MyServiceIT {
+ /**
+ * @return Name of the Service which is used to discover the Service by the User
+ **/
+ @Override
+ public String getName() {
+ return "Lame Service";
+ }
+
+ /**
+ * @return Description of the Service
+ **/
+ @Override
+ public String getDescription() {
+ return "My Lame Service is for testing @TestReference for in running JUnit tests within Sling";
+ }
+}
diff --git a/src/it/annotations-it/src/test/java/org/apache/sling/junit/annotations/ReferenceIT.java b/src/it/annotations-it/src/test/java/org/apache/sling/junit/annotations/ReferenceIT.java
new file mode 100644
index 0000000..d9389f7
--- /dev/null
+++ b/src/it/annotations-it/src/test/java/org/apache/sling/junit/annotations/ReferenceIT.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.sling.junit.annotations;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.sling.junit.it.impl.MyLameServiceForTestingIT;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.apache.sling.testing.clients.osgi.OsgiConsoleClient;
+
+import java.io.UnsupportedEncodingException;
+import java.net.ServerSocket;
+import java.net.URI;
+import java.util.Collections;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExamServer;
+import org.ops4j.pax.exam.options.extra.VMOption;
+import org.apache.sling.testing.paxexam.SlingOptions;
+import static org.apache.sling.testing.paxexam.SlingOptions.logback;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import org.apache.sling.testing.paxexam.TestSupport;
+
+
+public class ReferenceIT extends TestSupport {
+ protected static int httpPort;
+ protected static OsgiConsoleClient CLIENT;
+ private final static int STARTUP_WAIT_SECONDS = 30;
+
+ @ClassRule
+ public static PaxExamServer serverRule = new PaxExamServer() {
+ @Override
+ protected void before() throws Exception {
+ // Use a different port for each OSGi framework instance
+ // that's started - they can overlap if the previous one
+ // is not fully stopped when the next one starts.
+ setHttpPort();
+ super.before();
+ }
+ };
+
+ @Configuration
+ public Option[] configuration() throws Exception {
+
+ final String vmOpt = System.getProperty("pax.vm.options");
+ VMOption vmOption = null;
+ if (StringUtils.isNotEmpty(vmOpt)) {
+ vmOption = new VMOption(vmOpt);
+ }
+
+ final String jacocoOpt = System.getProperty("jacoco.command");
+ VMOption jacocoCommand = null;
+ if (StringUtils.isNotEmpty(jacocoOpt)) {
+ jacocoCommand = new VMOption(jacocoOpt);
+ }
+
+ final String workingDirectory = workingDirectory();
+
+ // Need recent commons.johnzon for the osgi.contract=JavaJSONP capability
+ SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.commons.johnzon", "1.2.6");
+
+ return composite(
+ // TODO not sure why the below list of bundles is different from
+ // running tests with PaxExam.class (SLING-9929)
+ //super.baseConfiguration(),
+
+ when(vmOption != null).useOptions(vmOption),
+ when(jacocoCommand != null).useOptions(jacocoCommand),
+
+ // For some reason, Jetty starts first on port 8080 without this
+ systemProperty("org.osgi.service.http.port").value(String.valueOf(httpPort)),
+
+ slingQuickstartOakTar(workingDirectory, httpPort),
+
+ testBundle("bundle.filename"),
+ mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.junit.core").versionAsInProject(),
+
+ logback(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.log").version("1.2.4"),
+ mavenBundle().groupId("log4j").artifactId("log4j").version("1.2.17"),
+ mavenBundle().groupId("org.apache.aries.spifly").artifactId("org.apache.aries.spifly.dynamic.framework.extension").version("1.3.2"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.webconsole.plugins.ds").version("2.1.0")
+ ).getOptions();
+ }
+
+ @BeforeClass
+ public static void waitForSling() throws Exception {
+ final URI url = new URI(String.format("http://localhost:%d", httpPort));
+ CLIENT = new OsgiConsoleClient(url, "admin", "admin");
+
+ CLIENT.waitExists("/", STARTUP_WAIT_SECONDS * 1000, 500);
+
+ CLIENT.waitComponentRegistered(MyLameServiceForTestingIT.class.getName(), 10 * 1000, 500);
+
+ // Verify stable status for a bit
+ for(int i=0; i < 10 ; i++) {
+ CLIENT.waitComponentRegistered(MyLameServiceForTestingIT.class.getName(), 1000, 100);
+ Thread.sleep(100);
+ }
+ }
+
+ static void setHttpPort() {
+ try {
+ final ServerSocket serverSocket = new ServerSocket(0);
+ httpPort = serverSocket.getLocalPort();
+ serverSocket.close();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testReferenceJITest() throws ClientException, UnsupportedEncodingException {
+ SlingHttpResponse response = CLIENT.doPost("/system/sling/junit/org.apache.sling.junit.it.TestReferenceJITest.html",
+ new StringEntity("some text"),
+ Collections.emptyList(),
+ 200);
+ response.checkContentContains("TEST RUN FINISHED");
+ response.checkContentContains("failures:0");
+ response.checkContentContains("ignored:0");
+ response.checkContentContains("tests:4");
+ }
+}
diff --git a/src/it/annotations-it/src/test/resources/logback.xml b/src/it/annotations-it/src/test/resources/logback.xml
new file mode 100644
index 0000000..b8b7262
--- /dev/null
+++ b/src/it/annotations-it/src/test/resources/logback.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+<configuration>
+ <appender name="file" class="ch.qos.logback.core.FileAppender">
+ <file>target/test.log</file>
+ <append>true</append>
+ <encoder>
+ <pattern>%date level=%level thread=%thread logger=%logger sourcefile=%file line=%line %mdc message=%msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="info">
+ <appender-ref ref="file"/>
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/src/it/settings.xml b/src/it/settings.xml
new file mode 100644
index 0000000..3404fda
--- /dev/null
+++ b/src/it/settings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+
+<!--
+ As per https://maven.apache.org/plugins/maven-invoker-plugin/examples/fast-use.html
+ this lets invoked builds grab artifacts from the local repository instead of
+ downloading all of them.
+-->
+<settings>
+ <profiles>
+ <profile>
+ <id>it-repo</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <repositories>
+ <repository>
+ <id>local.central</id>
+ <url>@localRepositoryUrl@</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>local.central</id>
+ <url>@localRepositoryUrl@</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+ </profiles>
+</settings>
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/junit/annotations/SlingAnnotationsTestRunner.java b/src/main/java/org/apache/sling/junit/annotations/SlingAnnotationsTestRunner.java
index 9351d73..76b5bc6 100644
--- a/src/main/java/org/apache/sling/junit/annotations/SlingAnnotationsTestRunner.java
+++ b/src/main/java/org/apache/sling/junit/annotations/SlingAnnotationsTestRunner.java
@@ -18,42 +18,63 @@
import org.apache.sling.junit.Activator;
import org.apache.sling.junit.TestObjectProcessor;
+import org.apache.sling.junit.impl.AnnotationsProcessor;
+import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
/** TestRunner which uses a TestObjectProcessor to
* handle annotations in test classes.
* A test that has RunWith=SlingAnnotationsTestRunner can
* use @TestReference, for example, to access OSGi services.
*
- * @deprecated - the {#link TeleporterRule} is a much simpler way of executing
+ * FYI - {#link TeleporterRule} is another way of executing
* server-side tests, including OSGi service injection.
*/
-@Deprecated
+
public class SlingAnnotationsTestRunner extends BlockJUnit4ClassRunner {
private static final Logger log = LoggerFactory.getLogger(SlingAnnotationsTestRunner.class);
+ private TestObjectProcessor top;
+ private List<Object> tests;
public SlingAnnotationsTestRunner(Class<?> clazz) throws InitializationError {
super(clazz);
+ tests = new ArrayList<>();
}
@Override
protected Object createTest() throws Exception {
final BundleContext ctx = Activator.getBundleContext();
- final ServiceReference ref =
- ctx == null ? null : ctx.getServiceReference(TestObjectProcessor.class.getName());
- final TestObjectProcessor top = ref == null ? null : (TestObjectProcessor)ctx.getService(ref);
-
+ final ServiceReference<? extends Object> ref = ctx == null ? null : ctx.getServiceReference(TestObjectProcessor.class.getName());
+ top = ref == null ? null : (TestObjectProcessor) ctx.getService(ref);
if(top == null) {
log.info("No TestObjectProcessor service available, annotations will not be processed");
return super.createTest();
} else {
log.debug("Using TestObjectProcessor {}", top);
- return top.process(super.createTest());
+ Object test = top.process(super.createTest());
+ tests.add(test);
+ return test;
+ }
+ }
+
+ @Override
+ public void run(RunNotifier notifier){
+ try {
+ super.run(notifier);
+ } finally {
+ if (this.top instanceof AnnotationsProcessor) {
+ AnnotationsProcessor ap = (AnnotationsProcessor) this.top;
+ for (int i=0; i<tests.size(); i++) {
+ ap.cleanupTest(tests.get(i));
+ }
+ }
}
}
}
diff --git a/src/main/java/org/apache/sling/junit/annotations/TestReference.java b/src/main/java/org/apache/sling/junit/annotations/TestReference.java
index 21bb398..74cca27 100644
--- a/src/main/java/org/apache/sling/junit/annotations/TestReference.java
+++ b/src/main/java/org/apache/sling/junit/annotations/TestReference.java
@@ -26,11 +26,10 @@
* Annotation used to inject services in test classes. Similar to the Felix @Reference annotation, but we need RetentionPolicy.RUNTIME so we
* cannot use that one.
*
- * @deprecated - the {#link TeleporterRule} is a much simpler way of executing server-side tests, including OSGi service injection.
+ * FYI {#link TeleporterRule} is another way of executing server-side tests, including OSGi service injection.
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
-@Deprecated
public @interface TestReference {
/**
* The local name of the reference. Default value is the name of the field to which the annotation applies.
@@ -40,6 +39,12 @@
String name() default "";
/**
+ * LDAP-style filter for targeting a specific reference implementation
+ *
+ * @return the local name of the reference ("" by default)
+ */
+ String target() default "";
+ /**
* The name of the service interface. This name is used by the Service Component Runtime to access the service on behalf of the
* component. The default value for is the type of the field to which the annotation applies.
*
diff --git a/src/main/java/org/apache/sling/junit/annotations/package-info.java b/src/main/java/org/apache/sling/junit/annotations/package-info.java
index f265cce..c3874ab 100644
--- a/src/main/java/org/apache/sling/junit/annotations/package-info.java
+++ b/src/main/java/org/apache/sling/junit/annotations/package-info.java
@@ -16,7 +16,7 @@
~ specific language governing permissions and limitations
~ under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-@Version("1.1.0")
+@Version("1.2.0")
package org.apache.sling.junit.annotations;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/junit/impl/AnnotationsProcessor.java b/src/main/java/org/apache/sling/junit/impl/AnnotationsProcessor.java
index c4a4e92..364120e 100644
--- a/src/main/java/org/apache/sling/junit/impl/AnnotationsProcessor.java
+++ b/src/main/java/org/apache/sling/junit/impl/AnnotationsProcessor.java
@@ -16,12 +16,17 @@
*/
package org.apache.sling.junit.impl;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import org.apache.sling.junit.TestObjectProcessor;
import org.apache.sling.junit.annotations.TestReference;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
@@ -32,9 +37,11 @@
public class AnnotationsProcessor implements TestObjectProcessor {
private Logger log = LoggerFactory.getLogger(getClass());
private BundleContext bundleContext;
-
+ private Map<Object, List<ServiceGetter<?>>> map;
+
protected void activate(ComponentContext ctx) {
bundleContext = ctx.getBundleContext();
+ this.map = new IdentityHashMap<>();
if(bundleContext == null) {
throw new IllegalArgumentException("Null BundleContext in activate()");
}
@@ -44,11 +51,17 @@
protected void deactivate(ComponentContext ctx) {
bundleContext = null;
log.debug("{} deactivated", this);
+ for (Map.Entry<Object, List<ServiceGetter<?>>> entry : map.entrySet()) {
+ cleanupTest(entry.getKey());
+ }
+ map.clear();
}
/** Process annotations on the test object */
+ @Override
public Object process(Object testObject) throws Exception {
log.debug("processing {}", testObject);
+ map.put(testObject, new ArrayList<>());
for(Field f : testObject.getClass().getDeclaredFields()) {
if(f.isAnnotationPresent(TestReference.class)) {
processTestReference(testObject, f);
@@ -57,6 +70,13 @@
return testObject;
}
+ public void cleanupTest(Object test) {
+ List<ServiceGetter<?>> serviceGetters = this.map.remove(test);
+ for (int i=0; i<serviceGetters.size(); i++) {
+ serviceGetters.get(i).close();
+ }
+ }
+
/** Process the TestReference annotation to inject services into fields */
private void processTestReference(Object testObject, Field f) throws Exception {
if(bundleContext == null) {
@@ -64,31 +84,30 @@
log.error(msg);
throw new IllegalArgumentException(msg);
}
-
final Class<?> serviceType = f.getType();
- final Object service = getService(serviceType);
- if(service != null) {
- f.setAccessible(true);
- f.set(testObject, service);
- log.debug("Injected service {} into field {}",
- serviceType.getName(), f.getName());
- } else {
- log.warn("Service {} not found for field {}",
- serviceType.getName(), f.getName());
- }
- }
-
- private Object getService(Class<?> c) {
- Object result = null;
- // BundleContext is not a service, but can be injected
- if(c.equals(BundleContext.class)) {
- result = bundleContext;
- } else {
- ServiceReference ref = bundleContext.getServiceReference(c.getName());
- if(ref != null) {
- result = bundleContext.getService(ref);
+ Annotation[] testReferences = f.getDeclaredAnnotations();
+ if(Objects.nonNull(testReferences) && testReferences.length != 0){
+ TestReference testReference = (TestReference) testReferences[0];
+
+ final Object service = getService(serviceType, testReference.target(), testObject);
+ if(service != null) {
+ f.setAccessible(true);
+ f.set(testObject, service);
+ log.debug("Injected service {} into field {}",
+ serviceType.getName(), f.getName());
+ } else {
+ log.warn("Service {} not found for field {}",
+ serviceType.getName(), f.getName());
}
}
+ }
+
+ private Object getService(Class<?> c, String target, Object testObj) {
+ // target may be used to get a specific service implementation of the interface, c
+ Object result = null;
+ final ServiceGetter<? extends Object> serviceGetter = ServiceGetter.create(bundleContext, c, target);
+ result = serviceGetter.getService();
+ this.map.get(testObj).add(serviceGetter);
return result;
}
}
diff --git a/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java b/src/main/java/org/apache/sling/junit/impl/ServiceGetter.java
similarity index 94%
rename from src/main/java/org/apache/sling/junit/rules/ServiceGetter.java
rename to src/main/java/org/apache/sling/junit/impl/ServiceGetter.java
index d506db8..35c5ceb 100644
--- a/src/main/java/org/apache/sling/junit/rules/ServiceGetter.java
+++ b/src/main/java/org/apache/sling/junit/impl/ServiceGetter.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.sling.junit.rules;
+package org.apache.sling.junit.impl;
import java.io.Closeable;
@@ -27,13 +27,13 @@
import org.osgi.util.tracker.ServiceTracker;
/** Implements the logic used to get a service */
-class ServiceGetter<T> implements Closeable {
+public class ServiceGetter<T> implements Closeable {
private final ServiceTracker tracker;
private final BundleContext bundleContext;
public static <T> ServiceGetter<T> create(BundleContext bundleContext, Class<T> serviceClass, String ldapFilter) {
- return new ServiceGetter<T>(bundleContext, serviceClass, ldapFilter);
+ return new ServiceGetter<>(bundleContext, serviceClass, ldapFilter);
}
@SuppressWarnings("unchecked")
diff --git a/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java b/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java
index 3de6538..2834567 100644
--- a/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java
+++ b/src/main/java/org/apache/sling/junit/rules/ServerSideTeleporter.java
@@ -20,6 +20,7 @@
import java.util.List;
import org.apache.sling.junit.Activator;
+import org.apache.sling.junit.impl.ServiceGetter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
diff --git a/src/main/java/org/apache/sling/junit/rules/Service.java b/src/main/java/org/apache/sling/junit/rules/Service.java
index ba84c18..346e133 100644
--- a/src/main/java/org/apache/sling/junit/rules/Service.java
+++ b/src/main/java/org/apache/sling/junit/rules/Service.java
@@ -18,6 +18,7 @@
package org.apache.sling.junit.rules;
import org.apache.sling.junit.Activator;
+import org.apache.sling.junit.impl.ServiceGetter;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;