Skip to content

Commit 4a1ee95

Browse files
committed
Added support for running the TCK in Arquillian
This provides a test class that can be run in Arquillian. I have tested this with Thorntail (using an Arquillian AuxillaryArchiveAppender to add in my own implementation), and it works. I did originally try and make this work better with Arquillian, by trying to have it deploy each test to the container. I was able to work around the fact that the tests are created by a factory by writing my own custom container test runner that gets them from the factory. I was able to work around the fact that none of the tests extend the TestNG Arquillian by extracting all its logic into a TestNG listener that was applied to the whole suite. And finally, I was able to work around the fact that none of the test classes has an @deployment annotated method on it, by writing a custom DeploymentScenarioGenerator that extracted the deployment from a different class - the same class for each test. This was the most annoying one because WildFly has its own custom one, so doing this in a generic way was impossible. So, in the end it worked, however, it was a lot of what felt like very fragile code, so my strategy in the end was simply to write one Arquillian test that has one test method, and then, in that test method, instantiate a TestNG runner that runs the entire suite for me, using a CDI injected ReactiveStreamsEngine. If you run this from the IDE, it says there's only one test, but the logging shows 1100 or so tests, and makes it clear which failed and what the failures are. Also fixed two bugs in the TCK where the default engine was loaded instead of the passed in one.
1 parent 0d0c4f5 commit 4a1ee95

File tree

5 files changed

+372
-3
lines changed

5 files changed

+372
-3
lines changed

streams/pom.xml

+18
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<modules>
3838
<module>api</module>
3939
<module>tck</module>
40+
<module>tck-arquillian</module>
4041
<module>spec</module>
4142
</modules>
4243

@@ -67,6 +68,23 @@
6768
<artifactId>org.osgi.annotation.versioning</artifactId>
6869
<version>1.0.0</version>
6970
</dependency>
71+
<dependency>
72+
<groupId>org.jboss.arquillian</groupId>
73+
<artifactId>arquillian-bom</artifactId>
74+
<version>1.1.15.Final</version>
75+
<scope>import</scope>
76+
<type>pom</type>
77+
</dependency>
78+
<dependency>
79+
<groupId>org.testng</groupId>
80+
<artifactId>testng</artifactId>
81+
<version>6.11</version>
82+
</dependency>
83+
<dependency>
84+
<groupId>javax.enterprise</groupId>
85+
<artifactId>cdi-api</artifactId>
86+
<version>2.0</version>
87+
</dependency>
7088
</dependencies>
7189
</dependencyManagement>
7290
</project>

streams/tck-arquillian/pom.xml

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
~ Copyright (c) 2018 Contributors to the Eclipse Foundation
4+
~
5+
~ See the NOTICE file(s) distributed with this work for additional
6+
~ information regarding copyright ownership.
7+
~
8+
~ Licensed under the Apache License, Version 2.0 (the "License");
9+
~ You may not use this file except in compliance with the License.
10+
~ You may obtain a copy of the License at
11+
~
12+
~ http://www.apache.org/licenses/LICENSE-2.0
13+
~
14+
~ Unless required by applicable law or agreed to in writing, software
15+
~ distributed under the License is distributed on an "AS IS" BASIS,
16+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
~ See the License for the specific language governing permissions and
18+
~ limitations under the License.
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
20+
21+
<!--
22+
Licensed under the Apache License, Version 2.0 (the
23+
"License"); you may not use this file except in compliance
24+
with the License. You may obtain a copy of the License at
25+
26+
http://www.apache.org/licenses/LICENSE-2.0
27+
28+
Unless required by applicable law or agreed to in writing,
29+
software distributed under the License is distributed on an
30+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
31+
KIND, either express or implied. See the License for the
32+
specific language governing permissions and limitations
33+
under the License.
34+
-->
35+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
36+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
37+
<modelVersion>4.0.0</modelVersion>
38+
39+
<parent>
40+
<groupId>org.eclipse.microprofile.reactive.streams</groupId>
41+
<artifactId>microprofile-reactive-streams-parent</artifactId>
42+
<version>1.0-SNAPSHOT</version>
43+
</parent>
44+
45+
<artifactId>microprofile-reactive-streams-operators-tck-arquillian</artifactId>
46+
<name>MicroProfile Reactive Streams Operators TCK Arquillian</name>
47+
<description>MicroProfile Reactive Streams Operators :: TCK Arquillian runner</description>
48+
49+
<dependencies>
50+
<dependency>
51+
<groupId>org.eclipse.microprofile.reactive.streams</groupId>
52+
<artifactId>microprofile-reactive-streams-operators</artifactId>
53+
<version>${project.version}</version>
54+
<scope>provided</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.eclipse.microprofile.reactive.streams</groupId>
58+
<artifactId>microprofile-reactive-streams-operators-tck</artifactId>
59+
<version>${project.version}</version>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.jboss.arquillian.test</groupId>
63+
<artifactId>arquillian-test-spi</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.jboss.arquillian.testng</groupId>
67+
<artifactId>arquillian-testng-container</artifactId>
68+
</dependency>
69+
<dependency>
70+
<groupId>javax.enterprise</groupId>
71+
<artifactId>cdi-api</artifactId>
72+
<scope>provided</scope>
73+
</dependency>
74+
</dependencies>
75+
76+
<build>
77+
<plugins>
78+
<plugin>
79+
<groupId>org.apache.maven.plugins</groupId>
80+
<artifactId>maven-javadoc-plugin</artifactId>
81+
<executions>
82+
<execution>
83+
<id>attach-javadocs</id>
84+
<goals>
85+
<goal>jar</goal>
86+
</goals>
87+
</execution>
88+
</executions>
89+
</plugin>
90+
<plugin>
91+
<groupId>org.apache.maven.plugins</groupId>
92+
<artifactId>maven-source-plugin</artifactId>
93+
<executions>
94+
<execution>
95+
<id>attach-sources</id>
96+
<goals>
97+
<goal>jar</goal>
98+
</goals>
99+
</execution>
100+
</executions>
101+
</plugin>
102+
<plugin>
103+
<groupId>org.eclipse.microprofile.maven</groupId>
104+
<artifactId>microprofile-maven-build-extension</artifactId>
105+
<extensions>true</extensions>
106+
</plugin>
107+
</plugins>
108+
</build>
109+
110+
<profiles>
111+
<profile>
112+
<id>eclipse-jarsigner</id>
113+
<build>
114+
<plugins>
115+
<plugin>
116+
<groupId>org.eclipse.cbi.maven.plugins</groupId>
117+
<artifactId>eclipse-jarsigner-plugin</artifactId>
118+
<executions>
119+
<execution>
120+
<goals>
121+
<goal>sign</goal>
122+
</goals>
123+
</execution>
124+
</executions>
125+
</plugin>
126+
</plugins>
127+
</build>
128+
</profile>
129+
</profiles>
130+
131+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2018 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* You may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
******************************************************************************/
19+
20+
package org.eclipse.microprofile.reactive.streams.tck.arquillian;
21+
22+
import org.eclipse.microprofile.reactive.streams.tck.ReactiveStreamsTck;
23+
import org.jboss.arquillian.container.test.api.Deployment;
24+
import org.jboss.arquillian.testng.Arquillian;
25+
import org.jboss.shrinkwrap.api.ShrinkWrap;
26+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
27+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
28+
import org.reactivestreams.tck.TestEnvironment;
29+
import org.testng.IClassListener;
30+
import org.testng.IMethodInstance;
31+
import org.testng.IMethodInterceptor;
32+
import org.testng.IObjectFactory;
33+
import org.testng.ITestClass;
34+
import org.testng.ITestContext;
35+
import org.testng.ITestListener;
36+
import org.testng.ITestNGListener;
37+
import org.testng.ITestResult;
38+
import org.testng.TestNG;
39+
import org.testng.annotations.Test;
40+
import org.testng.internal.ObjectFactoryImpl;
41+
42+
import javax.inject.Inject;
43+
import java.util.ArrayList;
44+
import java.util.Collections;
45+
import java.util.Comparator;
46+
import java.util.List;
47+
import java.util.concurrent.atomic.AtomicInteger;
48+
import java.util.concurrent.atomic.AtomicReference;
49+
50+
/**
51+
* Test runner for running the TCK in Arquillian.
52+
* <p>
53+
* It would be nice if this was able to run the tests properly, and I did get something working, but it was so complex
54+
* and fragile because Arquillian really isn't that flexible that I decided it simply wasn't worth it, this is much
55+
* simpler.
56+
*/
57+
public class ReactiveStreamsArquillianTck extends Arquillian {
58+
@Deployment
59+
public static JavaArchive tckDeployment() {
60+
return ShrinkWrap.create(JavaArchive.class)
61+
// Add everything from the TCK
62+
.addPackages(true, ReactiveStreamsTck.class.getPackage())
63+
// And add the reactive streams TCK
64+
.addPackages(true, TestEnvironment.class.getPackage())
65+
// And we need a CDI descriptor
66+
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
67+
68+
}
69+
70+
@Inject
71+
private ReactiveStreamsCdiTck tck;
72+
73+
@Test
74+
public void runAllTckTests() throws Throwable {
75+
TestNG testng = new TestNG();
76+
77+
ObjectFactoryImpl delegate = new ObjectFactoryImpl();
78+
testng.setObjectFactory((IObjectFactory) (constructor, params) -> {
79+
if (constructor.getDeclaringClass().equals(ReactiveStreamsCdiTck.class)) {
80+
return tck;
81+
}
82+
else {
83+
return delegate.newInstance(constructor, params);
84+
}
85+
});
86+
87+
testng.setUseDefaultListeners(false);
88+
ResultListener resultListener = new ResultListener();
89+
testng.addListener((ITestNGListener) resultListener);
90+
testng.setTestClasses(new Class[]{ ReactiveStreamsCdiTck.class });
91+
testng.setMethodInterceptor(new IMethodInterceptor() {
92+
@Override
93+
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
94+
methods.sort(Comparator.comparing(m -> m.getInstance().getClass().getName()));
95+
return methods;
96+
}
97+
});
98+
testng.run();
99+
int total = resultListener.success.get() + resultListener.failed.get() + resultListener.skipped.get();
100+
System.out.println(String.format("Ran %d tests, %d passed, %d failed, %d skipped.", total, resultListener.success.get(),
101+
resultListener.failed.get(), resultListener.skipped.get()));
102+
System.out.println("Failed tests:");
103+
resultListener.failures.forEach(result -> {
104+
System.out.println(result.getInstance().getClass().getName() + "." + result.getMethod().getMethodName());
105+
});
106+
if (resultListener.failed.get() > 0) {
107+
if (resultListener.lastFailure.get() != null) {
108+
throw resultListener.lastFailure.get();
109+
}
110+
else {
111+
throw new Exception("Tests failed with no exception");
112+
}
113+
}
114+
}
115+
116+
private static class ResultListener implements IClassListener, ITestListener {
117+
private final AtomicInteger success = new AtomicInteger();
118+
private final AtomicInteger failed = new AtomicInteger();
119+
private final AtomicInteger skipped = new AtomicInteger();
120+
private final AtomicReference<Throwable> lastFailure = new AtomicReference<>();
121+
private final List<ITestResult> failures = Collections.synchronizedList(new ArrayList<>());
122+
123+
@Override
124+
public void onBeforeClass(ITestClass testClass) {
125+
System.out.println(testClass.getName() + ":");
126+
}
127+
128+
@Override
129+
public void onAfterClass(ITestClass testClass) {
130+
}
131+
132+
@Override
133+
public void onTestStart(ITestResult result) {
134+
}
135+
136+
@Override
137+
public void onTestSuccess(ITestResult result) {
138+
printResult(result, "SUCCESS");
139+
success.incrementAndGet();
140+
}
141+
142+
@Override
143+
public void onTestFailure(ITestResult result) {
144+
printResult(result, "FAILED");
145+
if (result.getThrowable() != null) {
146+
result.getThrowable().printStackTrace(System.out);
147+
lastFailure.set(result.getThrowable());
148+
}
149+
failures.add(result);
150+
failed.incrementAndGet();
151+
}
152+
153+
@Override
154+
public void onTestSkipped(ITestResult result) {
155+
printResult(result, "SKIPPED");
156+
skipped.incrementAndGet();
157+
}
158+
159+
@Override
160+
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
161+
}
162+
163+
@Override
164+
public void onStart(ITestContext context) {
165+
}
166+
167+
@Override
168+
public void onFinish(ITestContext context) {
169+
170+
}
171+
172+
private static void printResult(ITestResult result, String status) {
173+
String methodName = String.format("%-100s", result.getMethod().getMethodName()).replace(' ', '.');
174+
System.out.println(" - " + methodName + "." + status);
175+
}
176+
}
177+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2018 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* You may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
******************************************************************************/
19+
20+
package org.eclipse.microprofile.reactive.streams.tck.arquillian;
21+
22+
import org.eclipse.microprofile.reactive.streams.spi.ReactiveStreamsEngine;
23+
import org.eclipse.microprofile.reactive.streams.tck.ReactiveStreamsTck;
24+
import org.reactivestreams.tck.TestEnvironment;
25+
26+
import javax.enterprise.context.ApplicationScoped;
27+
import javax.inject.Inject;
28+
29+
@ApplicationScoped
30+
public class ReactiveStreamsCdiTck extends ReactiveStreamsTck<ReactiveStreamsEngine> {
31+
32+
public ReactiveStreamsCdiTck() {
33+
super(new TestEnvironment());
34+
}
35+
36+
@Inject
37+
private ReactiveStreamsEngine engine;
38+
39+
@Override
40+
protected ReactiveStreamsEngine createEngine() {
41+
return engine;
42+
}
43+
}

0 commit comments

Comments
 (0)