Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,4 @@ Thumbs.db
**/resource-config.json
**/.canopy/
**/bin/
**/build/
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.canopy.adapters.libgdx.data.assets

import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.createTempDirectory
import kotlin.io.path.deleteRecursively
import kotlin.io.path.writeText
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import com.badlogic.gdx.files.FileHandle

@OptIn(ExperimentalPathApi::class)
class GdxAssetEntryTests {

private val tempRoots = mutableListOf<java.nio.file.Path>()

@AfterTest
fun cleanup() {
tempRoots.forEach { it.deleteRecursively() }
tempRoots.clear()
}

@Test
fun `entry delegates file properties and read operations`() {
val root = createTempDirectory("canopy-gdx-asset")
tempRoots.add(root)
val file = root.resolve("hello.txt")
file.writeText("hello")

val entry = GdxAssetEntry(FileHandle(file.toFile()))

assertTrue(entry.exists())
assertEquals("hello.txt", entry.name)
assertEquals("txt", entry.extension)
assertFalse(entry.isDirectory)
assertEquals("hello", entry.readText())
assertContentEquals("hello".encodeToByteArray(), entry.readBytes())
assertEquals(file.toFile().path.replace('\\', '/'), entry.path.replace('\\', '/'))
}

@Test
fun `entry writes and lists children`() {
val root = createTempDirectory("canopy-gdx-dir")
tempRoots.add(root)
val child = root.resolve("child.txt")
child.writeText("first")

val directory = GdxAssetEntry(FileHandle(root.toFile()))
val fileEntry = directory.list().single() as GdxAssetEntry
fileEntry.writeText("-second", append = true)

assertTrue(directory.isDirectory)
assertEquals(listOf("child.txt"), directory.list().map { it.name })
assertEquals("first-second", child.toFile().readText())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.canopy.adapters.libgdx.data.assets

import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.backends.headless.HeadlessApplication
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration
import io.canopy.engine.data.assets.FileSource

class GdxAssetsManagerTests {

private var app: HeadlessApplication? = null

@AfterTest
fun cleanup() {
app?.exit()
app = null
}

private fun startHeadless() {
if (app == null) {
app = HeadlessApplication(object : ApplicationAdapter() {}, HeadlessApplicationConfiguration())
}
}

@Test
fun `resolveFile supports absolute files`() {
startHeadless()
val tempFile = kotlin.io.path.createTempFile("canopy-gdx-absolute", ".txt").toFile()
tempFile.writeText("value")

val manager = GdxAssetsManager()
val file = manager.resolveFile(tempFile.absolutePath, FileSource.Absolute)

assertTrue(file.exists())
assertEquals(tempFile.absolutePath.replace('\\', '/'), file.path().replace('\\', '/'))
}

@Test
fun `loadFile returns asset entry and applies custom options`() {
startHeadless()
val tempFile = kotlin.io.path.createTempFile("canopy-gdx-load", ".txt").toFile()
tempFile.writeText("value")

var configuredPath = ""
val asset = GdxAssetsManager().loadFile(tempFile.absolutePath, FileSource.Absolute) {
configuredPath = path
}

assertIs<GdxAssetEntry>(asset)
assertEquals("value", asset.readText())
assertEquals(asset.path, configuredPath)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package io.canopy.adapters.libgdx.input

import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertTrue
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import io.canopy.engine.input.binds.InputBind
import io.mockk.every
import io.mockk.mockk

class GdxInputManagerMappingTests {

private val originalInput = Gdx.input

@AfterTest
fun cleanup() {
Gdx.input = originalInput
}

@Test
fun `all keyboard binds map to a concrete gdx key`() {
val expected = mapOf(
InputBind.A to Input.Keys.A,
InputBind.B to Input.Keys.B,
InputBind.C to Input.Keys.C,
InputBind.D to Input.Keys.D,
InputBind.E to Input.Keys.E,
InputBind.F to Input.Keys.F,
InputBind.G to Input.Keys.G,
InputBind.H to Input.Keys.H,
InputBind.I to Input.Keys.I,
InputBind.J to Input.Keys.J,
InputBind.K to Input.Keys.K,
InputBind.L to Input.Keys.L,
InputBind.M to Input.Keys.M,
InputBind.N to Input.Keys.N,
InputBind.O to Input.Keys.O,
InputBind.P to Input.Keys.P,
InputBind.Q to Input.Keys.Q,
InputBind.R to Input.Keys.R,
InputBind.S to Input.Keys.S,
InputBind.T to Input.Keys.T,
InputBind.U to Input.Keys.U,
InputBind.V to Input.Keys.V,
InputBind.W to Input.Keys.W,
InputBind.X to Input.Keys.X,
InputBind.Y to Input.Keys.Y,
InputBind.Z to Input.Keys.Z,
InputBind.NUM_0 to Input.Keys.NUM_0,
InputBind.NUM_1 to Input.Keys.NUM_1,
InputBind.NUM_2 to Input.Keys.NUM_2,
InputBind.NUM_3 to Input.Keys.NUM_3,
InputBind.NUM_4 to Input.Keys.NUM_4,
InputBind.NUM_5 to Input.Keys.NUM_5,
InputBind.NUM_6 to Input.Keys.NUM_6,
InputBind.NUM_7 to Input.Keys.NUM_7,
InputBind.NUM_8 to Input.Keys.NUM_8,
InputBind.NUM_9 to Input.Keys.NUM_9,
InputBind.LEFT to Input.Keys.LEFT,
InputBind.RIGHT to Input.Keys.RIGHT,
InputBind.UP to Input.Keys.UP,
InputBind.DOWN to Input.Keys.DOWN,
InputBind.SPACE to Input.Keys.SPACE,
InputBind.ENTER to Input.Keys.ENTER,
InputBind.ESCAPE to Input.Keys.ESCAPE,
InputBind.TAB to Input.Keys.TAB,
InputBind.BACKSPACE to Input.Keys.BACKSPACE,
InputBind.INSERT to Input.Keys.INSERT,
InputBind.DELETE to Input.Keys.FORWARD_DEL,
InputBind.HOME to Input.Keys.HOME,
InputBind.END to Input.Keys.END,
InputBind.PAGE_UP to Input.Keys.PAGE_UP,
InputBind.PAGE_DOWN to Input.Keys.PAGE_DOWN,
InputBind.SHIFT_LEFT to Input.Keys.SHIFT_LEFT,
InputBind.SHIFT_RIGHT to Input.Keys.SHIFT_RIGHT,
InputBind.CTRL_LEFT to Input.Keys.CONTROL_LEFT,
InputBind.CTRL_RIGHT to Input.Keys.CONTROL_RIGHT,
InputBind.ALT_LEFT to Input.Keys.ALT_LEFT,
InputBind.ALT_RIGHT to Input.Keys.ALT_RIGHT,
InputBind.META_LEFT to Input.Keys.SYM,
InputBind.META_RIGHT to Input.Keys.SYM,
InputBind.CAPS_LOCK to Input.Keys.CAPS_LOCK,
InputBind.NUM_LOCK to Input.Keys.NUM,
InputBind.SCROLL_LOCK to Input.Keys.SCROLL_LOCK,
InputBind.PRINT_SCREEN to Input.Keys.PRINT_SCREEN,
InputBind.PAUSE to Input.Keys.PAUSE,
InputBind.GRAVE to Input.Keys.GRAVE,
InputBind.MINUS to Input.Keys.MINUS,
InputBind.EQUALS to Input.Keys.EQUALS,
InputBind.LEFT_BRACKET to Input.Keys.LEFT_BRACKET,
InputBind.RIGHT_BRACKET to Input.Keys.RIGHT_BRACKET,
InputBind.BACKSLASH to Input.Keys.BACKSLASH,
InputBind.SEMICOLON to Input.Keys.SEMICOLON,
InputBind.APOSTROPHE to Input.Keys.APOSTROPHE,
InputBind.COMMA to Input.Keys.COMMA,
InputBind.PERIOD to Input.Keys.PERIOD,
InputBind.SLASH to Input.Keys.SLASH,
InputBind.F1 to Input.Keys.F1,
InputBind.F2 to Input.Keys.F2,
InputBind.F3 to Input.Keys.F3,
InputBind.F4 to Input.Keys.F4,
InputBind.F5 to Input.Keys.F5,
InputBind.F6 to Input.Keys.F6,
InputBind.F7 to Input.Keys.F7,
InputBind.F8 to Input.Keys.F8,
InputBind.F9 to Input.Keys.F9,
InputBind.F10 to Input.Keys.F10,
InputBind.F11 to Input.Keys.F11,
InputBind.F12 to Input.Keys.F12,
InputBind.NUMPAD_0 to Input.Keys.NUMPAD_0,
InputBind.NUMPAD_1 to Input.Keys.NUMPAD_1,
InputBind.NUMPAD_2 to Input.Keys.NUMPAD_2,
InputBind.NUMPAD_3 to Input.Keys.NUMPAD_3,
InputBind.NUMPAD_4 to Input.Keys.NUMPAD_4,
InputBind.NUMPAD_5 to Input.Keys.NUMPAD_5,
InputBind.NUMPAD_6 to Input.Keys.NUMPAD_6,
InputBind.NUMPAD_7 to Input.Keys.NUMPAD_7,
InputBind.NUMPAD_8 to Input.Keys.NUMPAD_8,
InputBind.NUMPAD_9 to Input.Keys.NUMPAD_9,
InputBind.NUMPAD_ADD to Input.Keys.NUMPAD_ADD,
InputBind.NUMPAD_SUBTRACT to Input.Keys.NUMPAD_SUBTRACT,
InputBind.NUMPAD_MULTIPLY to Input.Keys.NUMPAD_MULTIPLY,
InputBind.NUMPAD_DIVIDE to Input.Keys.NUMPAD_DIVIDE,
InputBind.NUMPAD_DECIMAL to Input.Keys.NUMPAD_DOT,
InputBind.NUMPAD_ENTER to Input.Keys.NUMPAD_ENTER
)

val input = mockk<Input>()
every { input.isKeyPressed(any()) } answers
{ firstArg<Int>() == Input.Keys.UNKNOWN || firstArg<Int>() in expected.values }
every { input.isButtonPressed(any()) } returns false
Gdx.input = input

val manager = GdxInputManager()
expected.keys.forEach { bind ->
assertTrue(manager.isPressed(bind), "Expected $bind to map to a queried GDX key")
}
}

@Test
fun `all mouse binds map to a concrete gdx button`() {
val expected = mapOf(
InputBind.LEFT_MOUSE to Input.Buttons.LEFT,
InputBind.RIGHT_MOUSE to Input.Buttons.RIGHT,
InputBind.MIDDLE_MOUSE to Input.Buttons.MIDDLE,
InputBind.BACK_MOUSE to Input.Buttons.BACK,
InputBind.FORWARD_MOUSE to Input.Buttons.FORWARD
)

val input = mockk<Input>()
every { input.isButtonPressed(any()) } answers { firstArg<Int>() in expected.values }
every { input.isKeyPressed(any()) } returns false
Gdx.input = input

val manager = GdxInputManager()
expected.keys.forEach { bind ->
assertTrue(manager.isPressed(bind), "Expected $bind to map to a queried GDX button")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.canopy.adapters.libgdx.input

import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import io.canopy.engine.input.binds.InputBind
import io.mockk.every
import io.mockk.mockk

class GdxInputManagerTests {

private val originalInput = Gdx.input

@AfterTest
fun cleanup() {
Gdx.input = originalInput
}

@Test
fun `keyboard binds delegate to Gdx key polling`() {
val input = mockk<Input>()
every { input.isKeyPressed(Input.Keys.SPACE) } returns true
every { input.isKeyPressed(Input.Keys.FORWARD_DEL) } returns false
every { input.isButtonPressed(any()) } returns false
Gdx.input = input

val manager = GdxInputManager()

assertTrue(manager.isPressed(InputBind.SPACE))
assertFalse(manager.isPressed(InputBind.DELETE))
}

@Test
fun `mouse binds delegate to Gdx button polling`() {
val input = mockk<Input>()
every { input.isButtonPressed(Input.Buttons.RIGHT) } returns true
every { input.isButtonPressed(Input.Buttons.MIDDLE) } returns false
every { input.isKeyPressed(any()) } returns false
Gdx.input = input

val manager = GdxInputManager()

assertTrue(manager.isPressed(InputBind.RIGHT_MOUSE))
assertFalse(manager.isPressed(InputBind.MIDDLE_MOUSE))
}
}
28 changes: 27 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,27 @@ plugins {
base
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.kover)
alias(libs.plugins.ktlint) apply false
}

group = "io.canopy"
version = canopyVersion

dependencies {
kover(projects.engine)
kover(projects.adapters.libgdx)
kover(projects.platforms.headless)
kover(projects.platforms.terminal)
kover(projects.tooling.devtools)
kover(projects.tooling.utils)
}

allprojects {
apply(plugin = "eclipse")
apply(plugin = "idea")

group = "io.canopy"
group = "io.github.canopy"
version = canopyVersion

extensions.configure<IdeaModel> {
Expand All @@ -36,6 +46,8 @@ allprojects {
}

subprojects {
apply(plugin = "org.jetbrains.kotlinx.kover")

plugins.withType<BasePlugin> {
extensions.configure<BasePluginExtension>("base") {
archivesName.set(project.path.removePrefix(":").replace(":", "-"))
Expand Down Expand Up @@ -87,6 +99,20 @@ subprojects {
tasks.withType(Test::class.java).configureEach {
useJUnitPlatform()
}

}

kover {
reports {
total {
html {
onCheck = true
}
xml {
onCheck = true
}
}
}
}

extensions.configure<EclipseModel> {
Expand Down
Loading
Loading