Last active
January 5, 2025 13:04
-
-
Save dellisd/a1df42787d42b41cd3ce16f573984674 to your computer and use it in GitHub Desktop.
Kotlin Multiplatform test resources
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Part of a multi-module project, hence the rootProject everywhere | |
task copyTestResourcesForJs(type: Copy) { | |
from "$projectDir/src/commonTest/resources" | |
into "${rootProject.buildDir}/js/packages/${rootProject.name}-${project.name}-test/src/commonTest/resources" | |
} | |
jsNodeTest.dependsOn copyTestResourcesForJs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Common | |
const val RESOURCE_PATH = "./src/commonTest/resources" | |
expect class Resource(name: String) { | |
val name: String | |
fun exists(): Boolean | |
fun readText(): String | |
} | |
// JVM | |
import java.io.File | |
actual class Resource actual constructor(actual val name: String) { | |
private val file = File("$RESOURCE_PATH/$name") | |
actual fun exists(): Boolean = file.exists() | |
actual fun readText(): String = file.readText() | |
} | |
// Native | |
import kotlinx.cinterop.* | |
import platform.posix.* | |
actual class Resource actual constructor(actual val name: String) { | |
private val file: CPointer<FILE>? = fopen("$RESOURCE_PATH/$name", "r") | |
actual fun exists(): Boolean = file != null | |
actual fun readText(): String { | |
fseek(file, 0, SEEK_END) | |
val size = ftell(file) | |
rewind(file) | |
return memScoped { | |
val tmp = allocArray<ByteVar>(size) | |
fread(tmp, sizeOf<ByteVar>().convert(), size.convert(), file) | |
tmp.toKString() | |
} | |
} | |
} | |
// JS (Node) | |
private external fun require(module: String): dynamic | |
private val fs = require("fs") | |
actual class Resource actual constructor(actual val name: String) { | |
private val path = "$RESOURCE_PATH/$name" | |
actual fun exists(): Boolean = fs.existsSync(path) as Boolean | |
actual fun readText(): String = fs.readFileSync(path, "utf8") as String | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test | |
fun testLength2() { | |
// Located at /src/commonTest/resources/test.json | |
val resource = Resource("test.json") | |
val geometry = resource.readText().toGeometry<LineString>() | |
assertEquals(42.560767589197006, length(geometry, Units.Kilometers)) | |
} |
That's true. This was built for use in JVM, Native (non-iOS/watchOS/tvOS), and Node environments.
Android Instrumentation tests, those other Apple environments, and Browser environments would require some more work, but I suspect they're not impossible.
It didn't work for me while trying to run tests on iOS. This modification did the job:
// Native
import kotlinx.cinterop.*
import platform.posix.*
actual class Resource actual constructor(actual val name: String) {
private val path = NSBundle.mainBundle.pathForResource("resources/FILE_NAME", "FILE_EXTENSION") ?: ""
private val file: CPointer<FILE>? = fopen(path, "r")
Then, in the build.gradle.kts file:
tasks.register<Copy>("copyiOSTestResources") {
from("src/commonTest/resources")
into("build/bin/iosX64/debugTest/resources")
}
tasks.findByName("iosX64Test")!!.dependsOn("copyiOSTestResources")
This article helped me out a lot: https://developer.squareup.com/blog/kotlin-multiplatform-shared-test-resources/
Cannot you simply specify
private val path = "kotlin/$name"
in the JS implementation of the Resource
class's constructor (i.e. without the need for the copyTestResourcesForJs
Gradle task)?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yesterday I played with this script a bit and it didn't really work for iosX64 target. I guess it's more complicated than that, after all it's not run inside an ios filesystem (although on an m1 mac there should be a way).