Skip to content

Commit 119be6f

Browse files
committed
Fix unit tests with database
1 parent 3a57b1b commit 119be6f

File tree

6 files changed

+159
-73
lines changed

6 files changed

+159
-73
lines changed

core/build.gradle.kts

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import com.android.build.gradle.internal.tasks.factory.dependsOn
12
import com.powersync.plugins.sonatype.setupGithubRepository
23
import de.undercouch.gradle.tasks.download.Download
34
import org.gradle.internal.os.OperatingSystem
45
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
56
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
67
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
8+
import org.jetbrains.kotlin.gradle.plugin.mpp.TestExecutable
79
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
10+
import org.jetbrains.kotlin.konan.target.Family
811

912
plugins {
1013
alias(libs.plugins.kotlinMultiplatform)
@@ -74,6 +77,56 @@ val buildCInteropDef by tasks.registering {
7477
outputs.files(defFile)
7578
}
7679

80+
val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop")
81+
val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) {
82+
val coreVersion = libs.versions.powersync.core.get()
83+
val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so"
84+
val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so"
85+
val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib"
86+
val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib"
87+
val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll"
88+
89+
if (binariesAreProvided) {
90+
src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64))
91+
} else {
92+
val (aarch64, x64) = when {
93+
os.isLinux -> linux_aarch64 to linux_x64
94+
os.isMacOsX -> macos_aarch64 to macos_x64
95+
os.isWindows -> null to windows_x64
96+
else -> error("Unknown operating system: $os")
97+
}
98+
val arch = System.getProperty("os.arch")
99+
src(when {
100+
crossArch -> listOfNotNull(aarch64, x64)
101+
arch == "aarch64" -> listOfNotNull(aarch64)
102+
arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64)
103+
else -> error("Unsupported architecture: $arch")
104+
})
105+
}
106+
dest(binariesFolder.map { it.dir("powersync") })
107+
onlyIfModified(true)
108+
}
109+
110+
val downloadPowersyncFramework by tasks.registering(Download::class) {
111+
val coreVersion = libs.versions.powersync.core.get()
112+
val framework = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync-sqlite-core.xcframework.zip"
113+
114+
src(framework)
115+
dest(binariesFolder.map { it.file("framework/powersync-sqlite-core.xcframework.zip") })
116+
onlyIfModified(true)
117+
}
118+
119+
val unzipPowersyncFramework by tasks.registering(Copy::class) {
120+
dependsOn(downloadPowersyncFramework)
121+
122+
from(
123+
zipTree(downloadPowersyncFramework.get().dest).matching {
124+
include("powersync-sqlite-core.xcframework/**")
125+
},
126+
)
127+
into(binariesFolder.map { it.dir("framework") })
128+
}
129+
77130
kotlin {
78131
androidTarget {
79132
publishLibraryVariants("release", "debug")
@@ -92,7 +145,7 @@ kotlin {
92145
}
93146
}
94147

95-
iosX64()
148+
// iosX64()
96149
iosArm64()
97150
iosSimulatorArm64()
98151

@@ -112,6 +165,29 @@ kotlin {
112165
}
113166
cinterops.create("powersync-sqlite-core")
114167
}
168+
169+
if (konanTarget.family == Family.IOS) {
170+
binaries.withType<TestExecutable>().configureEach {
171+
linkTaskProvider.dependsOn(unzipPowersyncFramework)
172+
linkerOpts("-framework", "powersync-sqlite-core")
173+
174+
val framework = if (konanTarget.name.contains("simulator")) {
175+
"ios-arm64_x86_64-simulator"
176+
} else {
177+
"ios-arm64"
178+
}
179+
val frameworkRoot = binariesFolder.map { it.dir("framework/powersync-sqlite-core.xcframework/$framework") }.get().asFile.path
180+
181+
linkerOpts("-F", frameworkRoot)
182+
linkerOpts("-rpath", frameworkRoot)
183+
}
184+
} else {
185+
binaries.withType<TestExecutable>().configureEach {
186+
linkTaskProvider.dependsOn(downloadPowersyncDesktopBinaries)
187+
linkerOpts("-lpowersync")
188+
linkerOpts("-L", binariesFolder.map { it.dir("powersync") }.get().asFile.path)
189+
}
190+
}
115191
}
116192

117193
explicitApi()
@@ -138,7 +214,6 @@ kotlin {
138214
api(libs.kermit)
139215
}
140216

141-
androidMain.get()
142217
androidMain.dependencies {
143218
implementation(libs.ktor.client.okhttp)
144219
}
@@ -155,8 +230,8 @@ kotlin {
155230
commonTest.dependencies {
156231
implementation(libs.kotlin.test)
157232
implementation(libs.test.coroutines)
158-
implementation(libs.kermit.test)
159233
implementation(libs.test.turbine)
234+
implementation(libs.kermit.test)
160235
}
161236
}
162237
}
@@ -212,40 +287,11 @@ android {
212287
val os = OperatingSystem.current()
213288
val binariesAreProvided = project.findProperty("powersync.binaries.provided") == "true"
214289
val crossArch = project.findProperty("powersync.binaries.cross-arch") == "true"
215-
val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop")
216290

217291
if (binariesAreProvided && crossArch) {
218292
error("powersync.binaries.provided and powersync.binaries.cross-arch must not be both defined.")
219293
}
220294

221-
val downloadPowersyncDesktopBinaries = tasks.register<Download>("downloadPowersyncDesktopBinaries") {
222-
val coreVersion = libs.versions.powersync.core.get()
223-
val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so"
224-
val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so"
225-
val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib"
226-
val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib"
227-
val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll"
228-
if (binariesAreProvided) {
229-
src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64))
230-
} else {
231-
val (aarch64, x64) = when {
232-
os.isLinux -> linux_aarch64 to linux_x64
233-
os.isMacOsX -> macos_aarch64 to macos_x64
234-
os.isWindows -> null to windows_x64
235-
else -> error("Unknown operating system: $os")
236-
}
237-
val arch = System.getProperty("os.arch")
238-
src(when {
239-
crossArch -> listOfNotNull(aarch64, x64)
240-
arch == "aarch64" -> listOfNotNull(aarch64)
241-
arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64)
242-
else -> error("Unsupported architecture: $arch")
243-
})
244-
}
245-
dest(binariesFolder.map { it.dir("powersync") })
246-
onlyIfModified(true)
247-
}
248-
249295
tasks.named<ProcessResources>(kotlin.jvm().compilations["main"].processResourcesTaskName) {
250296
from(downloadPowersyncDesktopBinaries)
251297
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.powersync.testutils
2+
3+
import com.powersync.DatabaseDriverFactory
4+
import java.io.File
5+
6+
actual typealias IgnoreOnAndroid = org.junit.Ignore
7+
8+
actual val factory: DatabaseDriverFactory
9+
get() = error("Unsupported")
10+
11+
actual fun cleanup(path: String) {
12+
File(path).delete()
13+
}

core/src/commonTest/kotlin/com/powersync/DatabaseTest.kt

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,80 +6,97 @@ import com.powersync.db.getString
66
import com.powersync.db.schema.Column
77
import com.powersync.db.schema.Schema
88
import com.powersync.db.schema.Table
9+
import com.powersync.testutils.IgnoreOnAndroid
910
import com.powersync.testutils.cleanup
1011
import com.powersync.testutils.factory
12+
import kotlinx.coroutines.runBlocking
1113
import kotlinx.coroutines.test.runTest
1214
import kotlin.test.AfterTest
1315
import kotlin.test.BeforeTest
1416
import kotlin.test.Test
1517
import kotlin.test.assertEquals
1618

19+
@IgnoreOnAndroid
1720
class DatabaseTest {
1821
private lateinit var database: PowerSyncDatabase
1922

2023
@BeforeTest
2124
fun setupDatabase() {
22-
database = PowerSyncDatabase(
23-
factory = factory,
24-
schema = Schema(
25-
Table(name = "users", columns = listOf(Column.text("name"), Column.text("email")))
26-
),
27-
dbFilename = "testdb"
28-
)
25+
database =
26+
PowerSyncDatabase(
27+
factory = factory,
28+
schema =
29+
Schema(
30+
Table(name = "users", columns = listOf(Column.text("name"), Column.text("email"))),
31+
),
32+
dbFilename = "testdb",
33+
)
34+
35+
runBlocking {
36+
database.disconnectAndClear(true)
37+
}
2938
}
3039

3140
@AfterTest
3241
fun tearDown() {
42+
runBlocking { database.disconnectAndClear(true) }
3343
cleanup("testdb")
3444
}
3545

3646
@Test
37-
fun testLinksPowerSync() = runTest {
38-
database.get("SELECT powersync_rs_version() AS r;") { it.getString(0)!! }
39-
}
47+
fun testLinksPowerSync() =
48+
runTest {
49+
database.get("SELECT powersync_rs_version() AS r;") { it.getString(0)!! }
50+
}
4051

4152
@Test
42-
fun testTableUpdates() = runTest {
43-
turbineScope {
44-
val query = database.watch("SELECT * FROM users") { User.from(it) }.testIn(this)
53+
fun testTableUpdates() =
54+
runTest {
55+
turbineScope {
56+
val query = database.watch("SELECT * FROM users") { User.from(it) }.testIn(this)
4557

46-
// Wait for initial query
47-
assertEquals(0, query.awaitItem().size)
58+
// Wait for initial query
59+
assertEquals(0, query.awaitItem().size)
4860

49-
database.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test", "[email protected]"))
50-
assertEquals(1, query.awaitItem().size)
61+
database.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test", "[email protected]"))
62+
assertEquals(1, query.awaitItem().size)
5163

52-
database.writeTransaction {
53-
it.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test2", "[email protected]"))
54-
it.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test3", "[email protected]"))
55-
}
64+
database.writeTransaction {
65+
it.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test2", "[email protected]"))
66+
it.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test3", "[email protected]"))
67+
}
5668

57-
assertEquals(3, query.awaitItem().size)
69+
assertEquals(3, query.awaitItem().size)
5870

59-
try {
60-
database.writeTransaction {
61-
it.execute("DELETE FROM users;")
62-
it.execute("syntax error, revert please")
71+
try {
72+
database.writeTransaction {
73+
it.execute("DELETE FROM users;")
74+
it.execute("syntax error, revert please")
75+
}
76+
} catch (e: Exception) {
77+
// Ignore
6378
}
64-
} catch (e: Exception) {
65-
// Ignore
66-
}
6779

68-
database.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test4", "[email protected]"))
69-
assertEquals(4, query.awaitItem().size)
80+
database.execute("INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", listOf("Test4", "[email protected]"))
81+
assertEquals(4, query.awaitItem().size)
7082

71-
query.expectNoEvents()
72-
query.cancel()
83+
query.expectNoEvents()
84+
query.cancel()
85+
}
7386
}
74-
}
7587

76-
private data class User(val id: String, val name: String, val email: String) {
88+
private data class User(
89+
val id: String,
90+
val name: String,
91+
val email: String,
92+
) {
7793
companion object {
78-
fun from(cursor: SqlCursor): User = User(
79-
id = cursor.getString("id"),
80-
name = cursor.getString("name"),
81-
email = cursor.getString("email")
82-
)
94+
fun from(cursor: SqlCursor): User =
95+
User(
96+
id = cursor.getString("id"),
97+
name = cursor.getString("name"),
98+
email = cursor.getString("email"),
99+
)
83100
}
84101
}
85102
}

core/src/commonTest/kotlin/com/powersync/testutils/TestUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ package com.powersync.testutils
22

33
import com.powersync.DatabaseDriverFactory
44

5+
expect annotation class IgnoreOnAndroid()
6+
57
expect val factory: DatabaseDriverFactory
8+
69
expect fun cleanup(path: String)

core/src/iosTest/kotlin/com/powersync/testutils/TestUtils.ios.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import com.powersync.DatabaseDriverFactory
44
import kotlinx.io.files.Path
55
import kotlinx.io.files.SystemFileSystem
66

7+
actual annotation class IgnoreOnAndroid
8+
79
actual val factory: DatabaseDriverFactory
810
get() = DatabaseDriverFactory()
911

1012
actual fun cleanup(path: String) {
11-
SystemFileSystem.delete(Path(path))
13+
val resolved = Path(path)
14+
if (SystemFileSystem.exists(resolved)) {
15+
SystemFileSystem.delete(resolved)
16+
}
1217
}

core/src/jvmTest/kotlin/com/powersync/testutils/TestUtils.jvm.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.powersync.testutils
33
import com.powersync.DatabaseDriverFactory
44
import java.io.File
55

6+
actual annotation class IgnoreOnAndroid
7+
68
actual val factory: DatabaseDriverFactory
79
get() = DatabaseDriverFactory()
810

0 commit comments

Comments
 (0)