feat: more common library wrapper implementation and test

This commit is contained in:
0x330a 2023-02-01 18:06:03 +11:00
parent 9bc69f7d54
commit 74b2486e92
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
4 changed files with 282 additions and 19 deletions

View File

@ -352,4 +352,199 @@ class InstrumentedTests {
assertEquals(1, convos.sizeOneToOnes())
}
@Test
fun test_open_group_urls() {
val (base1, room1, pk1) = Conversation.OpenGroup.parseFullUrl(
"https://example.com/" +
"SomeRoom?public_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
)!!
val (base2, room2, pk2) = Conversation.OpenGroup.parseFullUrl(
"HTTPS://EXAMPLE.COM/" +
"sOMErOOM?public_key=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
)!!
val (base3, room3, pk3) = Conversation.OpenGroup.parseFullUrl(
"HTTPS://EXAMPLE.COM/r/" +
"someroom?public_key=0123456789aBcdEF0123456789abCDEF0123456789ABCdef0123456789ABCDEF"
)!!
val (base4, room4, pk4) = Conversation.OpenGroup.parseFullUrl(
"http://example.com/r/" +
"someroom?public_key=0123456789aBcdEF0123456789abCDEF0123456789ABCdef0123456789ABCDEF"
)!!
val (base5, room5, pk5) = Conversation.OpenGroup.parseFullUrl(
"HTTPS://EXAMPLE.com:443/r/" +
"someroom?public_key=0123456789aBcdEF0123456789abCDEF0123456789ABCdef0123456789ABCDEF"
)!!
val (base6, room6, pk6) = Conversation.OpenGroup.parseFullUrl(
"HTTP://EXAMPLE.com:80/r/" +
"someroom?public_key=0123456789aBcdEF0123456789abCDEF0123456789ABCdef0123456789ABCDEF"
)!!
val (base7, room7, pk7) = Conversation.OpenGroup.parseFullUrl(
"http://example.com:80/r/" +
"someroom?public_key=ASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8"
)!!
val (base8, room8, pk8) = Conversation.OpenGroup.parseFullUrl(
"http://example.com:80/r/" +
"someroom?public_key=yrtwk3hjixg66yjdeiuauk6p7hy1gtm8tgih55abrpnsxnpm3zzo"
)!!
assertEquals("https://example.com", base1)
assertEquals(base1, base2)
assertEquals(base1, base3)
assertNotEquals(base1, base4)
assertEquals(base4, "http://example.com")
assertEquals(base1, base5)
assertEquals(base4, base6)
assertEquals(base4, base7)
assertEquals(base4, base8)
assertEquals(room1, "someroom")
assertEquals(room2, "someroom")
assertEquals(room3, "someroom")
assertEquals(room4, "someroom")
assertEquals(room5, "someroom")
assertEquals(room6, "someroom")
assertEquals(room7, "someroom")
assertEquals(room8, "someroom")
assertEquals(Hex.toStringCondensed(pk1), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk2), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk3), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk4), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk5), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk6), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk7), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
assertEquals(Hex.toStringCondensed(pk8), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
}
@Test
fun test_conversations() {
val convos = ConversationVolatileConfig.newInstance(keyPair.secretKey)
val definitelyRealId = "055000000000000000000000000000000000000000000000000000000000000000"
assertNull(convos.getOneToOne(definitelyRealId))
assertTrue(convos.empty())
assertEquals(0, convos.size())
val c = convos.getOrConstructOneToOne(definitelyRealId)
assertEquals(definitelyRealId, c.sessionId)
assertEquals(0, c.lastRead)
assertFalse(convos.needsPush())
assertFalse(convos.needsDump())
assertEquals(0, convos.push().seqNo)
val nowMs = System.currentTimeMillis()
c.lastRead = nowMs
convos.set(c)
assertNull(convos.getLegacyClosedGroup(definitelyRealId))
assertNotNull(convos.getOneToOne(definitelyRealId))
assertEquals(nowMs, convos.getOneToOne(definitelyRealId)?.lastRead)
assertTrue(convos.needsPush())
assertTrue(convos.needsDump())
val openGroupPubKey = Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
val og = convos.getOrConstructOpenGroup("http://Example.ORG:5678", "SudokuRoom", openGroupPubKey)
assertEquals("http://example.org:5678", og.baseUrl) // Note: lower-case
assertEquals("sudokuroom", og.room) // Note: lower-case
assertEquals(32, og.pubKey.size);
assertEquals("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", og.pubKeyHex)
og.unread = true
convos.set(og)
val (toPush, seqNo) = convos.push()
assertEquals(1, seqNo)
convos.confirmPushed(seqNo)
assertTrue(convos.needsDump())
assertFalse(convos.needsPush())
val convos2 = ConversationVolatileConfig.newInstance(keyPair.secretKey, toPush)
assertFalse(convos.needsPush())
assertFalse(convos.needsDump())
assertEquals(1, convos.push().seqNo)
assertFalse(convos.needsDump())
val x1 = convos2.getOneToOne(definitelyRealId)!!
assertEquals(nowMs, x1.lastRead)
assertEquals(definitelyRealId, x1.sessionId)
assertEquals(false, x1.unread)
val x2 = convos2.getOpenGroup("http://EXAMPLE.org:5678", "sudokuRoom", openGroupPubKey)!!
assertEquals("http://example.org:5678", x2.baseUrl)
assertEquals("sudokuroom", x2.room)
assertEquals(x2.pubKeyHex, Hex.toStringCondensed(openGroupPubKey))
assertTrue(x2.unread)
val anotherId = "051111111111111111111111111111111111111111111111111111111111111111"
val c2 = convos.getOrConstructLegacyClosedGroup(anotherId)
c2.unread = true
convos2.set(c2)
val c3 = convos.getOrConstructLegacyClosedGroup(
"05cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
)
c3.lastRead = nowMs - 50
convos2.set(c3)
assertTrue(convos2.needsPush())
val (toPush2, seqNo2) = convos2.push()
assertEquals(2, seqNo2)
convos.merge(toPush2)
convos2.confirmPushed(seqNo2)
assertFalse(convos.needsPush())
assertEquals(seqNo2, convos.push().seqNo)
val seen = mutableListOf<String>()
for (conv in listOf(convos, convos2)) {
seen.clear()
assertEquals(4, conv.size())
assertEquals(2, conv.sizeOneToOnes())
assertEquals(1, conv.sizeOpenGroups())
assertEquals(1, conv.sizeLegacyClosedGroups())
assertFalse(conv.empty())
for (convo in conv.all()) {
when (convo) {
is Conversation.OneToOne -> seen.add("1-to-1: ${convo.sessionId}")
is Conversation.OpenGroup -> seen.add("og: ${convo.baseUrl}/r/${convo.room}")
is Conversation.LegacyClosedGroup -> seen.add("cl: ${convo.groupId}")
}
}
assertEquals(listOf(
"1-to-1: " +
"051111111111111111111111111111111111111111111111111111111111111111",
"1-to-1: " +
"055000000000000000000000000000000000000000000000000000000000000000",
"og: http://example.org:5678/r/sudokuroom",
"cl: " +
"05ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
), seen)
}
assertFalse(convos.needsPush())
convos.eraseOneToOne("052000000000000000000000000000000000000000000000000000000000000000")
assertFalse(convos.needsPush())
convos.eraseOneToOne("055000000000000000000000000000000000000000000000000000000000000000")
assertTrue(convos.needsPush())
}
}

View File

@ -2,6 +2,26 @@
#include "conversation.h"
#pragma clang diagnostic push
extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_util_Conversation_00024OpenGroup_00024Companion_parseFullUrl(
JNIEnv *env, jobject thiz, jstring full_url) {
auto bytes = env->GetStringUTFChars(full_url, nullptr);
auto [base, room, pk] = session::config::convo::open_group::parse_full_url(bytes);
env->ReleaseStringUTFChars(full_url, bytes);
jclass clazz = env->FindClass("kotlin/Triple");
jmethodID constructor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V");
auto base_j = env->NewStringUTF(base.data());
auto room_j = env->NewStringUTF(room.data());
auto pk_jbytes = util::bytes_from_ustring(env, pk);
jobject triple = env->NewObject(clazz, constructor, base_j, room_j, pk_jbytes);
return triple;
}
extern "C"
#pragma ide diagnostic ignored "bugprone-reserved-identifier"
JNIEXPORT jobject JNICALL
@ -32,15 +52,7 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_00024Com
return newConfig;
}
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_set(JNIEnv *env,
jobject thiz,
jobject to_store) {
auto conversations = ptrToConvoInfo(env, thiz);
auto one_to_one = deserialize_one_to_one(env, to_store);
conversations->set(*one_to_one);
}
extern "C"
JNIEXPORT jint JNICALL
@ -78,4 +90,43 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_eraseAll
}
return removed;
}
extern "C"
JNIEXPORT jint JNICALL
Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_size(JNIEnv *env,
jobject thiz) {
auto config = ptrToConvoInfo(env, thiz);
return (jint)config->size();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_empty(JNIEnv *env,
jobject thiz) {
auto config = ptrToConvoInfo(env, thiz);
return config->empty();
}
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_set(JNIEnv *env,
jobject thiz,
jobject to_store) {
auto convos = ptrToConvoInfo(env, thiz);
jclass one_to_one = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne");
jclass open_group = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OpenGroup");
jclass legacy_closed_group = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyClosedGroup");
jclass to_store_class = env->GetObjectClass(to_store);
if (to_store_class == one_to_one) {
// store as 1to1
convos->set(*deserialize_one_to_one(env, to_store));
} else if (to_store_class == open_group) {
// store as open_group
convos->set(*deserialize_open_group(env, to_store));
} else if (to_store_class == legacy_closed_group) {
// store as legacy_closed_group
convos->set(*deserialize_legacy_closed_group(env, to_store));
}
}

View File

@ -78,16 +78,18 @@ class ConversationVolatileConfig(pointer: Long): ConfigBase(pointer) {
external fun getOneToOne(pubKeyHex: String): Conversation.OneToOne?
external fun getOrConstructOneToOne(pubKeyHex: String): Conversation.OneToOne
external fun set(toStore: Conversation.OneToOne)
external fun getOpenGroup(baseUrl: String, room: String, pubKeyHex: String): Conversation.OpenGroup?
external fun getOpenGroup(baseUrl: String, room: String, pubKey: ByteArray): Conversation.OpenGroup?
external fun getOrConstructOpenGroup(baseUrl: String, room: String, pubKey: ByteArray): Conversation.OpenGroup
external fun getOrConstructOpenGroup(baseUrl: String, room: String, pubKeyHex: String): Conversation.OpenGroup
external fun getLegacyClosedGroup(groupId: String): Conversation.LegacyClosedGroup?
external fun getOrConstructLegacyClosedGroup(groupId: String): Conversation.LegacyClosedGroup
external fun erase(conversation: Conversation): Boolean
external fun set(toStore: Conversation)
/**
* Erase all conversations that do not satisfy the `predicate`, similar to [MutableList.removeAll]
*/
@ -96,6 +98,9 @@ class ConversationVolatileConfig(pointer: Long): ConfigBase(pointer) {
external fun sizeOneToOnes(): Int
external fun sizeOpenGroups(): Int
external fun sizeLegacyClosedGroups(): Int
external fun size(): Int
external fun empty(): Boolean
external fun all(): List<Conversation>

View File

@ -1,23 +1,35 @@
package network.loki.messenger.libsession_util.util
import org.session.libsignal.utilities.Hex
sealed class Conversation {
abstract val lastRead: Long
abstract val unread: Boolean
abstract var lastRead: Long
abstract var unread: Boolean
data class OneToOne(
val sessionId: String,
override val lastRead: Long,
override val unread: Boolean
override var lastRead: Long,
override var unread: Boolean
): Conversation()
data class OpenGroup(
val baseUrl: String,
val room: String, // lowercase
val pubKey: ByteArray,
override val lastRead: Long,
override val unread: Boolean
override var lastRead: Long,
override var unread: Boolean
) : Conversation() {
val pubKeyHex: String
get() = Hex.toStringCondensed(pubKey)
companion object {
init {
System.loadLibrary("session_util")
}
external fun parseFullUrl(fullUrl: String): Triple<String, String, ByteArray>?
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@ -43,7 +55,7 @@ sealed class Conversation {
data class LegacyClosedGroup(
val groupId: String,
override val lastRead: Long,
override val unread: Boolean
override var lastRead: Long,
override var unread: Boolean
): Conversation()
}