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
|
||||
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