Skip to content

WIP: Report download progress #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 1.0.0-BETA29 (unreleased)

* Fix potential race condition between jobs in `connect()` and `disconnect()`.
* Report real-time progress information about downloads through `SyncStatus.downloadProgress`.
* Compose: Add `composeState()` extension method on `SyncStatus`.

## 1.0.0-BETA28

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.powersync.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import com.powersync.sync.SyncStatus
import com.powersync.sync.SyncStatusData
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.debounce
import kotlin.time.Duration

@OptIn(FlowPreview::class)
@Composable
public fun SyncStatus.composeState(debounce: Duration=Duration.ZERO): State<SyncStatusData> {
var flow: Flow<SyncStatusData> = asFlow()
if (debounce.isPositive()) {
flow = flow.debounce(debounce)
}

return flow.collectAsState(initial = this)
}
5 changes: 4 additions & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,10 @@ androidComponents.onVariants {
}

tasks.named<ProcessResources>(kotlin.jvm().compilations["main"].processResourcesTaskName) {
from(downloadPowersyncDesktopBinaries)
//from(downloadPowersyncDesktopBinaries)
from("/Users/simon/src/powersync-sqlite-core/target/debug/libpowersync.dylib") {
rename { "libpowersync_aarch64.dylib" }
}
}

// We want to build with recent JDKs, but need to make sure we support Java 8. https://jakewharton.com/build-on-latest-java-test-through-lowest-java/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.powersync.sync

import co.touchlab.kermit.ExperimentalKermitApi
import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import co.touchlab.kermit.TestConfig
import co.touchlab.kermit.TestLogWriter
import com.powersync.PowerSyncDatabase
import com.powersync.connectors.PowerSyncBackendConnector
import com.powersync.connectors.PowerSyncCredentials
import com.powersync.db.PowerSyncDatabaseImpl
import com.powersync.db.schema.Schema
import com.powersync.testutils.MockSyncService
import com.powersync.testutils.UserRow
import com.powersync.testutils.cleanup
import com.powersync.testutils.factory
import com.powersync.testutils.generatePrintLogWriter
import dev.mokkery.answering.returns
import dev.mokkery.everySuspend
import dev.mokkery.mock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.JsonObject
import kotlin.test.AfterTest
import kotlin.test.BeforeTest

@OptIn(ExperimentalKermitApi::class)
abstract class BaseInMemorySyncTest {
val logWriter =
TestLogWriter(
loggable = Severity.Debug,
)

val logger =
Logger(
TestConfig(
minSeverity = Severity.Debug,
logWriterList = listOf(logWriter, generatePrintLogWriter()),
),
)
internal lateinit var database: PowerSyncDatabaseImpl
internal lateinit var connector: PowerSyncBackendConnector
internal lateinit var syncLines: Channel<SyncLine>

@BeforeTest
fun setup() {
cleanup("testdb")
logWriter.reset()
database = openDb()
connector =
mock<PowerSyncBackendConnector> {
everySuspend { getCredentialsCached() } returns
PowerSyncCredentials(
token = "test-token",
userId = "test-user",
endpoint = "https://test.com",
)

everySuspend { invalidateCredentials() } returns Unit
}
syncLines = Channel()

runBlocking {
database.disconnectAndClear(true)
}
}

@AfterTest
fun teardown() {
runBlocking {
database.close()
}
cleanup("testdb")
}

internal fun openDb() =
PowerSyncDatabase(
factory = factory,
schema = Schema(UserRow.table),
dbFilename = "testdb",
) as PowerSyncDatabaseImpl

internal fun syncStream(): SyncStream {
val client = MockSyncService(syncLines)
return SyncStream(
bucketStorage = database.bucketStorage,
connector = connector,
httpEngine = client,
uploadCrud = { },
retryDelayMs = 10,
logger = logger,
params = JsonObject(emptyMap()),
)
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
package com.powersync
package com.powersync.sync

import app.cash.turbine.turbineScope
import co.touchlab.kermit.ExperimentalKermitApi
import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import co.touchlab.kermit.TestConfig
import co.touchlab.kermit.TestLogWriter
import com.powersync.PowerSyncDatabase
import com.powersync.PowerSyncException
import com.powersync.bucket.BucketChecksum
import com.powersync.bucket.BucketPriority
import com.powersync.bucket.Checkpoint
import com.powersync.bucket.OpType
import com.powersync.bucket.OplogEntry
import com.powersync.connectors.PowerSyncBackendConnector
import com.powersync.connectors.PowerSyncCredentials
import com.powersync.db.PowerSyncDatabaseImpl
import com.powersync.db.schema.Schema
import com.powersync.sync.SyncLine
import com.powersync.sync.SyncStream
import com.powersync.testutils.MockSyncService
import com.powersync.testutils.UserRow
import com.powersync.testutils.cleanup
import com.powersync.testutils.factory
import com.powersync.testutils.generatePrintLogWriter
import com.powersync.testutils.waitFor
import com.powersync.utils.JsonUtil
import dev.mokkery.answering.returns
import dev.mokkery.everySuspend
import dev.mokkery.mock
import dev.mokkery.verify
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonObject
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
Expand All @@ -44,75 +27,8 @@ import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds

@OptIn(ExperimentalKermitApi::class)
class SyncIntegrationTest {
private val logWriter =
TestLogWriter(
loggable = Severity.Debug,
)

private val logger =
Logger(
TestConfig(
minSeverity = Severity.Debug,
logWriterList = listOf(logWriter, generatePrintLogWriter()),
),
)
private lateinit var database: PowerSyncDatabaseImpl
private lateinit var connector: PowerSyncBackendConnector
private lateinit var syncLines: Channel<SyncLine>

@BeforeTest
fun setup() {
cleanup("testdb")
logWriter.reset()
database = openDb()
connector =
mock<PowerSyncBackendConnector> {
everySuspend { getCredentialsCached() } returns
PowerSyncCredentials(
token = "test-token",
userId = "test-user",
endpoint = "https://test.com",
)

everySuspend { invalidateCredentials() } returns Unit
}
syncLines = Channel()

runBlocking {
database.disconnectAndClear(true)
}
}

@AfterTest
fun teardown() {
runBlocking {
database.close()
}
cleanup("testdb")
}

private fun openDb() =
PowerSyncDatabase(
factory = factory,
schema = Schema(UserRow.table),
dbFilename = "testdb",
) as PowerSyncDatabaseImpl

private fun syncStream(): SyncStream {
val client = MockSyncService(syncLines)
return SyncStream(
bucketStorage = database.bucketStorage,
connector = connector,
httpEngine = client,
uploadCrud = { },
retryDelayMs = 10,
logger = logger,
params = JsonObject(emptyMap()),
)
}

@OptIn(co.touchlab.kermit.ExperimentalKermitApi::class)
class SyncIntegrationTest : BaseInMemorySyncTest() {
private suspend fun expectUserCount(amount: Int) {
val users = database.getAll("SELECT * FROM users;") { UserRow.from(it) }
assertEquals(amount, users.size, "Expected $amount users, got $users")
Expand Down
Loading