Continue debug test-home naming
This commit is contained in:
@@ -85,6 +85,7 @@ Run `./gradlew test` for the server-free JUnit suite. It has 26 tests covering:
|
|||||||
- `MetropolisPlotGoCommandTest`: self and target teleportation, ambiguous memberships, absent membership, and unsafe-destination feedback.
|
- `MetropolisPlotGoCommandTest`: self and target teleportation, ambiguous memberships, absent membership, and unsafe-destination feedback.
|
||||||
- `MetropolisPlotReserveCommandTest`: parsing six coordinates and rejecting malformed coordinates.
|
- `MetropolisPlotReserveCommandTest`: parsing six coordinates and rejecting malformed coordinates.
|
||||||
- `MetropolisHomeAcquireTest`: acquisition below the limit and refusal at the limit.
|
- `MetropolisHomeAcquireTest`: acquisition below the limit and refusal at the limit.
|
||||||
|
- `MetropolisDebugGenerateTestHomesCommandTest`: asynchronous tick-queued generation, one home per interval, no overlapping batches, and continuing test-player names across repeated runs.
|
||||||
|
|
||||||
The two opt-in tests in `src/integrationTest` require a running Paper server and RCON. They check that Metropolis appears in `/plugins` and that a coordinate reservation is persisted by WorldGuard. They do not currently verify restart persistence, join allocation, WorldEdit-selection reservations, permissions, teleports, generated blocks, protection behavior, or the other command workflows above. Run them with the environment variables documented in `README.md`; use a disposable server because the reservation test changes its WorldGuard data.
|
The two opt-in tests in `src/integrationTest` require a running Paper server and RCON. They check that Metropolis appears in `/plugins` and that a coordinate reservation is persisted by WorldGuard. They do not currently verify restart persistence, join allocation, WorldEdit-selection reservations, permissions, teleports, generated blocks, protection behavior, or the other command workflows above. Run them with the environment variables documented in `README.md`; use a disposable server because the reservation test changes its WorldGuard data.
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ Assign these permission nodes to groups or individual users in your permission p
|
|||||||
| `/metropolis-plot-reserve <name> <minX> <minY> <minZ> <maxX> <maxY> <maxZ>` | `metropolis.plot.reserve` | Creates a named reservation from explicit bounds. A player can instead run `/metropolis-plot-reserve <name>` with a WorldEdit selection. |
|
| `/metropolis-plot-reserve <name> <minX> <minY> <minZ> <maxX> <maxY> <maxZ>` | `metropolis.plot.reserve` | Creates a named reservation from explicit bounds. A player can instead run `/metropolis-plot-reserve <name>` with a WorldEdit selection. |
|
||||||
| `/metropolis-plot-go [playerName]` | Reservation owner/member; `metropolis.plot.go` to target others or bypass membership | Teleports the sender or an online target to a reservation they own or belong to. If they have several, specify one with `/metropolis-plot-go <reservationName> [playerName]`. |
|
| `/metropolis-plot-go [playerName]` | Reservation owner/member; `metropolis.plot.go` to target others or bypass membership | Teleports the sender or an online target to a reservation they own or belong to. If they have several, specify one with `/metropolis-plot-go <reservationName> [playerName]`. |
|
||||||
| `/metropolis-flag-reset` | `metropolis.flag.reset` | Reapplies Metropolis protection flags to the City and home regions. |
|
| `/metropolis-flag-reset` | `metropolis.flag.reset` | Reapplies Metropolis protection flags to the City and home regions. |
|
||||||
| `/metropolis-debug-generatetesthomes <count>` | `metropolis.debug` | Generates test homes; intended for development servers. |
|
| `/metropolis-debug-generatetesthomes <count>` | `metropolis.debug` | Queues test-home generation on the main thread, creating one home every two ticks; intended for development servers. |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,22 @@ along with Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
|||||||
|
|
||||||
package com.majinnaibu.bukkitplugins.metropolis.commands;
|
package com.majinnaibu.bukkitplugins.metropolis.commands;
|
||||||
|
|
||||||
import com.majinnaibu.bukkitplugins.metropolis.MetropolisPlugin;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import com.majinnaibu.bukkitplugins.metropolis.MetropolisPlugin;
|
||||||
|
import com.majinnaibu.bukkitplugins.metropolis.PlayerHome;
|
||||||
|
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
public class MetropolisDebugGenerateTestHomesCommand implements CommandExecutor {
|
public class MetropolisDebugGenerateTestHomesCommand implements CommandExecutor {
|
||||||
|
private static final long TICKS_BETWEEN_HOMES = 2L;
|
||||||
|
|
||||||
private MetropolisPlugin _plugin;
|
private final MetropolisPlugin _plugin;
|
||||||
|
private BukkitTask _activeTask;
|
||||||
|
|
||||||
public MetropolisDebugGenerateTestHomesCommand(MetropolisPlugin plugin) {
|
public MetropolisDebugGenerateTestHomesCommand(MetropolisPlugin plugin) {
|
||||||
_plugin = plugin;
|
_plugin = plugin;
|
||||||
@@ -33,15 +40,102 @@ public class MetropolisDebugGenerateTestHomesCommand implements CommandExecutor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||||
try {
|
if (args.length != 1) {
|
||||||
int numHomes = Integer.parseInt(args[0]);
|
sender.sendMessage("Usage: /metropolis-debug-generatetesthomes <count>");
|
||||||
for (int i = 1; i <= numHomes; i++) {
|
return false;
|
||||||
_plugin.generateHome(String.format("test%d", i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
int numHomes;
|
||||||
|
try {
|
||||||
|
numHomes = Integer.parseInt(args[0]);
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
|
sender.sendMessage("Usage: /metropolis-debug-generatetesthomes <count>");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (numHomes < 1) {
|
||||||
|
sender.sendMessage("Usage: /metropolis-debug-generatetesthomes <count>");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_activeTask != null && !_activeTask.isCancelled()) {
|
||||||
|
sender.sendMessage("[Metropolis] Test-home generation is already in progress.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestHomeBatch batch = new TestHomeBatch(sender, numHomes);
|
||||||
|
_activeTask =
|
||||||
|
_plugin.getServer().getScheduler().runTaskTimer(_plugin, batch, 1L, TICKS_BETWEEN_HOMES);
|
||||||
|
sender.sendMessage(
|
||||||
|
"[Metropolis] Queued "
|
||||||
|
+ numHomes
|
||||||
|
+ " test homes; generating one every "
|
||||||
|
+ TICKS_BETWEEN_HOMES
|
||||||
|
+ " ticks.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class TestHomeBatch implements Runnable {
|
||||||
|
private final CommandSender sender;
|
||||||
|
private final int requestedHomes;
|
||||||
|
private int generatedHomes;
|
||||||
|
private int nextTestNumber = 1;
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
private TestHomeBatch(CommandSender sender, int requestedHomes) {
|
||||||
|
this.sender = sender;
|
||||||
|
this.requestedHomes = requestedHomes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int testNumber = nextTestNumber++;
|
||||||
|
String testName = "Test" + testNumber;
|
||||||
|
try {
|
||||||
|
OfflinePlayer testPlayer = _plugin.getServer().getOfflinePlayer(testName);
|
||||||
|
if (_plugin.getNumPlots(testPlayer.getUniqueId()) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerHome home = _plugin.generateHome(testPlayer, 1);
|
||||||
|
if (home == null) {
|
||||||
|
sender.sendMessage(
|
||||||
|
"[Metropolis] Could not generate "
|
||||||
|
+ testName
|
||||||
|
+ "; completed "
|
||||||
|
+ generatedHomes
|
||||||
|
+ " of "
|
||||||
|
+ requestedHomes
|
||||||
|
+ " test homes.");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedHomes++;
|
||||||
|
if (generatedHomes == requestedHomes) {
|
||||||
|
sender.sendMessage("[Metropolis] Generated " + generatedHomes + " test homes.");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
_plugin
|
||||||
|
.getLogger()
|
||||||
|
.log(Level.SEVERE, "Failed to generate test home " + testName, exception);
|
||||||
|
sender.sendMessage(
|
||||||
|
"[Metropolis] Test-home generation failed after " + generatedHomes + " homes.");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finish() {
|
||||||
|
finished = true;
|
||||||
|
if (_activeTask != null) {
|
||||||
|
_activeTask.cancel();
|
||||||
|
_activeTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Metropolis.
|
||||||
|
|
||||||
|
Metropolis is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Metropolis is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.majinnaibu.bukkitplugins.metropolis.commands;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.majinnaibu.bukkitplugins.metropolis.MetropolisPlugin;
|
||||||
|
import com.majinnaibu.bukkitplugins.metropolis.PlayerHome;
|
||||||
|
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.scheduler.BukkitScheduler;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class MetropolisDebugGenerateTestHomesCommandTest {
|
||||||
|
@Test
|
||||||
|
void repeatedRunsContinueWithTheNextUnusedTestName() {
|
||||||
|
MetropolisPlugin plugin = mock(MetropolisPlugin.class);
|
||||||
|
Server server = mock(Server.class);
|
||||||
|
BukkitScheduler scheduler = mock(BukkitScheduler.class);
|
||||||
|
CommandSender sender = mock(CommandSender.class);
|
||||||
|
Set<UUID> existingHomeOwners = new HashSet<>();
|
||||||
|
List<String> generatedNames = new ArrayList<>();
|
||||||
|
List<Runnable> scheduledBatches = new ArrayList<>();
|
||||||
|
|
||||||
|
when(plugin.getServer()).thenReturn(server);
|
||||||
|
when(server.getScheduler()).thenReturn(scheduler);
|
||||||
|
when(scheduler.runTaskTimer(eq(plugin), any(Runnable.class), eq(1L), eq(2L)))
|
||||||
|
.thenAnswer(
|
||||||
|
invocation -> {
|
||||||
|
scheduledBatches.add(invocation.getArgument(1));
|
||||||
|
return mock(BukkitTask.class);
|
||||||
|
});
|
||||||
|
when(server.getOfflinePlayer(anyString()))
|
||||||
|
.thenAnswer(
|
||||||
|
invocation -> {
|
||||||
|
String name = invocation.getArgument(0);
|
||||||
|
OfflinePlayer player = mock(OfflinePlayer.class);
|
||||||
|
UUID playerId = UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8));
|
||||||
|
when(player.getName()).thenReturn(name);
|
||||||
|
when(player.getUniqueId()).thenReturn(playerId);
|
||||||
|
return player;
|
||||||
|
});
|
||||||
|
when(plugin.getNumPlots(any(UUID.class)))
|
||||||
|
.thenAnswer(invocation -> existingHomeOwners.contains(invocation.getArgument(0)) ? 1 : 0);
|
||||||
|
when(plugin.generateHome(any(OfflinePlayer.class), eq(1)))
|
||||||
|
.thenAnswer(
|
||||||
|
invocation -> {
|
||||||
|
OfflinePlayer player = invocation.getArgument(0);
|
||||||
|
generatedNames.add(player.getName());
|
||||||
|
existingHomeOwners.add(player.getUniqueId());
|
||||||
|
return mock(PlayerHome.class);
|
||||||
|
});
|
||||||
|
|
||||||
|
MetropolisDebugGenerateTestHomesCommand executor =
|
||||||
|
new MetropolisDebugGenerateTestHomesCommand(plugin);
|
||||||
|
|
||||||
|
assertTrue(executor.onCommand(sender, mock(Command.class), "testhomes", new String[] {"10"}));
|
||||||
|
assertTrue(generatedNames.isEmpty());
|
||||||
|
assertEquals(1, scheduledBatches.size());
|
||||||
|
assertFalse(executor.onCommand(sender, mock(Command.class), "testhomes", new String[] {"10"}));
|
||||||
|
|
||||||
|
scheduledBatches.getFirst().run();
|
||||||
|
assertEquals(List.of("Test1"), generatedNames);
|
||||||
|
for (int tick = 1; tick < 10; tick++) {
|
||||||
|
scheduledBatches.getFirst().run();
|
||||||
|
}
|
||||||
|
assertEquals(10, generatedNames.size());
|
||||||
|
|
||||||
|
assertTrue(executor.onCommand(sender, mock(Command.class), "testhomes", new String[] {"10"}));
|
||||||
|
assertEquals(2, scheduledBatches.size());
|
||||||
|
for (int tick = 0; tick < 20; tick++) {
|
||||||
|
scheduledBatches.get(1).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> expectedNames = new ArrayList<>();
|
||||||
|
for (int testNumber = 1; testNumber <= 20; testNumber++) {
|
||||||
|
expectedNames.add("Test" + testNumber);
|
||||||
|
}
|
||||||
|
assertEquals(expectedNames, generatedNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsNonPositiveCountsWithoutGeneratingHomes() {
|
||||||
|
MetropolisPlugin plugin = mock(MetropolisPlugin.class);
|
||||||
|
MetropolisDebugGenerateTestHomesCommand executor =
|
||||||
|
new MetropolisDebugGenerateTestHomesCommand(plugin);
|
||||||
|
|
||||||
|
assertFalse(
|
||||||
|
executor.onCommand(
|
||||||
|
mock(CommandSender.class), mock(Command.class), "testhomes", new String[] {"0"}));
|
||||||
|
|
||||||
|
verify(plugin, never()).generateHome(any(OfflinePlayer.class), eq(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user