Skip to content

Commit 6b990d2

Browse files
authored
Tests (#20)
Add test coverage + more unit tests
1 parent 2889651 commit 6b990d2

File tree

67 files changed

+1460
-543
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1460
-543
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ deploy:
1818
name: $project_version
1919
install:
2020
mvn --settings .maven.xml clean install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V
21+
after_success:
22+
- mvn clean test jacoco:report coveralls:report

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Kyle
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# mcspring [![Build Status](https://travis-ci.org/kylepls/mcspring.svg?branch=master)](https://travis-ci.org/kylepls/mcspring) ![Maven Central](https://img.shields.io/maven-central/v/in.kyle.mcspring/mcspring)
1+
# mcspring [![Build Status](https://travis-ci.org/kylepls/mcspring.svg?branch=master)](https://travis-ci.org/kylepls/mcspring) ![Maven Central](https://img.shields.io/maven-central/v/in.kyle.mcspring/mcspring-starter) [![Coverage Status](https://coveralls.io/repos/github/kylepls/mcspring/badge.svg)](https://coveralls.io/github/kylepls/mcspring)
22

33
Writing Bukkit plugins is a nightmare. I often lay awake in my bed late at night unable to sleep
44
because Bukkit made events an annotation but commands are created by implementing a class.

mcspring-api/mcspring-base/pom.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
<groupId>org.springframework.boot</groupId>
3434
<artifactId>spring-boot-loader</artifactId>
3535
</dependency>
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-starter-aop</artifactId>
39+
</dependency>
3640
<dependency>
3741
<groupId>org.springframework.boot</groupId>
3842
<artifactId>spring-boot-starter</artifactId>
@@ -50,17 +54,13 @@
5054
<scope>provided</scope>
5155
</dependency>
5256
<dependency>
53-
<groupId>org.springframework.boot</groupId>
54-
<artifactId>spring-boot-starter-aop</artifactId>
57+
<groupId>org.projectlombok</groupId>
58+
<artifactId>lombok</artifactId>
5559
</dependency>
5660
<dependency>
5761
<groupId>org.springframework.boot</groupId>
5862
<artifactId>spring-boot-starter-test</artifactId>
5963
<scope>test</scope>
6064
</dependency>
61-
<dependency>
62-
<groupId>org.projectlombok</groupId>
63-
<artifactId>lombok</artifactId>
64-
</dependency>
6565
</dependencies>
6666
</project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package in.kyle.mcspring;
2+
3+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
@Target(ElementType.TYPE)
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@ConditionalOnProperty("spigot.plugin")
13+
public @interface RequiresSpigot {
14+
}

mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.java

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import org.springframework.core.io.DefaultResourceLoader;
1111
import org.springframework.core.io.ResourceLoader;
1212

13-
import java.util.Arrays;
1413
import java.util.LinkedHashMap;
1514
import java.util.Map;
1615

@@ -26,20 +25,6 @@ public class SpringPlugin {
2625
@Getter
2726
private ConfigurableApplicationContext context;
2827

29-
public static void setup(Plugin plugin, Class<?> config) {
30-
setupLogger();
31-
SpringPlugin springPlugin = new SpringPlugin(plugin);
32-
springPlugin.initSpring(config);
33-
SETUP_PLUGINS.put(plugin, springPlugin);
34-
}
35-
36-
public static void teardown(Plugin plugin) {
37-
SpringPlugin springPlugin = SETUP_PLUGINS.remove(plugin);
38-
if (springPlugin != null) {
39-
springPlugin.onDisable(plugin);
40-
}
41-
}
42-
4328
public final void onDisable(Plugin plugin) {
4429
if (context != null) {
4530
context.close();
@@ -50,20 +35,30 @@ public final void onDisable(Plugin plugin) {
5035

5136
private void initSpring(Class<?> config) {
5237
SpringApplicationBuilder builder = new SpringApplicationBuilder();
53-
ClassLoader classLoader = plugin.getClass().getClassLoader();
54-
ResourceLoader loader = new DefaultResourceLoader(classLoader);
55-
Class<?>[] sources = new Class[]{config, SpringSpigotSupport.class};
5638
if (!SETUP_PLUGINS.isEmpty()) {
5739
SpringPlugin parent = findParentCandidate();
5840
builder.parent(parent.getContext());
59-
sources = Arrays.copyOfRange(sources, 0, 1);
6041
}
61-
context = builder.sources(sources)
62-
.resourceLoader(loader)
63-
.bannerMode(Banner.Mode.OFF)
64-
.properties("spigot.plugin=" + plugin.getName())
65-
.logStartupInfo(true)
66-
.run();
42+
43+
ClassLoader classLoader = plugin.getClass().getClassLoader();
44+
Class<?>[] sources = new Class[]{config, SpringSpigotSupport.class};
45+
ResourceLoader loader = new DefaultResourceLoader(classLoader);
46+
47+
runWithContextClassLoader(classLoader, () -> {
48+
context = builder.sources(sources)
49+
.resourceLoader(loader)
50+
.bannerMode(Banner.Mode.OFF)
51+
.properties("spigot.plugin=" + plugin.getName())
52+
.logStartupInfo(true)
53+
.run();
54+
});
55+
}
56+
57+
private void runWithContextClassLoader(ClassLoader classLoader, Runnable runnable) {
58+
ClassLoader temp = Thread.currentThread().getContextClassLoader();
59+
Thread.currentThread().setContextClassLoader(classLoader);
60+
runnable.run();
61+
Thread.currentThread().setContextClassLoader(temp);
6762
}
6863

6964
private static void setupLogger() {
@@ -80,4 +75,18 @@ private SpringPlugin findParentCandidate() {
8075
.map(Map.Entry::getValue)
8176
.orElse(null);
8277
}
78+
79+
public static void setup(Plugin plugin, Class<?> config) {
80+
setupLogger();
81+
SpringPlugin springPlugin = new SpringPlugin(plugin);
82+
springPlugin.initSpring(config);
83+
SETUP_PLUGINS.put(plugin, springPlugin);
84+
}
85+
86+
public static void teardown(Plugin plugin) {
87+
SpringPlugin springPlugin = SETUP_PLUGINS.remove(plugin);
88+
if (springPlugin != null) {
89+
springPlugin.onDisable(plugin);
90+
}
91+
}
8392
}

mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.bukkit.Bukkit;
44
import org.bukkit.Server;
5+
import org.bukkit.command.CommandMap;
56
import org.bukkit.configuration.file.FileConfiguration;
67
import org.bukkit.plugin.Plugin;
78
import org.bukkit.plugin.PluginDescriptionFile;
@@ -17,13 +18,15 @@
1718
import org.springframework.context.annotation.EnableAspectJAutoProxy;
1819
import org.springframework.scheduling.annotation.EnableScheduling;
1920

21+
import java.lang.reflect.Field;
2022
import java.util.logging.Logger;
2123

2224
@Configuration
2325
@ComponentScan(basePackageClasses = SpringPlugin.class)
2426
@EnableScheduling
2527
@EnableAspectJAutoProxy
26-
class SpringSpigotSupport {
28+
@RequiresSpigot
29+
public class SpringSpigotSupport {
2730

2831
@Bean
2932
Plugin plugin(@Value("${spigot.plugin}") String pluginName) {
@@ -74,4 +77,11 @@ BukkitScheduler scheduler(Server server) {
7477
PluginLoader pluginLoader(Plugin plugin) {
7578
return plugin.getPluginLoader();
7679
}
80+
81+
@Bean
82+
CommandMap getCommandMap(Server server) throws Exception {
83+
Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap");
84+
bukkitCommandMap.setAccessible(true);
85+
return (CommandMap) bukkitCommandMap.get(server);
86+
}
7787
}

mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package in.kyle.mcspring.command;
22

3-
import org.springframework.context.annotation.Lazy;
3+
import com.google.common.annotations.VisibleForTesting;
4+
45
import org.springframework.stereotype.Component;
56
import org.springframework.util.ClassUtils;
67

@@ -13,6 +14,7 @@
1314
import java.util.stream.Collectors;
1415
import java.util.stream.Stream;
1516

17+
import in.kyle.mcspring.command.registration.Resolver;
1618
import lombok.RequiredArgsConstructor;
1719
import lombok.SneakyThrows;
1820
import lombok.var;
@@ -21,15 +23,17 @@
2123
* Used to inject method parameters
2224
* Does not support annotated parameters
2325
*/
24-
@Lazy
2526
@Component
2627
@RequiredArgsConstructor
2728
public class SimpleMethodInjection {
2829

2930
private final List<Resolver> resolvers;
3031

3132
@SneakyThrows
32-
public Object invoke(Method method, Object object, List<Resolver> resolvers, Object... contextObjects) {
33+
public Object invoke(Method method,
34+
Object object,
35+
List<Resolver> resolvers,
36+
Object... contextObjects) {
3337
Object[] params = getParameters(method, resolvers, contextObjects);
3438
method.setAccessible(true);
3539
if (params.length != 0) {
@@ -39,7 +43,8 @@ public Object invoke(Method method, Object object, List<Resolver> resolvers, Obj
3943
}
4044
}
4145

42-
private List<Resolver> makeResolvers(Object... contextObjects) {
46+
@VisibleForTesting
47+
List<Resolver> makeResolvers(Object... contextObjects) {
4348
return Stream.of(contextObjects)
4449
.filter(Objects::nonNull)
4550
.map(o -> (Resolver) parameter -> ClassUtils.isAssignable(parameter.getType(),
@@ -49,17 +54,25 @@ private List<Resolver> makeResolvers(Object... contextObjects) {
4954
.collect(Collectors.toList());
5055
}
5156

52-
public Object[] getParameters(Method method, List<Resolver> contextResolvers, Object... contextObjects) {
57+
public Object[] getParameters(Method method,
58+
List<Resolver> contextResolvers,
59+
Object... contextObjects) {
60+
List<Resolver> methodResolvers = new ArrayList<>();
61+
methodResolvers.addAll(contextResolvers);
62+
methodResolvers.addAll(makeResolvers(contextObjects));
63+
return getParameters(method, methodResolvers);
64+
}
65+
66+
public Object[] getParameters(Method method, List<Resolver> contextResolvers) {
5367
Parameter[] parameters = method.getParameters();
5468
Object[] params = new Object[parameters.length];
5569

5670
List<Resolver> methodResolvers = new ArrayList<>();
5771
methodResolvers.addAll(contextResolvers);
58-
methodResolvers.addAll(makeResolvers(contextObjects));
5972
methodResolvers.addAll(resolvers);
6073
for (int i = 0; i < parameters.length; i++) {
6174
Parameter parameter = parameters[i];
62-
75+
6376
for (int j = 0; j < methodResolvers.size(); j++) {
6477
Resolver methodResolver = methodResolvers.get(j);
6578
Optional<Object> resolved = methodResolver.resolve(parameter);
@@ -70,7 +83,7 @@ public Object[] getParameters(Method method, List<Resolver> contextResolvers, Ob
7083
break;
7184
}
7285
}
73-
86+
7487
if (params[i] == null) {
7588
throw new RuntimeException(
7689
"Unable to resolve parameter " + parameter.getType() + " for " +
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package in.kyle.mcspring.command.injection;
2+
3+
import org.springframework.beans.factory.annotation.Qualifier;
4+
import org.springframework.context.ApplicationContext;
5+
import org.springframework.core.annotation.Order;
6+
import org.springframework.stereotype.Component;
7+
8+
import java.lang.reflect.Parameter;
9+
import java.util.Optional;
10+
11+
import in.kyle.mcspring.command.registration.Resolver;
12+
import lombok.RequiredArgsConstructor;
13+
14+
@Order(1)
15+
@Component
16+
@RequiredArgsConstructor
17+
class QualifierResolver implements Resolver {
18+
19+
private final ApplicationContext context;
20+
21+
@Override
22+
public Optional<Object> resolve(Parameter parameter) {
23+
Qualifier qualifier = parameter.getAnnotation(Qualifier.class);
24+
if (qualifier != null) {
25+
Object bean = context.getBean(qualifier.value(), parameter.getType());
26+
// spring will error out if mismatched type
27+
return Optional.of(bean);
28+
} else {
29+
return Optional.empty();
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)