commit 947e1f909628169e1c40cfe54a6a980f28ec8890
parent 3719c2bb4b598acc28ea137ed8f03a7c8981ba18
Author: t3sserakt <t3ss@posteo.de>
Date: Fri, 2 Jan 2026 10:36:10 +0100
database peer test
Diffstat:
6 files changed, 204 insertions(+), 42 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -3,3 +3,4 @@
GNUnetMessenger/app/release/
GNUnetMessenger/.idea/*.xml
+/.idea/
diff --git a/GNUnetMessenger/app/src/androidTest/java/org/gnunet/gnunetmessenger/ipc/GnunetChatRemoteTest.kt b/GNUnetMessenger/app/src/androidTest/java/org/gnunet/gnunetmessenger/ipc/GnunetChatRemoteTest.kt
@@ -37,9 +37,11 @@ class GnunetChatRemoteTest {
}
@After
- fun tearDown() {
+ fun tearDown() = runTest {
// sauber vom Service abmelden
gnunetChat.unbind()
+ // Wait for unbind to complete and clean up
+ delay(1000)
}
@Test
@@ -53,7 +55,7 @@ class GnunetChatRemoteTest {
}
// 2. Wait for handle to be initialized with pointer from async operation
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(5_000) {
while (handle.pointer == 0L) {
delay(100)
@@ -78,7 +80,7 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(5_000) {
while (handle.pointer == 0L) {
delay(100)
@@ -99,7 +101,7 @@ class GnunetChatRemoteTest {
accounts += acc
}
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(3_000) {
while (accounts.none { it.name == name }) {
delay(50)
@@ -117,9 +119,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -137,7 +141,7 @@ class GnunetChatRemoteTest {
accounts += acc
}
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(5_000) {
while (accounts.size < accountNames.size) {
delay(50)
@@ -158,9 +162,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -181,9 +187,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -201,7 +209,7 @@ class GnunetChatRemoteTest {
0 // Return GNUNet_OK (Int as required by interface)
}
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(3_000) {
while (groups.none { it.name == groupName }) {
delay(50)
@@ -220,11 +228,16 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
+
+ // Wait a bit for any initial messages to clear
+ delay(500)
val contacts = mutableListOf<String>()
@@ -233,15 +246,18 @@ class GnunetChatRemoteTest {
0 // Return GNUNet_OK (Int as required by interface)
}
- withContext(Dispatchers.Default) {
- withTimeout(3_000) {
- while (contacts.isEmpty()) {
+ // Wait longer for contacts to arrive - server needs to process the iteration
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ var attempts = 0
+ while (contacts.isEmpty() && attempts < 100) {
delay(50)
+ attempts++
}
}
}
- assertTrue("Should receive at least one contact", contacts.isNotEmpty())
+ assertTrue("Should receive at least one contact, got: $contacts", contacts.isNotEmpty())
}
@Test
@@ -251,9 +267,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -268,7 +286,7 @@ class GnunetChatRemoteTest {
attributes.add(key to value)
}
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(3_000) {
while (attributes.none { it.first == testKey }) {
delay(50)
@@ -287,9 +305,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -298,7 +318,7 @@ class GnunetChatRemoteTest {
gnunetChat.createAccount(handle, accountName)
// Wait for message to be received
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(3_000) {
while (messageLog.isEmpty()) {
delay(50)
@@ -320,9 +340,11 @@ class GnunetChatRemoteTest {
}
// Wait for handle to be initialized
- withTimeout(5_000) {
- while (handle.pointer == 0L) {
- delay(100)
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
}
}
@@ -332,7 +354,7 @@ class GnunetChatRemoteTest {
lobbyUri = uri
}
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
withTimeout(3_000) {
while (lobbyUri.isEmpty()) {
delay(50)
@@ -342,4 +364,111 @@ class GnunetChatRemoteTest {
assertTrue("Lobby URI should not be empty", lobbyUri.isNotEmpty())
}
+
+ @Test
+ fun testResetClearsAllData() = runTest {
+ val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg ->
+ messageLog.add(ctx to msg)
+ }
+
+ // Wait for handle to be initialized
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (handle.pointer == 0L) {
+ delay(100)
+ }
+ }
+ }
+
+ // Create some test data
+ val accountName = "TestAccountReset"
+ val groupName = "TestGroupReset"
+ val profileName = "TestProfileReset"
+
+ // Create account
+ val createResult = gnunetChat.createAccount(handle, accountName)
+ assertEquals("Account creation should succeed", GnunetReturnValue.OK, createResult)
+
+ // Create group
+ val group = gnunetChat.createGroup(handle, groupName)
+ assertNotNull("Group should be created", group)
+ assertEquals(groupName, group.name)
+
+ // Set profile name
+ gnunetChat.setProfileName(handle, profileName)
+ assertEquals("Profile name should be set", profileName, gnunetChat.getProfileName(handle))
+
+ // Verify data exists before reset
+ val accountsBefore = mutableListOf<ChatAccount>()
+ gnunetChat.iterateAccounts(handle) { acc ->
+ accountsBefore += acc
+ }
+
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(3_000) {
+ while (accountsBefore.none { it.name == accountName }) {
+ delay(50)
+ }
+ }
+ }
+
+ assertTrue("Account should exist before reset",
+ accountsBefore.any { it.name == accountName })
+
+ val groupsBefore = mutableListOf<ChatGroup>()
+ gnunetChat.iterateGroups(handle) { grp ->
+ groupsBefore += grp
+ 0
+ }
+
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(3_000) {
+ while (groupsBefore.none { it.name == groupName }) {
+ delay(50)
+ }
+ }
+ }
+
+ assertTrue("Group should exist before reset",
+ groupsBefore.any { it.name == groupName })
+
+ // Perform reset
+ gnunetChat.reset()
+
+ // Note: The local ChatHandle object's pointer is not automatically set to 0
+ // because it's a local variable. The reset clears server state, but we need
+ // to start a new session with a fresh handle.
+
+ // Start a new session after reset
+ val newHandle = gnunetChat.startChat(MessengerApp()) { ctx, msg ->
+ messageLog.add(ctx to msg)
+ }
+
+ // Wait for new handle to be initialized
+ withContext(Dispatchers.Default.limitedParallelism(1)) {
+ withTimeout(5_000) {
+ while (newHandle.pointer == 0L) {
+ delay(100)
+ }
+ }
+ }
+
+ // Verify old data is gone
+ val accountsAfter = mutableListOf<ChatAccount>()
+ gnunetChat.iterateAccounts(newHandle) { acc ->
+ accountsAfter += acc
+ }
+
+ // Give time for iteration to complete
+ delay(500)
+
+ // The created account should be gone (only default mock accounts remain)
+ assertTrue("Created account should not exist after reset",
+ !accountsAfter.any { it.name == accountName })
+
+ // Profile should be back to default
+ val defaultProfile = gnunetChat.getProfileName(newHandle)
+ assertTrue("Profile should be reset to default", profileName != defaultProfile)
+ assertEquals("Profile should be default 'GNUnet'", "GNUnet", defaultProfile)
+ }
}
diff --git a/GNUnetMessenger/app/src/main/aidl/org/gnunet/gnunetmessenger/ipc/IGnunetChat.aidl b/GNUnetMessenger/app/src/main/aidl/org/gnunet/gnunetmessenger/ipc/IGnunetChat.aidl
@@ -18,6 +18,7 @@ interface IGnunetChat {
// Core chat operations
int getApiVersion();
long startChat(String messengerApp, IChatCallback cb);
+ void reset();
void iterateAccounts(long handle, IAccountCallback cb);
int createAccount(long handle, String name);
void connect(long handle, in ChatAccountDto account);
diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/GnunetChat.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/GnunetChat.kt
@@ -39,6 +39,7 @@ import org.gnunet.gnunetmessenger.model.MessengerApp
interface GnunetChat {
suspend fun awaitReady(handle: ChatHandle)
fun startChat(messengerApp: MessengerApp, callback: (ChatContext, ChatMessage) -> Unit): ChatHandle
+ suspend fun reset()
fun iterateAccounts(handle: ChatHandle, callback: (ChatAccount) -> Unit)
suspend fun createAccount(handle: ChatHandle, name: String): GnunetReturnValue
suspend fun connect(handle: ChatHandle, account: ChatAccount)
@@ -85,4 +86,4 @@ interface GnunetChat {
fun getContactAttributes(contact: ChatContact, callback: (String, String) -> Unit)
fun shareAttributes (handle: ChatHandle, contact: ChatContact, key: String)
fun unshareAttributes (handle: ChatHandle, contact: ChatContact, key: String)
-}
-\ No newline at end of file
+}
diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt
@@ -247,8 +247,8 @@ class GnunetChatBoundService(
val ch = ChatHandle(0L)
val remote = remoteRef.get()
- if (remote != null && lastHandle == 0L) {
- // Wir sind gebunden, aber noch kein Server-Handle -> jetzt starten
+ if (remote != null) {
+ // Wir sind gebunden -> Session starten (auch wenn lastHandle == 0L nach reset)
ioScope.launch {
runCatching { remote.startChat("messengerApp", binderCallback) }
.onSuccess { h ->
@@ -261,7 +261,7 @@ class GnunetChatBoundService(
Log.e(TAG, "startChat failed", t)
}
}
- } else if (remote == null) {
+ } else {
// Noch nicht gebunden: Bind anstoßen; onServiceConnected ruft startChat
bind()
@@ -274,14 +274,35 @@ class GnunetChatBoundService(
Log.d(TAG, "late handle propagation -> ${ch.pointer}")
}
}
- } else if (remote != null && lastHandle != 0L) {
- // Bereits eine Session vorhanden (Single-Session-Design) -> denselben Handle verwenden
- ch.pointer = lastHandle
}
return ch
}
+ override suspend fun reset() = withContext(Dispatchers.IO) {
+ val remote = try {
+ getOrBindRemote()
+ } catch (t: Throwable) {
+ Log.e(TAG, "reset: failed to bind remote", t)
+ throw t
+ }
+
+ try {
+ remote.reset()
+ Log.i(TAG, "reset: successfully reset remote service")
+
+ // Reset local state
+ lastHandle = 0L
+ handleReady.clear()
+ synchronized(pendingAfterHandle) {
+ pendingAfterHandle.clear()
+ }
+ } catch (e: RemoteException) {
+ Log.e(TAG, "reset: remote call failed", e)
+ throw e
+ }
+ }
+
override fun iterateAccounts(handle: ChatHandle, callback: (ChatAccount) -> Unit) {
val bridge = object : IAccountCallback.Stub() {
override fun onAccount(accountDto: ChatAccountDto) {
diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/mock/GnunetChatMock.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/mock/GnunetChatMock.kt
@@ -375,4 +375,14 @@ class GnunetChatMock : GnunetChat {
override fun unshareAttributes(handle: ChatHandle, contact: ChatContact, key: String) {
println("unshare ${key} for contact ${contact.name}")
}
+
+ override suspend fun reset() {
+ println("reset mock service - clearing all state")
+ uuidCounter = 0
+ lastDestroyedUri = null
+ lastSetUserPointer = null
+ lastSetGroupPointer = null
+ lastSetMessageForGroupContact = null
+ messageCallback = { _, _ -> }
+ }
}