Skip to content

Commit aade194

Browse files
committed
Allow specifying arbitrary Runners to use through LWTS
1 parent 1d4264e commit aade194

File tree

4 files changed

+257
-51
lines changed

4 files changed

+257
-51
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* This file is part of LaunchWrapperTestSuite, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.lwts.runner;
26+
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Inherited;
29+
import java.lang.annotation.Retention;
30+
import java.lang.annotation.RetentionPolicy;
31+
import java.lang.annotation.Target;
32+
33+
import org.junit.runner.Description;
34+
import org.junit.runner.RunWith;
35+
import org.junit.runner.Runner;
36+
import org.junit.runner.notification.RunNotifier;
37+
import org.junit.runners.model.InitializationError;
38+
import org.junit.runners.model.RunnerBuilder;
39+
40+
import com.google.common.collect.Lists;
41+
42+
/**
43+
* A runner that delegates to another runner. Note that this class does not do anything
44+
* to run in a Launchwrapper context; use {@link LaunchWrapperDelegateRunner} for that.
45+
*
46+
* When using this directly with {@link RunWith}, also specify a {@link DelegatedRunWith}
47+
* annotation to indicate the runner to delegate to.
48+
*/
49+
public class DelegateRunner extends Runner {
50+
51+
/**
52+
* Specifies the runner to delegate to.
53+
*/
54+
@Retention(RetentionPolicy.RUNTIME)
55+
@Target(ElementType.TYPE)
56+
@Inherited
57+
public @interface DelegatedRunWith {
58+
/**
59+
* @return the runner to use
60+
*/
61+
public Class<? extends Runner> value();
62+
}
63+
64+
/**
65+
* Invoked by JUnit to initialize this runner.
66+
*
67+
* @param testClass The test class
68+
* @param builder The RunnerBuilder
69+
* @throws InitializationError If an error occurs during initialization
70+
*/
71+
public DelegateRunner(Class<?> testClass, RunnerBuilder builder) throws InitializationError {
72+
this(getDelegateClass(testClass), testClass, builder);
73+
}
74+
75+
/**
76+
* Initialize this runner with another runner to delegate to.
77+
*
78+
* @param runnerClass The runner to delegate to
79+
* @param testClass The test class
80+
* @param builder The RunnerBuilder
81+
* @throws InitializationError If an error occurs during initialization
82+
*/
83+
protected DelegateRunner(Class<? extends Runner> runnerClass, Class<?> testClass, RunnerBuilder builder) throws InitializationError {
84+
this.runner = constructRunner(runnerClass, testClass, builder);
85+
}
86+
87+
/**
88+
* Finds the delegate runner class using the DelegatedRunWith annotation.
89+
*/
90+
private static Class<? extends Runner> getDelegateClass(Class<?> testClass) throws InitializationError {
91+
DelegatedRunWith annotation = testClass.getAnnotation(DelegatedRunWith.class);
92+
if (annotation == null) {
93+
throw new InitializationError("class '" + testClass + "' must have a DelegateRunWith annotation");
94+
}
95+
return annotation.value();
96+
}
97+
98+
/**
99+
* Constructs the runner instance, trying both a constructor that takes only
100+
* a class, and one that takes a class and a RunnerBuilder. (See
101+
* https://git.io/fNeAw)
102+
*/
103+
@SuppressWarnings("serial")
104+
private static Runner constructRunner(Class<? extends Runner> runnerClass, Class<?> testClass, RunnerBuilder builder) throws InitializationError {
105+
try {
106+
return runnerClass.getConstructor(Class.class).newInstance(testClass);
107+
} catch (Exception e) {
108+
try {
109+
return runnerClass.getConstructor(Class.class, RunnerBuilder.class).newInstance(testClass, builder);
110+
} catch (Exception e2) {
111+
throw new InitializationError(Lists.newArrayList(
112+
new Throwable("Failed to construct runner '" + runnerClass
113+
+ "' for test '" + testClass + "'", null, false, false) {
114+
@Override public String toString() { return getMessage(); }
115+
},
116+
e, e2));
117+
}
118+
}
119+
}
120+
121+
private final Runner runner;
122+
123+
@Override
124+
public Description getDescription() {
125+
return runner.getDescription();
126+
}
127+
128+
@Override
129+
public void run(RunNotifier notifier) {
130+
runner.run(notifier);
131+
}
132+
133+
@Override
134+
public int testCount() {
135+
return runner.testCount();
136+
}
137+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* This file is part of LaunchWrapperTestSuite, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.lwts.runner;
26+
27+
import org.junit.runner.RunWith;
28+
import org.junit.runner.Runner;
29+
import org.junit.runners.model.InitializationError;
30+
import org.junit.runners.model.RunnerBuilder;
31+
32+
import com.google.common.base.Strings;
33+
34+
import net.minecraft.launchwrapper.Launch;
35+
36+
/**
37+
* A runner that delegates to another runner, and runs in Launchwrapper context.
38+
*
39+
* When using this directly with {@link RunWith}, also specify a {@link DelegatedRunWith}
40+
* annotation to indicate the runner to delegate to.
41+
*/
42+
public class LaunchWrapperDelegateRunner extends DelegateRunner {
43+
44+
public static final String TWEAKER_PROPERTY = "lwts.tweaker";
45+
46+
private static boolean initialized;
47+
48+
/**
49+
* Invoked by JUnit to initialize this runner.
50+
*
51+
* @param klass The test class
52+
* @param builder The RunnerBuilder
53+
* @throws InitializationError If an error occurs during initialization
54+
*/
55+
public LaunchWrapperDelegateRunner(Class<?> klass, RunnerBuilder builder) throws InitializationError {
56+
super(loadTestClass(klass), builder);
57+
}
58+
59+
/**
60+
* Initialize this runner with another runner to delegate to.
61+
*
62+
* @param runnerClass The runner to delegate to
63+
* @param testClass The test class
64+
* @param builder The RunnerBuilder
65+
* @throws InitializationError If an error occurs during initialization
66+
*/
67+
protected LaunchWrapperDelegateRunner(Class<? extends Runner> runnerClass, Class<?> testClass, RunnerBuilder builder) throws InitializationError {
68+
super(runnerClass, loadTestClass(testClass), builder);
69+
}
70+
71+
/**
72+
* Loads a test class within the Launchwrapper context.
73+
*
74+
* <p>The context will be initialized the first time this method is
75+
* invoked.</p>
76+
*
77+
* @param originalClass The original test class to load using Launchwrapper
78+
* @return The loaded class
79+
* @throws InitializationError If an errors occurs when loading the class
80+
*/
81+
public static Class<?> loadTestClass(Class<?> originalClass) throws InitializationError {
82+
if (!initialized) {
83+
initialized = true;
84+
85+
String tweakClass = System.getProperty(TWEAKER_PROPERTY);
86+
if (Strings.isNullOrEmpty(tweakClass)) {
87+
throw new RuntimeException("Missing system property " + TWEAKER_PROPERTY);
88+
}
89+
90+
// Normally, LaunchWrapper sets the thread's context class loader
91+
// to the LaunchClassLoader. However, that causes issue as soon as
92+
// tests are run in the normal class loader in the same thread.
93+
// Simply resetting it seems to fix various issues with Mockito.
94+
Thread thread = Thread.currentThread();
95+
ClassLoader contextClassLoader = thread.getContextClassLoader();
96+
97+
Launch.main(new String[]{"--tweakClass", tweakClass});
98+
99+
thread.setContextClassLoader(contextClassLoader);
100+
}
101+
102+
try {
103+
return Class.forName(originalClass.getName(), true, Launch.classLoader);
104+
} catch (ClassNotFoundException e) {
105+
throw new InitializationError(e);
106+
}
107+
}
108+
109+
}

src/main/java/org/spongepowered/lwts/runner/LaunchWrapperParameterized.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,25 @@
2626

2727
import org.junit.runner.RunWith;
2828
import org.junit.runners.Parameterized;
29+
import org.junit.runners.model.RunnerBuilder;
2930

3031
/**
3132
* {@link Parameterized} test runner for JUnit. To run a parameterized test
3233
* in the Launchwrapper context, declare this class as test runner using
3334
* {@link RunWith}.
3435
*/
35-
public class LaunchWrapperParameterized extends Parameterized {
36+
public class LaunchWrapperParameterized extends LaunchWrapperDelegateRunner {
3637

3738
/**
3839
* Invoked by JUnit to initialize the {@link LaunchWrapperParameterized}
3940
* test runner.
4041
*
4142
* @param klass The test class
43+
* @param builder The RunnerBuilder
4244
* @throws Throwable If an error occurs during initialization
4345
*/
44-
public LaunchWrapperParameterized(Class<?> klass) throws Throwable {
45-
super(LaunchWrapperTestRunner.loadTestClass(klass));
46+
public LaunchWrapperParameterized(Class<?> klass, RunnerBuilder builder) throws Throwable {
47+
super(Parameterized.class, klass, builder);
4648
}
4749

4850
}

src/main/java/org/spongepowered/lwts/runner/LaunchWrapperTestRunner.java

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,68 +24,26 @@
2424
*/
2525
package org.spongepowered.lwts.runner;
2626

27-
import com.google.common.base.Strings;
28-
import net.minecraft.launchwrapper.Launch;
2927
import org.junit.runner.RunWith;
30-
import org.junit.runners.BlockJUnit4ClassRunner;
28+
import org.junit.runners.JUnit4;
3129
import org.junit.runners.model.InitializationError;
30+
import org.junit.runners.model.RunnerBuilder;
3231

3332
/**
3433
* Standard JUnit test runner. To run a test class in the Launchwrapper
3534
* context, declare this class as test runner using {@link RunWith}.
3635
*/
37-
public class LaunchWrapperTestRunner extends BlockJUnit4ClassRunner {
38-
39-
public static final String TWEAKER_PROPERTY = "lwts.tweaker";
40-
41-
private static boolean initialized;
36+
public class LaunchWrapperTestRunner extends LaunchWrapperDelegateRunner {
4237

4338
/**
4439
* Invoked by JUnit to initialize the {@link LaunchWrapperTestRunner}.
4540
*
4641
* @param klass The test class
42+
* @param builder The RunnerBuilder
4743
* @throws InitializationError If an error occurs during initialization
4844
*/
49-
public LaunchWrapperTestRunner(Class<?> klass) throws InitializationError {
50-
super(loadTestClass(klass));
51-
}
52-
53-
/**
54-
* Loads a test class within the Launchwrapper context.
55-
*
56-
* <p>The context will be initialized the first time this method is
57-
* invoked.</p>
58-
*
59-
* @param originalClass The original test class to load using Launchwrapper
60-
* @return The loaded class
61-
* @throws InitializationError If an errors occurs when loading the class
62-
*/
63-
public static Class<?> loadTestClass(Class<?> originalClass) throws InitializationError {
64-
if (!initialized) {
65-
initialized = true;
66-
67-
String tweakClass = System.getProperty(TWEAKER_PROPERTY);
68-
if (Strings.isNullOrEmpty(tweakClass)) {
69-
throw new RuntimeException("Missing system property " + TWEAKER_PROPERTY);
70-
}
71-
72-
// Normally, LaunchWrapper sets the thread's context class loader
73-
// to the LaunchClassLoader. However, that causes issue as soon as
74-
// tests are run in the normal class loader in the same thread.
75-
// Simply resetting it seems to fix various issues with Mockito.
76-
Thread thread = Thread.currentThread();
77-
ClassLoader contextClassLoader = thread.getContextClassLoader();
78-
79-
Launch.main(new String[]{"--tweakClass", tweakClass});
80-
81-
thread.setContextClassLoader(contextClassLoader);
82-
}
83-
84-
try {
85-
return Class.forName(originalClass.getName(), true, Launch.classLoader);
86-
} catch (ClassNotFoundException e) {
87-
throw new InitializationError(e);
88-
}
45+
public LaunchWrapperTestRunner(Class<?> klass, RunnerBuilder builder) throws InitializationError {
46+
super(JUnit4.class, klass, builder);
8947
}
9048

9149
}

0 commit comments

Comments
 (0)