commit
ebab533e08
@ -0,0 +1,36 @@
|
|||||||
|
name: Gradle Unit Tests
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop, actions, feature/unit-tests ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop, actions, feature/unit-tests ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Cache build data
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('build.gradle') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: chmod +x gradlew && ./gradlew test
|
||||||
|
|
||||||
|
- name: Cleanup Gradle Cache
|
||||||
|
run: |
|
||||||
|
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
rm -f ~/.gradle/caches/modules-2/gc.properties
|
@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
PLUGIN_VERSION=1.3.4
|
PLUGIN_VERSION=1.4.0
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.trivernis.chunkmaster.commands
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import net.trivernis.chunkmaster.lib.Subcommand
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
|
class CmdCompleted(private val plugin: Chunkmaster) : Subcommand {
|
||||||
|
override val name = "completed"
|
||||||
|
|
||||||
|
override fun execute(sender: CommandSender, args: List<String>): Boolean {
|
||||||
|
plugin.sqliteManager.completedGenerationTasks.getCompletedTasks().thenAccept { tasks ->
|
||||||
|
val worlds = tasks.map { it.world }.toHashSet()
|
||||||
|
var response = "\n" + plugin.langManager.getLocalized("COMPLETED_TASKS_HEADER") + "\n\n"
|
||||||
|
|
||||||
|
for (world in worlds) {
|
||||||
|
response += plugin.langManager.getLocalized("COMPLETED_WORLD_HEADER", world) + "\n"
|
||||||
|
|
||||||
|
for (task in tasks.filter { it.world == world }) {
|
||||||
|
response += plugin.langManager.getLocalized(
|
||||||
|
"COMPLETED_TASK_ENTRY",
|
||||||
|
task.id,
|
||||||
|
task.radius,
|
||||||
|
task.center.x,
|
||||||
|
task.center.z,
|
||||||
|
task.shape
|
||||||
|
) + "\n"
|
||||||
|
}
|
||||||
|
response += "\n"
|
||||||
|
}
|
||||||
|
sender.sendMessage(response)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
alias: String,
|
||||||
|
args: List<String>
|
||||||
|
): MutableList<String> {
|
||||||
|
return mutableListOf()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Better argument parser for command arguments
|
||||||
|
*/
|
||||||
|
class ArgParser {
|
||||||
|
private var input = ""
|
||||||
|
private var position = 0
|
||||||
|
private var currentChar = ' '
|
||||||
|
private var escaped = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses arguments from a string and respects quotes
|
||||||
|
*/
|
||||||
|
fun parseArguments(arguments: String): List<String> {
|
||||||
|
if (arguments.isEmpty()) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
input = arguments
|
||||||
|
position = 0
|
||||||
|
currentChar = input[position]
|
||||||
|
escaped = false
|
||||||
|
val args = ArrayList<String>()
|
||||||
|
var arg = ""
|
||||||
|
|
||||||
|
while (!endReached()) {
|
||||||
|
nextCharacter()
|
||||||
|
|
||||||
|
if (currentChar == '\\' && !escaped) {
|
||||||
|
escaped = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChar.isWhitespace() && !escaped) {
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = ""
|
||||||
|
} else if (currentChar == '"' && !escaped) {
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = parseString()
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
arg = ""
|
||||||
|
} else {
|
||||||
|
arg += currentChar
|
||||||
|
}
|
||||||
|
escaped = false
|
||||||
|
}
|
||||||
|
if (arg.isNotBlank()) {
|
||||||
|
args.add(arg)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an enquoted string
|
||||||
|
*/
|
||||||
|
private fun parseString(): String {
|
||||||
|
var output = ""
|
||||||
|
|
||||||
|
while (!endReached()) {
|
||||||
|
nextCharacter()
|
||||||
|
|
||||||
|
if (currentChar == '\\') {
|
||||||
|
escaped = !escaped
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChar == '"' && !escaped) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
output += currentChar
|
||||||
|
escaped = false
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nextCharacter() {
|
||||||
|
if (!endReached()) {
|
||||||
|
currentChar = input[position++]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun endReached(): Boolean {
|
||||||
|
return position >= input.length
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
|
||||||
|
data class CompletedGenerationTask(
|
||||||
|
val id: Int,
|
||||||
|
val world: String,
|
||||||
|
val radius: Int,
|
||||||
|
val center: ChunkCoordinates,
|
||||||
|
val shape: String
|
||||||
|
)
|
@ -0,0 +1,80 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.database
|
||||||
|
|
||||||
|
import net.trivernis.chunkmaster.lib.generation.ChunkCoordinates
|
||||||
|
import java.sql.ResultSet
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class CompletedGenerationTasks(private val sqliteManager: SqliteManager) {
|
||||||
|
/**
|
||||||
|
* Returns the list of all completed tasks
|
||||||
|
*/
|
||||||
|
fun getCompletedTasks(): CompletableFuture<List<CompletedGenerationTask>> {
|
||||||
|
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
|
||||||
|
|
||||||
|
sqliteManager.executeStatement("SELECT * FROM completed_generation_tasks", HashMap()) { res ->
|
||||||
|
val tasks = ArrayList<CompletedGenerationTask>()
|
||||||
|
|
||||||
|
while (res!!.next()) {
|
||||||
|
tasks.add(mapSqlResponseToWrapperObject(res))
|
||||||
|
}
|
||||||
|
completableFuture.complete(tasks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of completed tasks for a world
|
||||||
|
*/
|
||||||
|
fun getCompletedTasksForWorld(world: String): CompletableFuture<List<CompletedGenerationTask>> {
|
||||||
|
val completableFuture = CompletableFuture<List<CompletedGenerationTask>>()
|
||||||
|
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"SELECT * FROM completed_generation_tasks WHERE world = ?",
|
||||||
|
hashMapOf(1 to world)
|
||||||
|
) { res ->
|
||||||
|
val tasks = ArrayList<CompletedGenerationTask>()
|
||||||
|
|
||||||
|
while (res!!.next()) {
|
||||||
|
tasks.add(mapSqlResponseToWrapperObject(res))
|
||||||
|
}
|
||||||
|
completableFuture.complete(tasks)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapSqlResponseToWrapperObject(res: ResultSet): CompletedGenerationTask {
|
||||||
|
val id = res.getInt("id")
|
||||||
|
val world = res.getString("world")
|
||||||
|
val center = ChunkCoordinates(res.getInt("center_x"), res.getInt("center_z"))
|
||||||
|
val radius = res.getInt("completed_radius")
|
||||||
|
val shape = res.getString("shape")
|
||||||
|
return CompletedGenerationTask(id, world, radius, center, shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a completed task
|
||||||
|
*/
|
||||||
|
fun addCompletedTask(
|
||||||
|
id: Int,
|
||||||
|
world: String,
|
||||||
|
radius: Int,
|
||||||
|
center: ChunkCoordinates,
|
||||||
|
shape: String
|
||||||
|
): CompletableFuture<Void> {
|
||||||
|
val completableFuture = CompletableFuture<Void>()
|
||||||
|
sqliteManager.executeStatement(
|
||||||
|
"INSERT INTO completed_generation_tasks (id, world, completed_radius, center_x, center_z, shape) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
|
hashMapOf(
|
||||||
|
1 to id,
|
||||||
|
2 to world,
|
||||||
|
3 to radius,
|
||||||
|
4 to center.x,
|
||||||
|
5 to center.z,
|
||||||
|
6 to shape,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
completableFuture.complete(null)
|
||||||
|
}
|
||||||
|
return completableFuture
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ArgParserTest {
|
||||||
|
var argParser = ArgParser()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses arguments`() {
|
||||||
|
argParser.parseArguments("first second third forth").shouldBe(listOf("first", "second", "third", "forth"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it handles escaped sequences`() {
|
||||||
|
argParser.parseArguments("first second\\ pt2 third").shouldBe(listOf("first", "second pt2", "third"))
|
||||||
|
argParser.parseArguments("first \"second\\\" part 2\" third")
|
||||||
|
.shouldBe(listOf("first", "second\" part 2", "third"))
|
||||||
|
argParser.parseArguments("first \\\\second third").shouldBe(listOf("first", "\\second", "third"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses quoted arguments as one argument`() {
|
||||||
|
argParser.parseArguments("first \"second with space\" third")
|
||||||
|
.shouldBe(listOf("first", "second with space", "third"))
|
||||||
|
argParser.parseArguments("\"first\" \"second\" \"third\"").shouldBe(listOf("first", "second", "third"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses single arguments`() {
|
||||||
|
argParser.parseArguments("one").shouldBe(listOf("one"))
|
||||||
|
argParser.parseArguments("\"one\"").shouldBe(listOf("one"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses no arguments`() {
|
||||||
|
argParser.parseArguments("").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses just whitespace as no arguments`() {
|
||||||
|
argParser.parseArguments(" ").shouldBe(emptyList())
|
||||||
|
argParser.parseArguments("\t\t").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it parses arguments with weird whitespace`() {
|
||||||
|
argParser.parseArguments(" first second \t third \n forth ")
|
||||||
|
.shouldBe(listOf("first", "second", "third", "forth"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it deals predictable with malformed input`() {
|
||||||
|
argParser.parseArguments("first \"second third fourth").shouldBe(listOf("first", "second third fourth"))
|
||||||
|
argParser.parseArguments("\"first second \"third\" fourth")
|
||||||
|
.shouldBe(listOf("first second ", "third", " fourth"))
|
||||||
|
argParser.parseArguments("first second third fourth\"").shouldBe(listOf("first", "second", "third", "fourth"))
|
||||||
|
argParser.parseArguments("\"").shouldBe(emptyList())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib
|
||||||
|
|
||||||
|
import io.kotest.matchers.string.shouldNotBeEmpty
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import net.trivernis.chunkmaster.Chunkmaster
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class LanguageManagerTest {
|
||||||
|
private var langManager: LanguageManager
|
||||||
|
|
||||||
|
init {
|
||||||
|
val plugin = mockk<Chunkmaster>()
|
||||||
|
val config = mockk<FileConfiguration>()
|
||||||
|
|
||||||
|
every { plugin.dataFolder } returns createTempDir()
|
||||||
|
every { plugin.config } returns config
|
||||||
|
every { config.getString("language") } returns "en"
|
||||||
|
|
||||||
|
langManager = LanguageManager(plugin)
|
||||||
|
langManager.loadProperties()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns localized for a key`() {
|
||||||
|
langManager.getLocalized("NOT_PAUSED").shouldNotBeEmpty()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import io.kotest.matchers.booleans.shouldBeTrue
|
||||||
|
import io.kotest.matchers.collections.shouldContainAll
|
||||||
|
import io.kotest.matchers.doubles.shouldBeBetween
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
|
||||||
|
class CircleTest {
|
||||||
|
private val circle = Circle(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
circle.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it generates coordinates`() {
|
||||||
|
circle.next().shouldBe(Pair(0, 0))
|
||||||
|
circle.next().shouldBe(Pair(-1, -1))
|
||||||
|
circle.next().shouldBe(Pair(1, 0))
|
||||||
|
circle.next().shouldBe(Pair(-1, 0))
|
||||||
|
circle.next().shouldBe(Pair(1, -1))
|
||||||
|
circle.next().shouldBe(Pair(-1, 1))
|
||||||
|
circle.next().shouldBe(Pair(0, 1))
|
||||||
|
circle.next().shouldBe(Pair(0, -1))
|
||||||
|
circle.next().shouldBe(Pair(1, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports when reaching the end`() {
|
||||||
|
for (i in 1..25) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.endReached().shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports the radius`() {
|
||||||
|
for (i in 1..9) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.currentRadius().shouldBe(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the right edges`() {
|
||||||
|
circle.getShapeEdgeLocations().shouldContainAll(
|
||||||
|
listOf(
|
||||||
|
Pair(2, -1),
|
||||||
|
Pair(2, 0),
|
||||||
|
Pair(2, 1),
|
||||||
|
Pair(1, 2),
|
||||||
|
Pair(0, 2),
|
||||||
|
Pair(-1, 2),
|
||||||
|
Pair(-2, 1),
|
||||||
|
Pair(-2, 0),
|
||||||
|
Pair(-2, -1),
|
||||||
|
Pair(-1, -2),
|
||||||
|
Pair(0, -2),
|
||||||
|
Pair(1, -2),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the progress`() {
|
||||||
|
circle.progress(2).shouldBe(0)
|
||||||
|
for (i in 1..7) {
|
||||||
|
circle.next()
|
||||||
|
}
|
||||||
|
circle.progress(2).shouldBeBetween(.5, .8, .0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package net.trivernis.chunkmaster.lib.shapes
|
||||||
|
|
||||||
|
import io.kotest.matchers.booleans.shouldBeTrue
|
||||||
|
import io.kotest.matchers.collections.shouldContainAll
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
|
||||||
|
class SquareTest {
|
||||||
|
|
||||||
|
private val square = Square(center = Pair(0, 0), radius = 2, start = Pair(0, 0))
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
square.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it generates coordinates`() {
|
||||||
|
square.next().shouldBe(Pair(0, 0))
|
||||||
|
square.next().shouldBe(Pair(0, 1))
|
||||||
|
square.next().shouldBe(Pair(1, 1))
|
||||||
|
square.next().shouldBe(Pair(1, 0))
|
||||||
|
square.next().shouldBe(Pair(1, -1))
|
||||||
|
square.next().shouldBe(Pair(0, -1))
|
||||||
|
square.next().shouldBe(Pair(-1, -1))
|
||||||
|
square.next().shouldBe(Pair(-1, 0))
|
||||||
|
square.next().shouldBe(Pair(-1, 1))
|
||||||
|
square.next().shouldBe(Pair(-1, 2))
|
||||||
|
square.next().shouldBe(Pair(0, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports when reaching the end`() {
|
||||||
|
for (i in 1..25) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.endReached().shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it reports the radius`() {
|
||||||
|
for (i in 1..9) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.currentRadius().shouldBe(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the right edges`() {
|
||||||
|
square.getShapeEdgeLocations().shouldContainAll(listOf(Pair(2, 2), Pair(-2, 2), Pair(2, -2), Pair(-2, -2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `it returns the progress`() {
|
||||||
|
square.progress(2).shouldBe(0)
|
||||||
|
for (i in 1..8) {
|
||||||
|
square.next()
|
||||||
|
}
|
||||||
|
square.progress(2).shouldBe(0.5)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue