Add param type test for primitive types and fix things

- Made subcommand list sorted
- Simplified test command registration and running commands
- Fixed primitive type handling where I missed it
This commit is contained in:
Norbi Peti 2023-07-28 00:10:51 +02:00
parent f18c5483d8
commit 79c1cc47f7
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
6 changed files with 75 additions and 44 deletions

View file

@ -219,7 +219,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
if (params.isEmpty() || getArgNodes(node, params.toMutableList())) {
node.executes(::executeCommand)
}
return node.build().coreExecutable() ?: throw IllegalStateException("Command node should be executable but isn't: $fullPath")
return node.build().coreExecutable() ?: error("Command node should be executable but isn't: $fullPath")
}
/**
@ -296,19 +296,21 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
val (lowerLimit, upperLimit) = arg.limits
return if (arg.greedy) StringArgumentType.greedyString()
else if (ptype == String::class.java) StringArgumentType.word()
else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.java
|| ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.java
|| ptype == Short::class.javaPrimitiveType || ptype == Short::class.java
else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.javaObjectType
|| ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.javaObjectType
|| ptype == Short::class.javaPrimitiveType || ptype == Short::class.javaObjectType
)
IntegerArgumentType.integer(lowerLimit.toInt(), upperLimit.toInt())
else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.java)
else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.javaObjectType)
LongArgumentType.longArg(lowerLimit.toLong(), upperLimit.toLong())
else if (ptype == Float::class.javaPrimitiveType || ptype == Float::class.java)
else if (ptype == Float::class.javaPrimitiveType || ptype == Float::class.javaObjectType)
FloatArgumentType.floatArg(lowerLimit.toFloat(), upperLimit.toFloat())
else if (ptype == Double::class.javaPrimitiveType || ptype == Double::class.java)
else if (ptype == Double::class.javaPrimitiveType || ptype == Double::class.javaObjectType)
DoubleArgumentType.doubleArg(lowerLimit, upperLimit)
else if (ptype == Char::class.javaPrimitiveType || ptype == Char::class.java) StringArgumentType.word()
else if (ptype == Boolean::class.javaPrimitiveType || ptype == Boolean::class.java) BoolArgumentType.bool()
else if (ptype == Char::class.javaPrimitiveType || ptype == Char::class.javaObjectType)
StringArgumentType.word()
else if (ptype == Boolean::class.javaPrimitiveType || ptype == Boolean::class.javaObjectType)
BoolArgumentType.bool()
else StringArgumentType.word()
}
@ -320,10 +322,10 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return Vanilla command success level (0)
*/
private fun executeHelpText(context: CommandContext<TP>): Int {
val node = context.nodes.lastOrNull()?.node ?: throw IllegalStateException()
val helpText = node.subcommandDataNoOp()?.getHelpText(context.source) ?: throw IllegalStateException()
val node = context.nodes.lastOrNull()?.node ?: error("No nodes found when executing help text for ${context.input}!")
val helpText = node.subcommandDataNoOp()?.getHelpText(context.source) ?: error("No subcommand data found when executing help text for ${context.input}")
if (node.isCommand()) {
val subs = getSubcommands(node.coreCommandNoOp()!!).map { commandChar + it.data.fullPath }
val subs = getSubcommands(node.coreCommandNoOp()!!).map { commandChar + it.data.fullPath }.sorted()
context.source.sendMessage(helpText + "${ChatColor.GOLD}---- Subcommands ----" + subs)
}
return 0
@ -336,7 +338,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return Vanilla command success level (0)
*/
protected open fun executeCommand(context: CommandContext<TP>): Int {
val sd = context.nodes.lastOrNull()?.node?.subcommandData<_, TC>() ?: throw IllegalStateException("Could not find suitable command node for command ${context.input}")
val sd = context.nodes.lastOrNull()?.node?.subcommandData<_, TC>() ?: error("Could not find suitable command node for command ${context.input}")
val sender = context.source
if (!sd.hasPermission(sender)) {
@ -348,7 +350,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
val convertedSender = convertSenderType(sender, sd.senderType)
if (convertedSender == null) {
//TODO: Should have a prettier display of Command2 classes here
val type = sd.senderType.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
val type = sd.senderType.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }.trim()
sender.sendMessage("${ChatColor.RED}You need to be a $type to use this command.")
executeHelpText(context) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
return 0
@ -373,10 +375,14 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
} else {
val userArgument = context.getArgument(argument.name, String::class.java)
val converter = paramConverters[argument.type]?.converter
?: throw IllegalStateException("No suitable converter found for ${argument.type} ${argument.name}")
?: error("No suitable converter found for ${argument.type} ${argument.name}")
params.add(converter.apply(userArgument))
}
} catch (e: IllegalArgumentException) {
if (ChromaUtils.isTest && e.message?.contains("No such argument '${argument.name}' exists on this command") != true) {
println("For command ${sd.fullPath}:")
e.printStackTrace()
}
if (argument.optional) {
params.add(argument.type.getDefaultForEasilyRepresentable())
} else {

View file

@ -279,7 +279,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
for (suggestion in suggestions) sbuilder.suggest(suggestion)
}
}
if (argData.type === Boolean::class.javaPrimitiveType || argData.type === Boolean::class.java)
if (argData.type === Boolean::class.javaPrimitiveType || argData.type === Boolean::class.javaObjectType)
sbuilder.suggest("true").suggest("false")
val loweredInput = sbuilder.remaining.lowercase(Locale.getDefault())
// The list is automatically ordered, so we need to put the <param> at the end after that

View file

@ -7,16 +7,16 @@ import buttondevteam.lib.architecture.Component
abstract class ICommand2MC : ICommand2<Command2MCSender>(command2MC) {
private var _plugin: ButtonPlugin? = null
var plugin: ButtonPlugin
get() = _plugin ?: throw IllegalStateException("The command is not registered to a Button plugin!")
get() = _plugin ?: error("The command is not registered to a Button plugin!")
private set(value) {
if (_plugin != null) throw IllegalStateException("The command is already assigned to a Button plugin!")
if (_plugin != null) error("The command is already assigned to a Button plugin!")
_plugin = value
}
private var _component: Component<*>? = null
var component: Component<*>?
get() = _component
private set(value) {
if (_component != null) throw IllegalStateException("The command is already assigned to a component!")
if (_component != null) error("The command is already assigned to a component!")
_component = value
}

View file

@ -6,6 +6,7 @@ import buttondevteam.lib.chat.Command2MCSender
import buttondevteam.lib.chat.CommandClass
import buttondevteam.lib.chat.ICommand2MC
import buttondevteam.lib.player.TBMCPlayer
import org.bukkit.OfflinePlayer
abstract class Command2MCCommands {
@CommandClass(helpText = ["Test command", "Used for testing"])
@ -97,6 +98,16 @@ abstract class Command2MCCommands {
}
}
@CommandClass
object TestParamsCommand : ICommand2MC(), ITestCommand2MC {
override var testCommandReceived: String? = null
@Command2.Subcommand
fun def(sender: OfflinePlayer, testInt: Int?, testLong: Long?, testDouble: Double?, testDouble2: Double) {
testCommandReceived = "$testInt $testLong $testDouble $testDouble2 ${sender.name}"
}
}
interface ITestCommand2MC {
var testCommandReceived: String?
}

View file

@ -5,7 +5,9 @@ import buttondevteam.core.MainPlugin
import buttondevteam.core.component.channel.Channel
import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.chat.Command2MCSender
import buttondevteam.lib.chat.ICommand2MC
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
import buttondevteam.lib.chat.test.Command2MCCommands.*
import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.player.TBMCPlayer
import org.junit.jupiter.api.MethodOrderer
@ -34,22 +36,24 @@ class Command2MCTest {
@Test
@Order(2)
fun testRegisterCommand() {
MainPlugin.instance.registerCommand(Command2MCCommands.TestCommand)
TestCommand.register()
val nodes = ButtonPlugin.command2MC.commandNodes
assert(nodes.size == 1)
assert(nodes.first().literal == "test")
val coreExecutable = nodes.first().coreExecutable<Command2MCSender, Command2MCCommands.TestCommand>()
assertEquals(Command2MCCommands.TestCommand::class.qualifiedName, coreExecutable?.data?.command?.let { it::class.qualifiedName }, "The command class name doesn't match or command is null")
val coreExecutable = nodes.first().coreExecutable<Command2MCSender, TestCommand>()
assertEquals(TestCommand::class.qualifiedName, coreExecutable?.data?.command?.let { it::class.qualifiedName }, "The command class name doesn't match or command is null")
assertEquals("test", coreExecutable?.data?.argumentsInOrder?.firstOrNull()?.name, "Failed to get correct argument name")
assertEquals(String::class.java, coreExecutable?.data?.arguments?.get("test")?.type, "The argument could not be found or type doesn't match")
assertEquals(Command2MCSender::class.java, coreExecutable?.data?.senderType, "The sender's type doesn't seem to be stored correctly")
MainPlugin.instance.registerCommand(Command2MCCommands.NoArgTestCommand)
assertEquals("No sender parameter for method '${Command2MCCommands.ErroringTestCommand::class.java.getMethod("def")}'", assertFails { MainPlugin.instance.registerCommand(Command2MCCommands.ErroringTestCommand) }.message)
MainPlugin.instance.registerCommand(Command2MCCommands.MultiArgTestCommand)
NoArgTestCommand.register()
val errCmd = ErroringTestCommand
assertEquals("No sender parameter for method '${errCmd::class.java.getMethod("def")}'", assertFails { ErroringTestCommand.register() }.message)
MultiArgTestCommand.register()
MainPlugin.instance.registerCommand(Command2MCCommands.TestNoMainCommand1)
MainPlugin.instance.registerCommand(Command2MCCommands.TestNoMainCommand2)
TestNoMainCommand1.register()
TestNoMainCommand2.register()
TestParamsCommand.register()
}
@Test
@ -95,20 +99,20 @@ class Command2MCTest {
return messageReceived
}
}
runCommand(sender, "/test hmm", Command2MCCommands.TestCommand, "hmm")
runCommand(sender, "/noargtest", Command2MCCommands.NoArgTestCommand, "TestPlayer")
sender.runCommand("/test hmm", TestCommand, "hmm")
sender.runCommand("/noargtest", NoArgTestCommand, "TestPlayer")
assertFails { ButtonPlugin.command2MC.handleCommand(sender, "/noargtest failing") }
runFailingCommand(sender, "/erroringtest")
runCommand(sender, "/multiargtest test hmm mhm", Command2MCCommands.MultiArgTestCommand, "hmmmhm")
runCommand(sender, "/multiargtest test2 true 19", Command2MCCommands.MultiArgTestCommand, "true 19")
sender.runCommand("/multiargtest test hmm mhm", MultiArgTestCommand, "hmmmhm")
sender.runCommand("/multiargtest test2 true 19", MultiArgTestCommand, "true 19")
runCommand(sender, "/multiargtest testoptional", Command2MCCommands.MultiArgTestCommand, "false")
runCommand(sender, "/multiargtest testoptional true", Command2MCCommands.MultiArgTestCommand, "true")
runCommand(sender, "/multiargtest testoptionalmulti true teszt", Command2MCCommands.MultiArgTestCommand, "true teszt")
runCommand(sender, "/multiargtest testoptionalmulti true", Command2MCCommands.MultiArgTestCommand, "true null")
runCommand(sender, "/multiargtest testoptionalmulti", Command2MCCommands.MultiArgTestCommand, "false null")
sender.runCommand("/multiargtest testoptional", MultiArgTestCommand, "false")
sender.runCommand("/multiargtest testoptional true", MultiArgTestCommand, "true")
sender.runCommand("/multiargtest testoptionalmulti true teszt", MultiArgTestCommand, "true teszt")
sender.runCommand("/multiargtest testoptionalmulti true", MultiArgTestCommand, "true null")
sender.runCommand("/multiargtest testoptionalmulti", MultiArgTestCommand, "false null")
runCommand(sender, "/test plugin Chroma-Core", Command2MCCommands.TestCommand, "Chroma-Core")
sender.runCommand("/test plugin Chroma-Core", TestCommand, "Chroma-Core")
assertFails { ButtonPlugin.command2MC.handleCommand(sender, "/test playerfail TestPlayer") }
assertEquals("Test command\n" +
@ -117,16 +121,22 @@ class Command2MCTest {
"/test playerfail\n" +
"/test plugin", sender.withMessageReceive { ButtonPlugin.command2MC.handleCommand(sender, "/test") })
runCommand(sender, "/some test cmd", Command2MCCommands.TestNoMainCommand1, "TestPlayer")
runCommand(sender, "/some another cmd", Command2MCCommands.TestNoMainCommand2, "TestPlayer")
sender.runCommand("/some test cmd", TestNoMainCommand1, "TestPlayer")
sender.runCommand("/some another cmd", TestNoMainCommand2, "TestPlayer")
assertEquals("§6---- Subcommands ----\n" +
"/some test cmd\n" +
"/some another cmd", sender.withMessageReceive { ButtonPlugin.command2MC.handleCommand(sender, "/some") })
"/some another cmd\n" +
"/some test cmd", sender.withMessageReceive { ButtonPlugin.command2MC.handleCommand(sender, "/some") })
sender.runCommand("/testparams 12 34 56 78", TestParamsCommand, "12 34 56.0 78.0 Player0")
}
private fun runCommand(sender: Command2MCSender, command: String, obj: Command2MCCommands.ITestCommand2MC, expected: String) {
assert(ButtonPlugin.command2MC.handleCommand(sender, command)) { "Could not find command $command" }
private fun ICommand2MC.register() {
MainPlugin.instance.registerCommand(this)
}
private fun Command2MCSender.runCommand(command: String, obj: ITestCommand2MC, expected: String) {
assert(ButtonPlugin.command2MC.handleCommand(this, command)) { "Could not find command $command" }
assertEquals(expected, obj.testCommandReceived)
}

View file

@ -2,7 +2,7 @@ buttondevteam:
lib:
chat:
test:
Command2MCTest:
Command2MCCommands:
TestCommand:
def:
method: def()
@ -33,6 +33,10 @@ buttondevteam:
def:
method: def()
params: ''
TestParamsCommand:
def:
method: def()
params: 'testInt testLong testDouble testDouble2'
core:
ComponentCommand:
def: