Unverified Commit 8ef2b12d authored by Hubert Błaszczyk's avatar Hubert Błaszczyk Committed by GitHub

Extract skiko test utils into separate module (#1200)

This PR introduces the extraction of test utilities into a separate
module.

This is a required preparation step for extracting `Skottie` into a
separate module, as it uses some of these test utilities.

Fixes [SKIKO-1136](https://youtrack.jetbrains.com/issue/SKIKO-1136)
parent e39a7c81
......@@ -64,6 +64,9 @@ kotlin {
compilerOptions {
languageVersion.set(KotlinVersion.KOTLIN_2_2)
apiVersion.set(KotlinVersion.KOTLIN_2_2)
freeCompilerArgs.add(
"-opt-in=org.jetbrains.skiko.InternalSkikoApi"
)
}
applyHierarchyTemplate(skikoSourceSetHierarchyTemplate)
......@@ -184,6 +187,7 @@ kotlin {
sourceSets.commonTest.dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-annotations-common"))
implementation(project(":test-utils"))
}
skikoProjectContext.jvmMainSourceSet?.dependencies {
......
......@@ -18,3 +18,4 @@ pluginManagement {
rootProject.name = "skiko"
include("ci")
include("import-generator")
include("test-utils")
......@@ -2,11 +2,12 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skiko.InternalSkikoApi
/**
* Data holds an immutable data buffer.
*/
class Data internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR) {
class Data @InternalSkikoApi constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR) {
/**
* A reference to the underlying memory owner to prevent it from being cleaned by GC until Data instance finalization.
......
package org.jetbrains.skia.impl
import org.jetbrains.skia.ManagedString
import org.jetbrains.skiko.InternalSkikoApi
expect class NativePointer
internal expect class InteropPointer
@InternalSkikoApi
expect class InteropPointer
expect abstract class Native(ptr: NativePointer) {
internal var _ptr: NativePointer
......@@ -21,7 +23,8 @@ internal expect fun reachabilityBarrier(obj: Any?)
internal fun getPtr(n: Native?): NativePointer = n?._ptr ?: Native.NullPointer
internal expect class InteropScope() {
@InternalSkikoApi
expect class InteropScope() {
fun toInterop(string: String?): InteropPointer
fun toInterop(array: ByteArray?): InteropPointer
......@@ -181,7 +184,8 @@ internal inline fun withStringReferenceNullableResult(block: () -> NativePointer
}
internal interface ArrayInteropDecoder<T> {
@InternalSkikoApi
interface ArrayInteropDecoder<T> {
fun getArrayElement(array: InteropPointer, index: Int): T
fun getArraySize(array: InteropPointer): Int
fun disposeArray(array: InteropPointer)
......
package org.jetbrains.skiko
@RequiresOptIn(
level = RequiresOptIn.Level.ERROR,
message =
"This is internal API for Skiko modules that may change frequently " +
"and without warning.",
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.TYPEALIAS,
)
@Retention(AnnotationRetention.BINARY)
public annotation class InternalSkikoApi
......@@ -2,9 +2,11 @@
package org.jetbrains.skia.impl
import org.jetbrains.skiko.InternalSkikoApi
import org.jetbrains.skiko.internal.fastForEach
internal actual class InteropScope actual constructor() {
@InternalSkikoApi
actual class InteropScope actual constructor() {
private val elements = mutableListOf<NativePointer>()
private var callbacksInitialized = false
......
......@@ -3,6 +3,8 @@
@file:JsQualifier("api")
package org.jetbrains.skiko.wasm
import org.jetbrains.skiko.InternalSkikoApi
import kotlin.js.Promise
internal actual external val awaitSkiko: Promise<JsAny>
@InternalSkikoApi
actual external val awaitSkiko: Promise<JsAny>
package org.jetbrains.skia.impl
import org.jetbrains.skiko.InternalSkikoApi
import java.lang.ref.Reference
actual abstract class Native actual constructor(ptr: NativePointer) {
......@@ -48,7 +49,8 @@ internal actual fun reachabilityBarrier(obj: Any?) {
actual typealias NativePointer = Long
internal actual typealias InteropPointer = java.lang.Object
@InternalSkikoApi
actual typealias InteropPointer = java.lang.Object
internal object theScope: InteropScope()
internal actual inline fun <T> interopScope(block: InteropScope.() -> T): T {
......@@ -56,7 +58,8 @@ internal actual inline fun <T> interopScope(block: InteropScope.() -> T): T {
}
@Suppress("RETURN_TYPE_MISMATCH") // Hack to pass nulls as InteropPointer
internal actual open class InteropScope actual constructor() {
@InternalSkikoApi
actual open class InteropScope actual constructor() {
actual fun toInterop(string: String?): InteropPointer = string
actual fun toInterop(array: ByteArray?): InteropPointer = array
......
......@@ -2,6 +2,7 @@ package org.jetbrains.skia.impl
import kotlinx.cinterop.*
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skiko.InternalSkikoApi
import org.jetbrains.skiko.internal.fastForEach
import kotlin.native.internal.NativePtr
......@@ -49,7 +50,9 @@ actual abstract class Native actual constructor(ptr: NativePointer) {
}
actual typealias NativePointer = NativePtr
internal actual typealias InteropPointer = NativePtr
@InternalSkikoApi
actual typealias InteropPointer = NativePtr
internal actual fun reachabilityBarrier(obj: Any?) {
// TODO: implement native barrier
......@@ -64,7 +67,8 @@ internal actual inline fun <T> interopScope(block: InteropScope.() -> T): T {
}
}
internal actual class InteropScope actual constructor() {
@InternalSkikoApi
actual class InteropScope actual constructor() {
actual fun toInterop(string: String?): InteropPointer {
return if (string != null) {
val pinned = convertToZeroTerminatedString(string).pin()
......
......@@ -2,9 +2,11 @@
package org.jetbrains.skia.impl
import org.jetbrains.skiko.InternalSkikoApi
import org.jetbrains.skiko.internal.fastForEach
internal actual class InteropScope actual constructor() {
@InternalSkikoApi
actual class InteropScope actual constructor() {
private val elements = mutableListOf<NativePointer>()
private var callbacksInitialized = false
......
@file:JsModule("./skiko.mjs")
package org.jetbrains.skiko.wasm
import org.jetbrains.skiko.InternalSkikoApi
import kotlin.js.Promise
internal actual external val awaitSkiko: Promise<JsAny>
@InternalSkikoApi
actual external val awaitSkiko: Promise<JsAny>
package org.jetbrains.skia.impl
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skiko.InternalSkikoApi
actual abstract class Native actual constructor(ptr: NativePointer) {
actual var _ptr: NativePointer
......@@ -36,7 +37,9 @@ actual abstract class Native actual constructor(ptr: NativePointer) {
internal actual fun reachabilityBarrier(obj: Any?) {}
actual typealias NativePointer = Int
internal actual typealias InteropPointer = Int
@InternalSkikoApi
actual typealias InteropPointer = Int
private val INTEROP_SCOPE = InteropScope()
private var interopScopeCounter = 0
......
package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.InternalSkikoApi
import org.w3c.dom.HTMLCanvasElement
import kotlin.js.JsAny
import kotlin.js.Promise
......@@ -24,4 +25,5 @@ internal expect fun createWebGLContext(canvas: HTMLCanvasElement, attr: ContextA
internal expect fun onWasmReady(onReady: () -> Unit)
internal expect val awaitSkiko: Promise<JsAny>
\ No newline at end of file
@InternalSkikoApi
expect val awaitSkiko: Promise<JsAny>
\ No newline at end of file
@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class)
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.LibraryPlugin
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins {
kotlin("multiplatform")
}
if (supportAndroid) {
apply<LibraryPlugin>()
}
repositories {
mavenCentral()
google()
}
kotlin {
compilerOptions {
languageVersion.set(KotlinVersion.KOTLIN_2_2)
apiVersion.set(KotlinVersion.KOTLIN_2_2)
}
applyHierarchyTemplate(skikoSourceSetHierarchyTemplate)
if (supportAwt) {
jvm("awt") {
compilations.all {
compileTaskProvider.configure {
compilerOptions.jvmTarget.set(JvmTarget.JVM_11)
}
}
}
}
if (supportAndroid) {
androidTarget("android") {
compilations.all {
compileTaskProvider.configure {
compilerOptions.jvmTarget.set(JvmTarget.JVM_11)
}
}
}
}
if (supportWeb) {
js {
browser()
}
wasmJs {
browser()
}
}
if (supportNativeMac) {
macosX64()
macosArm64()
}
if (supportNativeLinux) {
linuxX64()
linuxArm64()
}
if (supportNativeIosArm64) {
iosArm64()
}
if (supportNativeIosSimulatorArm64) {
iosSimulatorArm64()
}
if (supportNativeIosX64) {
iosX64()
}
if (supportNativeTvosArm64) {
tvosArm64()
}
if (supportNativeTvosSimulatorArm64) {
tvosSimulatorArm64()
}
if (supportNativeTvosX64) {
tvosX64()
}
sourceSets.commonMain.dependencies {
implementation(kotlin("stdlib"))
api(project(":"))
implementation(kotlin("test"))
implementation(kotlin("test-annotations-common"))
implementation(libs.coroutines.core)
}
if (supportAwt) {
sourceSets.jvmMain.dependencies {
implementation(kotlin("test-junit"))
}
}
}
if (supportAndroid) {
configure<LibraryExtension> {
compileSdk = 33
namespace = "org.jetbrains.skiko.testutils"
defaultConfig.minSdk = 24
defaultConfig.targetSdk = 24
compileOptions.sourceCompatibility = JavaVersion.VERSION_11
compileOptions.targetCompatibility = JavaVersion.VERSION_11
}
}
tasks.withType<KotlinNativeCompile>().configureEach {
compilerOptions.freeCompilerArgs.add("-opt-in=kotlinx.cinterop.ExperimentalForeignApi")
}
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes")
}
......@@ -74,38 +74,38 @@ private inline fun TextBox.isCloseEnoughTo(textBox: TextBox, epsilon: Float = EP
return (direction == textBox.direction) && rect.isCloseEnoughTo(textBox.rect, epsilon)
}
internal fun assertCloseEnough(expected: Float, actual: Float, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: Float, actual: Float, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: Point, actual: Point, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: Point, actual: Point, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: TextBox, actual: TextBox, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: TextBox, actual: TextBox, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: FontMetrics, actual: FontMetrics, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: FontMetrics, actual: FontMetrics, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: Matrix33, actual: Matrix33, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: Matrix33, actual: Matrix33, epsilon: Float = EPSILON) {
assertTrue(
expected.mat.zip(actual.mat).all { (a, b) -> a.isCloseEnoughTo(b, epsilon) },
message = "expected=$expected, actual=$actual, eps=$epsilon"
)
}
internal fun assertCloseEnough(expected: Color4f, actual: Color4f, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: Color4f, actual: Color4f, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: Rect, actual: Rect, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: Rect, actual: Rect, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
internal fun assertCloseEnough(expected: LineMetrics, actual: LineMetrics, epsilon: Float = EPSILON) {
fun assertCloseEnough(expected: LineMetrics, actual: LineMetrics, epsilon: Float = EPSILON) {
assertTrue(expected.isCloseEnoughTo(actual, epsilon), message = "expected=$expected, actual=$actual, eps=$epsilon")
}
......@@ -113,7 +113,7 @@ private fun fail(message: String) {
throw AssertionError(message)
}
internal fun <T> assertContentEquivalent(expected: Iterator<T>, actual: Iterator<T>, eq: (a: T, b: T) -> Boolean) {
fun <T> assertContentEquivalent(expected: Iterator<T>, actual: Iterator<T>, eq: (a: T, b: T) -> Boolean) {
var count = 0
while (expected.hasNext() && actual.hasNext()) {
......@@ -134,22 +134,22 @@ internal fun <T> assertContentEquivalent(expected: Iterator<T>, actual: Iterator
}
}
internal fun assertContentCloseEnough(expected: FloatArray, actual: FloatArray, epsilon: Float = EPSILON) {
fun assertContentCloseEnough(expected: FloatArray, actual: FloatArray, epsilon: Float = EPSILON) {
assertContentEquivalent(expected.iterator(), actual.iterator()) { a, b -> a.isCloseEnoughTo(b, epsilon) }
}
internal fun assertContentCloseEnough(expected: Array<Point>, actual: Array<Point>, epsilon: Float = EPSILON) {
fun assertContentCloseEnough(expected: Array<Point>, actual: Array<Point>, epsilon: Float = EPSILON) {
assertContentEquivalent(expected.iterator(), actual.iterator()) { a, b -> a.isCloseEnoughTo(b, epsilon) }
}
internal fun assertContentCloseEnough(expected: List<Point>, actual: List<Point>, epsilon: Float = EPSILON) {
fun assertContentCloseEnough(expected: List<Point>, actual: List<Point>, epsilon: Float = EPSILON) {
assertContentEquivalent(expected.iterator(), actual.iterator()) { a, b -> a.isCloseEnoughTo(b, epsilon) }
}
internal fun assertContentCloseEnough(expected: Array<TextBox>, actual: Array<TextBox>, epsilon: Float = EPSILON) {
fun assertContentCloseEnough(expected: Array<TextBox>, actual: Array<TextBox>, epsilon: Float = EPSILON) {
assertContentEquivalent(expected.iterator(), actual.iterator()) { a, b -> a.isCloseEnoughTo(b, epsilon) }
}
internal fun assertContentCloseEnough(expected: Array<Shadow>, actual: Array<Shadow>, epsilon: Float = EPSILON) {
fun assertContentCloseEnough(expected: Array<Shadow>, actual: Array<Shadow>, epsilon: Float = EPSILON) {
assertContentEquivalent(expected.iterator(), actual.iterator()) { a, b -> a.isCloseEnoughTo(b, epsilon) }
}
......@@ -2,7 +2,7 @@ package org.jetbrains.skia.util
import kotlin.test.assertTrue
internal fun assertContentDifferent(array1: ByteArray, array2: ByteArray, message: String? = null) {
fun assertContentDifferent(array1: ByteArray, array2: ByteArray, message: String? = null) {
assertTrue(
actual = array1.size != array2.size ||
array1.asSequence().zip(array2.asSequence()).any { it.first != it.second },
......
......@@ -3,12 +3,14 @@ package org.jetbrains.skiko.tests
import org.jetbrains.skia.Data
import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.InternalSkikoApi
expect class TestReturnType
expect fun runTest(block: suspend () -> Unit): TestReturnType
internal expect fun InteropScope.allocateBytesForPixels(size: Int): NativePointer
@OptIn(InternalSkikoApi::class)
expect fun InteropScope.allocateBytesForPixels(size: Int): NativePointer
expect annotation class SkipNativeTarget()
......
......@@ -3,11 +3,13 @@ package org.jetbrains.skiko.tests
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise
import org.jetbrains.skiko.InternalSkikoApi
import org.jetbrains.skiko.wasm.awaitSkiko
/**
* Awaits for `wasmSetup` and then runs the [block] in a coroutine.
*/
@OptIn(InternalSkikoApi::class)
actual fun runTest(block: suspend () -> Unit): TestReturnType = MainScope().promise {
awaitSkiko.await()
testSetup()
......
......@@ -6,6 +6,7 @@ import org.jetbrains.skia.impl.BufferUtil
import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.makeFromFileName
import org.jetbrains.skiko.InternalSkikoApi
import java.nio.ByteBuffer
actual typealias TestReturnType = Unit
......@@ -14,7 +15,8 @@ actual fun runTest(block: suspend () -> Unit): TestReturnType {
return runBlocking { block() }
}
internal actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
@OptIn(InternalSkikoApi::class)
actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
return BufferUtil.getPointerFromByteBuffer(ByteBuffer.allocateDirect(size))
}
......
......@@ -5,6 +5,7 @@ import org.jetbrains.skia.Data
import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.makeFromFileName
import org.jetbrains.skiko.InternalSkikoApi
actual typealias TestReturnType = Unit
......@@ -12,7 +13,8 @@ actual fun runTest(block: suspend () -> Unit): TestReturnType {
return runBlocking { block() }
}
internal actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
@OptIn(InternalSkikoApi::class)
actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
return toInterop(ByteArray(size))
}
......
......@@ -3,6 +3,7 @@ package org.jetbrains.skiko.tests
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise
import org.jetbrains.skiko.InternalSkikoApi
import org.jetbrains.skiko.wasm.awaitSkiko
actual annotation class SkipJsTarget
......@@ -14,6 +15,7 @@ private external fun jsRef(): JsAny
/**
* Runs the [block] in a coroutine.
*/
@OptIn(InternalSkikoApi::class)
actual fun runTest(block: suspend () -> Unit): TestReturnType = MainScope().promise {
awaitSkiko.await<Any>()
block()
......
......@@ -3,8 +3,10 @@ package org.jetbrains.skiko.tests
import org.jetbrains.skia.Data
import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.InternalSkikoApi
internal actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
@OptIn(InternalSkikoApi::class)
actual fun InteropScope.allocateBytesForPixels(size: Int): NativePointer {
return toInterop(ByteArray(size))
}
......@@ -12,6 +14,7 @@ actual annotation class SkipJvmTarget
actual annotation class SkipNativeTarget
@OptIn(InternalSkikoApi::class)
actual fun makeFromFileName(path: String?): Data = Data(0)
actual val isDebugModeOnJvm: Boolean = false
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment