test command exection, fix execution bugs

This commit is contained in:
Bixilon 2022-05-28 18:13:26 +02:00
parent deed2949a3
commit 574c71f359
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
6 changed files with 188 additions and 13 deletions

8
doc/CLI.md Normal file
View File

@ -0,0 +1,8 @@
# CLI ToDo
- [ ] set default root node to `null`
- [ ] all parsers
- [ ] better suggestions
- [ ] gui suggestions
- [ ] improved entity parser
- [ ] some cli commands

View File

@ -0,0 +1,20 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.commands.errors
import de.bixilon.minosoft.commands.util.CommandReader
class DeadEndError(
reader: CommandReader,
) : ReaderError(reader, reader.pointer, reader.pointer)

View File

@ -35,9 +35,13 @@ abstract class CommandNode(
open fun execute(reader: CommandReader, stack: CommandStack) { open fun execute(reader: CommandReader, stack: CommandStack) {
val pointer = reader.pointer val pointer = reader.pointer
val stackSize = stack.size val stackSize = stack.size
var lastError: Throwable? = null
var highestStack = 0 var childError: Throwable? = null
var errorStack = -1
for (child in children) { for (child in children) {
reader.pointer = pointer
stack.reset(stackSize)
try { try {
executeChild(child, reader, stack) executeChild(child, reader, stack)
if (reader.canPeek()) { if (reader.canPeek()) {
@ -45,18 +49,13 @@ abstract class CommandNode(
} }
return return
} catch (error: Throwable) { } catch (error: Throwable) {
val size = stack.size if (stack.size > errorStack) {
if (size >= highestStack) { errorStack = stack.size
highestStack = size childError = error
lastError = error
} }
} }
reader.pointer = pointer
stack.reset(stackSize)
} }
throw childError ?: return
throw lastError ?: if (reader.canPeek()) throw TrailingTextArgument(reader) else return
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.commands.nodes package de.bixilon.minosoft.commands.nodes
import de.bixilon.minosoft.commands.errors.DeadEndError
import de.bixilon.minosoft.commands.errors.ExpectedArgumentError import de.bixilon.minosoft.commands.errors.ExpectedArgumentError
import de.bixilon.minosoft.commands.stack.CommandExecutor import de.bixilon.minosoft.commands.stack.CommandExecutor
import de.bixilon.minosoft.commands.stack.CommandStack import de.bixilon.minosoft.commands.stack.CommandStack
@ -38,11 +39,12 @@ abstract class ExecutableNode(
} }
override fun execute(reader: CommandReader, stack: CommandStack) { override fun execute(reader: CommandReader, stack: CommandStack) {
reader.skipWhitespaces()
if (!reader.canPeekNext()) { if (!reader.canPeekNext()) {
// empty string // empty string
if (executable) { if (executable) {
return execute(stack) return execute(stack)
} else if (children.isEmpty()) {
throw DeadEndError(reader)
} else { } else {
throw ExpectedArgumentError(reader) throw ExpectedArgumentError(reader)
} }

View File

@ -0,0 +1,111 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.commands.nodes
import de.bixilon.minosoft.commands.errors.DeadEndError
import de.bixilon.minosoft.commands.errors.ExpectedArgumentError
import de.bixilon.minosoft.commands.errors.literal.ExpectedLiteralArgument
import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError
import de.bixilon.minosoft.commands.errors.literal.TrailingTextArgument
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParseError
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser
import de.bixilon.minosoft.commands.stack.CommandStack
import de.bixilon.minosoft.commands.util.CommandReader
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
internal class ExecutionChildReadingTest {
private fun createCommand(): CommandNode {
return RootNode()
.addChild(LiteralNode("0_literal"))
.addChild(
LiteralNode("1_literal")
.addChild(LiteralNode("1_literal_2"))
.addChild(LiteralNode("2_literal_2", executable = true))
)
.addChild(LiteralNode("2_literal", executable = true))
.addChild(
LiteralNode("1_execute")
.addChild(ArgumentNode("args", StringParser(StringParser.StringModes.GREEDY), executable = true))
)
}
@Test
fun testCreation() {
assertTrue(createCommand() is RootNode)
}
@Test
fun testValid() {
val stack = CommandStack()
assertDoesNotThrow { (createCommand().execute(CommandReader("1_execute test"), stack)) }
assertNotNull(stack["1_execute"])
assertEquals(stack["args"], "test")
}
@Test
fun testValid2() {
val stack = CommandStack()
assertDoesNotThrow { (createCommand().execute(CommandReader("1_execute this is a really long valid greedy read string..."), stack)) }
assertNotNull(stack["1_execute"])
assertEquals(stack["args"], "this is a really long valid greedy read string...")
}
@Test
fun testInvalidLiteralValid() {
assertThrows<InvalidLiteralArgumentError> { (createCommand().execute(CommandReader("3_404_not_found"), CommandStack())) }
}
@Test
fun testBlankStringArgument() {
assertThrows<StringParseError> { (createCommand().execute(CommandReader("1_execute "), CommandStack())) }
}
@Test
fun testNoStringArgument() {
assertThrows<ExpectedArgumentError> { (createCommand().execute(CommandReader("1_execute"), CommandStack())) }
}
@Test
fun testDeadEnd() {
assertThrows<DeadEndError> { (createCommand().execute(CommandReader("0_literal"), CommandStack())) }
}
@Test
fun testTrailingData() {
assertThrows<TrailingTextArgument> { (createCommand().execute(CommandReader("0_literal test"), CommandStack())) }
}
@Test
fun testEmpty() {
assertThrows<ExpectedLiteralArgument> { (createCommand().execute(CommandReader(""), CommandStack())) }
}
@Test
fun testTrailingWhitespace() {
assertDoesNotThrow { createCommand().execute(CommandReader("2_literal "), CommandStack()) }
}
@Test
fun test2TrailingWhitespace() {
assertDoesNotThrow { createCommand().execute(CommandReader("1_literal 2_literal_2 "), CommandStack()) }
}
}

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.commands.nodes package de.bixilon.minosoft.commands.nodes
import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError
import de.bixilon.minosoft.commands.errors.literal.TrailingTextArgument
import de.bixilon.minosoft.commands.errors.reader.ExpectedWhitespaceError import de.bixilon.minosoft.commands.errors.reader.ExpectedWhitespaceError
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParseError import de.bixilon.minosoft.commands.parser.brigadier.string.StringParseError
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser
@ -29,7 +30,11 @@ internal class SuggestionChildReadingTest {
private fun createCommand(): CommandNode { private fun createCommand(): CommandNode {
return RootNode() return RootNode()
.addChild(LiteralNode("1_literal")) .addChild(
LiteralNode("1_literal")
.addChild(LiteralNode("1_literal_2"))
.addChild(LiteralNode("2_literal_2"))
)
.addChild(LiteralNode("2_literal")) .addChild(LiteralNode("2_literal"))
.addChild( .addChild(
LiteralNode("1_execute") LiteralNode("1_execute")
@ -76,4 +81,34 @@ internal class SuggestionChildReadingTest {
fun testEmptySuggestions() { fun testEmptySuggestions() {
assertEquals(createCommand().getSuggestions(CommandReader(""), CommandStack()), listOf("1_literal", "2_literal", "1_execute")) assertEquals(createCommand().getSuggestions(CommandReader(""), CommandStack()), listOf("1_literal", "2_literal", "1_execute"))
} }
@Test
fun testTrailingData() {
assertThrows<TrailingTextArgument> { (createCommand().getSuggestions(CommandReader("2_literal test"), CommandStack())) }
}
@Test
fun test2InvalidLiteral() {
assertThrows<InvalidLiteralArgumentError> { (createCommand().getSuggestions(CommandReader("1_literal test"), CommandStack())) }
}
@Test
fun test2EmptyLevelSuggestions() {
assertEquals(createCommand().getSuggestions(CommandReader("1_literal"), CommandStack()), listOf("1_literal_2", "2_literal_2"))
}
@Test
fun test2TrailingData() {
assertThrows<TrailingTextArgument> { (createCommand().getSuggestions(CommandReader("2_literal 1_literal_2 test"), CommandStack())) }
}
@Test
fun testTrailingWhitespace() {
assertEquals(createCommand().getSuggestions(CommandReader("2_literal "), CommandStack()), emptyList())
}
@Test
fun test2TrailingWhitespace() {
assertEquals(createCommand().getSuggestions(CommandReader("1_literal 1_literal_2 "), CommandStack()), emptyList())
}
} }