Skip to content

Commit b98644e

Browse files
authored
Merge pull request #5 from BentoBoxWorld/develop
Release 1.1.0
2 parents 96c57c6 + cd13d31 commit b98644e

5 files changed

Lines changed: 154 additions & 14 deletions

File tree

pom.xml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,9 @@
4141
</issueManagement>
4242

4343
<distributionManagement>
44-
<snapshotRepository>
45-
<id>codemc-snapshots</id>
46-
<url>https://repo.codemc.org/repository/maven-snapshots</url>
47-
</snapshotRepository>
4844
<repository>
49-
<id>codemc-releases</id>
50-
<url>https://repo.codemc.org/repository/maven-releases</url>
45+
<id>bentoboxworld</id>
46+
<url>https://repo.codemc.org/repository/bentoboxworld/</url>
5147
</repository>
5248
</distributionManagement>
5349

@@ -58,8 +54,8 @@
5854
<!-- Non-minecraft related dependencies -->
5955
<powermock.version>2.0.9</powermock.version>
6056
<!-- More visible way how to change dependency versions -->
61-
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
62-
<bentobox.version>1.23.0</bentobox.version>
57+
<spigot.version>1.21.3-R0.1-SNAPSHOT</spigot.version>
58+
<bentobox.version>2.7.1-SNAPSHOT</bentobox.version>
6359
<!-- AOneBlock addon version -->
6460
<aoneblock.version>1.12.3-SNAPSHOT</aoneblock.version>
6561
<!-- Panel Utils version -->
@@ -69,7 +65,7 @@
6965
<!-- Do not change unless you want different name for local builds. -->
7066
<build.number>-LOCAL</build.number>
7167
<!-- This allows to change between versions. -->
72-
<build.version>1.0.1</build.version>
68+
<build.version>1.1.0</build.version>
7369
<sonar.projectKey>BentoBoxWorld_TopBlock</sonar.projectKey>
7470
<sonar.organization>bentobox-world</sonar.organization>
7571
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
@@ -129,6 +125,10 @@
129125
<id>spigot-repo</id>
130126
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
131127
</repository>
128+
<repository>
129+
<id>bentoboxworld</id>
130+
<url>https://repo.codemc.org/repository/bentoboxworld/</url>
131+
</repository>
132132
<repository>
133133
<id>codemc-repo</id>
134134
<url>https://repo.codemc.org/repository/maven-public/</url>
@@ -355,7 +355,7 @@
355355
<plugin>
356356
<groupId>org.jacoco</groupId>
357357
<artifactId>jacoco-maven-plugin</artifactId>
358-
<version>0.8.8</version>
358+
<version>0.8.10</version>
359359
<configuration>
360360
<append>true</append>
361361
<excludes>

src/main/resources/addon.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: TopBlock
22
main: world.bentobox.topblock.TopBlock
33
version: ${version}${build.number}
44
icon: GRASS_BLOCK
5-
api-version: 1.21.0
5+
api-version: 2.7.1
66

77
authors: tastybento
88

src/main/resources/plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: BentoBox-TopBlock
22
main: world.bentobox.topblock.TopBlockPladdon
33
version: ${project.version}${build.number}
4-
api-version: "1.19"
4+
api-version: "1.21""
55
66
authors: [tastybento]
77
contributors: ["The BentoBoxWorld Community"]

src/test/java/world/bentobox/topblock/TopBlockManagerTest.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.junit.Assert.assertNotNull;
66
import static org.junit.Assert.assertTrue;
77
import static org.mockito.ArgumentMatchers.anyString;
8+
import static org.mockito.Mockito.mock;
89
import static org.mockito.Mockito.when;
910

1011
import java.util.ArrayList;
@@ -13,26 +14,35 @@
1314
import java.util.Optional;
1415
import java.util.UUID;
1516

17+
import org.bukkit.Bukkit;
18+
import org.bukkit.Server;
1619
import org.eclipse.jdt.annotation.NonNull;
20+
import org.junit.After;
1721
import org.junit.Before;
1822
import org.junit.Test;
1923
import org.junit.runner.RunWith;
2024
import org.mockito.Mock;
25+
import org.mockito.Mockito;
26+
import org.powermock.api.mockito.PowerMockito;
27+
import org.powermock.core.classloader.annotations.PrepareForTest;
2128
import org.powermock.modules.junit4.PowerMockRunner;
2229

2330
import world.bentobox.aoneblock.AOneBlock;
2431
import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
2532
import world.bentobox.aoneblock.listeners.BlockListener;
33+
import world.bentobox.bentobox.api.user.User;
2634
import world.bentobox.bentobox.database.objects.Island;
2735
import world.bentobox.bentobox.managers.IslandsManager;
2836
import world.bentobox.topblock.TopBlockManager.TopTenData;
2937
import world.bentobox.topblock.config.ConfigSettings;
38+
import world.bentobox.topblock.mocks.ServerMocks;
3039

3140
/**
3241
* @author tastybento
3342
*
3443
*/
3544
@RunWith(PowerMockRunner.class)
45+
@PrepareForTest(Bukkit.class)
3646
public class TopBlockManagerTest {
3747

3848
@Mock
@@ -43,8 +53,7 @@ public class TopBlockManagerTest {
4353
private TopBlockManager tbm;
4454
@Mock
4555
private AOneBlock aob;
46-
@Mock
47-
private BlockListener bl;
56+
4857
@Mock
4958
private IslandsManager im;
5059

@@ -54,6 +63,11 @@ public class TopBlockManagerTest {
5463
*/
5564
@Before
5665
public void setUp() throws Exception {
66+
Server server = ServerMocks.newServer();
67+
68+
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
69+
when(Bukkit.getServer()).thenReturn(server);
70+
5771
List<OneBlockIslands> list = new ArrayList<>();
5872
OneBlockIslands i = new OneBlockIslands(UUID.randomUUID().toString());
5973
i.setLifetime(100);
@@ -65,12 +79,20 @@ public void setUp() throws Exception {
6579
when(addon.getIslands()).thenReturn(im);
6680
when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
6781
// AOneBlock
82+
BlockListener bl = mock(BlockListener.class); // This class uses static initializations so if it is mocked as a field, it will spark an issue
6883
when(bl.getAllIslands()).thenReturn(list);
6984
when(aob.getBlockListener()).thenReturn(bl);
7085
when(addon.getaOneBlock()).thenReturn(aob);
7186
tbm = new TopBlockManager(addon);
7287
}
7388

89+
@After
90+
public void tearDown() {
91+
ServerMocks.unsetBukkitServer();
92+
User.clearUsers();
93+
Mockito.framework().clearInlineMocks();
94+
}
95+
7496
/**
7597
* Test method for {@link world.bentobox.topblock.TopBlockManager#TopBlockManager(world.bentobox.topblock.TopBlock)}.
7698
*/
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package world.bentobox.topblock.mocks;
2+
3+
import static org.mockito.ArgumentMatchers.notNull;
4+
import static org.mockito.Mockito.doAnswer;
5+
import static org.mockito.Mockito.doReturn;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.when;
8+
9+
import java.lang.reflect.Field;
10+
import java.util.HashMap;
11+
import java.util.Locale;
12+
import java.util.Map;
13+
import java.util.Set;
14+
import java.util.logging.Logger;
15+
16+
import org.bukkit.Bukkit;
17+
import org.bukkit.Keyed;
18+
import org.bukkit.NamespacedKey;
19+
import org.bukkit.Registry;
20+
import org.bukkit.Server;
21+
import org.bukkit.Tag;
22+
import org.bukkit.UnsafeValues;
23+
import org.eclipse.jdt.annotation.NonNull;
24+
25+
public final class ServerMocks {
26+
27+
public static @NonNull Server newServer() {
28+
Server mock = mock(Server.class);
29+
30+
Logger noOp = mock(Logger.class);
31+
when(mock.getLogger()).thenReturn(noOp);
32+
when(mock.isPrimaryThread()).thenReturn(true);
33+
34+
// Unsafe
35+
UnsafeValues unsafe = mock(UnsafeValues.class);
36+
when(mock.getUnsafe()).thenReturn(unsafe);
37+
38+
// Server must be available before tags can be mocked.
39+
Bukkit.setServer(mock);
40+
41+
// Bukkit has a lot of static constants referencing registry values. To initialize those, the
42+
// registries must be able to be fetched before the classes are touched.
43+
Map<Class<? extends Keyed>, Object> registers = new HashMap<>();
44+
45+
doAnswer(invocationGetRegistry -> registers.computeIfAbsent(invocationGetRegistry.getArgument(0), clazz -> {
46+
Registry<?> registry = mock(Registry.class);
47+
Map<NamespacedKey, Keyed> cache = new HashMap<>();
48+
doAnswer(invocationGetEntry -> {
49+
NamespacedKey key = invocationGetEntry.getArgument(0);
50+
// Some classes (like BlockType and ItemType) have extra generics that will be
51+
// erased during runtime calls. To ensure accurate typing, grab the constant's field.
52+
// This approach also allows us to return null for unsupported keys.
53+
Class<? extends Keyed> constantClazz;
54+
try {
55+
//noinspection unchecked
56+
constantClazz = (Class<? extends Keyed>) clazz
57+
.getField(key.getKey().toUpperCase(Locale.ROOT).replace('.', '_')).getType();
58+
} catch (ClassCastException e) {
59+
throw new RuntimeException(e);
60+
} catch (NoSuchFieldException e) {
61+
return null;
62+
}
63+
64+
return cache.computeIfAbsent(key, key1 -> {
65+
Keyed keyed = mock(constantClazz);
66+
doReturn(key).when(keyed).getKey();
67+
return keyed;
68+
});
69+
}).when(registry).get(notNull());
70+
return registry;
71+
})).when(mock).getRegistry(notNull());
72+
73+
// Tags are dependent on registries, but use a different method.
74+
// This will set up blank tags for each constant; all that needs to be done to render them
75+
// functional is to re-mock Tag#getValues.
76+
doAnswer(invocationGetTag -> {
77+
Tag<?> tag = mock(Tag.class);
78+
doReturn(invocationGetTag.getArgument(1)).when(tag).getKey();
79+
doReturn(Set.of()).when(tag).getValues();
80+
doAnswer(invocationIsTagged -> {
81+
Keyed keyed = invocationIsTagged.getArgument(0);
82+
Class<?> type = invocationGetTag.getArgument(2);
83+
if (!type.isAssignableFrom(keyed.getClass())) {
84+
return null;
85+
}
86+
// Since these are mocks, the exact instance might not be equal. Consider equal keys equal.
87+
return tag.getValues().contains(keyed)
88+
|| tag.getValues().stream().anyMatch(value -> value.getKey().equals(keyed.getKey()));
89+
}).when(tag).isTagged(notNull());
90+
return tag;
91+
}).when(mock).getTag(notNull(), notNull(), notNull());
92+
93+
// Once the server is all set up, touch BlockType and ItemType to initialize.
94+
// This prevents issues when trying to access dependent methods from a Material constant.
95+
try {
96+
Class.forName("org.bukkit.inventory.ItemType");
97+
Class.forName("org.bukkit.block.BlockType");
98+
} catch (ClassNotFoundException e) {
99+
throw new RuntimeException(e);
100+
}
101+
102+
return mock;
103+
}
104+
105+
public static void unsetBukkitServer() {
106+
try {
107+
Field server = Bukkit.class.getDeclaredField("server");
108+
server.setAccessible(true);
109+
server.set(null, null);
110+
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
111+
throw new RuntimeException(e);
112+
}
113+
}
114+
115+
private ServerMocks() {
116+
}
117+
118+
}

0 commit comments

Comments
 (0)