feat: add linux native library support for libsession to enable junit testing (might not be the best way)

This commit is contained in:
0x330a 2023-10-04 09:59:10 +11:00
parent fb2a7a8fed
commit e419024f6b
8 changed files with 98 additions and 29 deletions

View File

@ -21,7 +21,6 @@ import network.loki.messenger.libsession_util.util.UserPic
import org.session.libsession.avatars.AvatarHelper import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.database.StorageProtocol import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.BlindedIdMapping import org.session.libsession.messaging.BlindedIdMapping
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.calls.CallMessageType import org.session.libsession.messaging.calls.CallMessageType
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.AttachmentUploadJob
@ -920,7 +919,6 @@ open class Storage(
val userGroups = configFactory.userGroups ?: return Optional.absent() val userGroups = configFactory.userGroups ?: return Optional.absent()
val convoVolatile = configFactory.convoVolatile ?: return Optional.absent() val convoVolatile = configFactory.convoVolatile ?: return Optional.absent()
val ourSessionId = getUserPublicKey() ?: return Optional.absent() val ourSessionId = getUserPublicKey() ?: return Optional.absent()
val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return Optional.absent()
val groupCreationTimestamp = SnodeAPI.nowWithOffset val groupCreationTimestamp = SnodeAPI.nowWithOffset
@ -939,13 +937,11 @@ open class Storage(
LibSessionGroupMember(ourSessionId, "admin", admin = true) LibSessionGroupMember(ourSessionId, "admin", admin = true)
) )
val groupKeys = GroupKeysConfig.newInstance( members.forEach { groupMembers.set(LibSessionGroupMember(it.hexString(), "member", invitePending = true)) }
userKp.secretKey.asBytes,
Hex.fromStringCondensed(group.groupSessionId.publicKey), val groupKeys = configFactory.constructGroupKeysConfig(group.groupSessionId,
adminKey,
info = groupInfo, info = groupInfo,
members = groupMembers members = groupMembers) ?: return Optional.absent()
)
val newGroupRecipient = group.groupSessionId.hexString() val newGroupRecipient = group.groupSessionId.hexString()
val configTtl = 1 * 24 * 60 * 60 * 1000L // TODO: just testing here, 1 day so we don't fill large space on network val configTtl = 1 * 24 * 60 * 60 * 1000L // TODO: just testing here, 1 day so we don't fill large space on network

View File

@ -230,6 +230,21 @@ class ConfigFactory(
) )
} }
override fun constructGroupKeysConfig(
groupSessionId: SessionId,
info: GroupInfoConfig,
members: GroupMembersConfig
): GroupKeysConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, _) ->
val (userSk, _) = maybeGetUserInfo() ?: return null
GroupKeysConfig.newInstance(
userSk,
Hex.fromStringCondensed(groupSessionId.hexString()),
sk,
info = info,
members = members
)
}
override fun getUserConfigs(): List<ConfigBase> = override fun getUserConfigs(): List<ConfigBase> =
listOfNotNull(user, contacts, convoVolatile, userGroups) listOfNotNull(user, contacts, convoVolatile, userGroups)

View File

@ -48,18 +48,18 @@ class CreateGroupViewModel @Inject constructor(
val members = createGroupState.members val members = createGroupState.members
// do some validation // do some validation
// need a name
if (name.isEmpty()) { if (name.isEmpty()) {
_viewState.postValue( _viewState.postValue(
CreateGroupFragment.ViewState(false, R.string.error) CreateGroupFragment.ViewState(false, R.string.error)
) )
return null return null
} }
// TODO: need at least two members
if (members.isEmpty()) { if (members.size <= 1) {
_viewState.postValue( _viewState.postValue(
CreateGroupFragment.ViewState(false, R.string.error) CreateGroupFragment.ViewState(false, R.string.activity_create_closed_group_not_enough_group_members_error)
) )
return null
} }
// make a group // make a group

View File

@ -1,21 +1,42 @@
package org.thoughtcrime.securesms.groups package org.thoughtcrime.securesms.groups
import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertNotNull
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import network.loki.messenger.libsession_util.util.Sodium
import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.Hex
import org.session.libsignal.utilities.IdPrefix
import org.session.libsignal.utilities.SessionId
import org.thoughtcrime.securesms.MainCoroutineRule import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.database.ConfigDatabase
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.dependencies.ConfigFactory
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@RunWith(MockitoJUnitRunner::class) @RunWith(MockitoJUnitRunner::class)
class ClosedGroupViewTests { class ClosedGroupViewTests {
companion object {
private const val OTHER_ID = "051000000000000000000000000000000000000000000000000000000000000000"
}
private val seed =
Hex.fromStringCondensed("0123456789abcdef0123456789abcdef00000000000000000000000000000000")
private val keyPair = Sodium.ed25519KeyPair(seed)
private val userSessionId = SessionId(IdPrefix.STANDARD, Sodium.ed25519PkToCurve25519(keyPair.pubKey))
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@get:Rule @get:Rule
var mainCoroutineRule = MainCoroutineRule() var mainCoroutineRule = MainCoroutineRule()
@ -24,7 +45,22 @@ class ClosedGroupViewTests {
var taskRule = InstantTaskExecutorRule() var taskRule = InstantTaskExecutorRule()
@Mock lateinit var textSecurePreferences: TextSecurePreferences @Mock lateinit var textSecurePreferences: TextSecurePreferences
@Mock lateinit var storage: Storage lateinit var storage: Storage
@Before
fun setup() {
whenever(textSecurePreferences.getLocalNumber()).thenReturn(userSessionId.hexString())
val context = mock<Context>()
val emptyDb = mock<ConfigDatabase> { db ->
whenever(db.retrieveConfigAndHashes(any(), any())).thenReturn(byteArrayOf())
}
val overridenStorage = Storage(mock(), mock(), ConfigFactory(context, emptyDb) {
keyPair.secretKey to userSessionId.hexString()
}, mock())
storage = spy(overridenStorage) { storage ->
whenever(storage.createNewGroup(any(), any(), any())).thenCallRealMethod()
}
}
@Test @Test
fun `Should error on empty name`() { fun `Should error on empty name`() {
@ -50,6 +86,17 @@ class ClosedGroupViewTests {
assertNotNull(viewModel.viewState.value?.error) assertNotNull(viewModel.viewState.value?.error)
} }
@Test
fun `Should work with valid name and members`() {
val viewModel = createViewModel()
val state = CreateGroupState(
groupName = "group",
groupDescription = "",
members = emptySet()
)
assertNotNull(viewModel.tryCreateGroup(state))
}
private fun createViewModel() = CreateGroupViewModel(textSecurePreferences, storage) private fun createViewModel() = CreateGroupViewModel(textSecurePreferences, storage)
} }

View File

@ -42,18 +42,25 @@ add_library( # Sets the name of the library.
# Provides a relative path to your source file(s). # Provides a relative path to your source file(s).
${SOURCES}) ${SOURCES})
if (LINUX)
message("Linux machine detected")
set(JAVA_INCLUDE_PATH "$ENV{JAVA_HOME}/include;$ENV{JAVA_HOME}/include/linux")
find_package(JNI REQUIRED)
include_directories(${JAVA_INCLUDE_PATH})
endif()
# Searches for a specified prebuilt library and stores the path as a # Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by # variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library # default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before # you want to add. CMake verifies that the library exists before
# completing its build. # completing its build.
find_library( # Sets the name of the path variable. #find_library( # Sets the name of the path variable.
log-lib # log-lib
#
# Specifies the name of the NDK library that # # Specifies the name of the NDK library that
# you want CMake to locate. # # you want CMake to locate.
log) # log)
# Specifies libraries CMake should link to your target library. You # Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this # can link multiple libraries, such as libraries you define in this
@ -66,4 +73,5 @@ target_link_libraries( # Specifies the target library.
libsession::crypto libsession::crypto
# Links the target library to the log library # Links the target library to the log library
# included in the NDK. # included in the NDK.
${log-lib}) )
#${log-lib})

View File

@ -203,7 +203,6 @@ inline jobject iterator_as_java_stack(JNIEnv *env, const session::config::UserGr
} else if (auto* community = std::get_if<session::config::community_info>(&item)) { } else if (auto* community = std::get_if<session::config::community_info>(&item)) {
serialized = serialize_community_info(env, *community); serialized = serialize_community_info(env, *community);
} else if (auto* closed = std::get_if<session::config::group_info>(&item)) { } else if (auto* closed = std::get_if<session::config::group_info>(&item)) {
LOGD("Item is closed group");
serialized = serialize_closed_group_info(env, *closed); serialized = serialize_closed_group_info(env, *closed);
} }
if (serialized != nullptr) { if (serialized != nullptr) {
@ -219,8 +218,6 @@ JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_UserGroupsConfig_all(JNIEnv *env, jobject thiz) { Java_network_loki_messenger_libsession_1util_UserGroupsConfig_all(JNIEnv *env, jobject thiz) {
std::lock_guard lock{util::util_mutex_}; std::lock_guard lock{util::util_mutex_};
auto conf = ptrToUserGroups(env, thiz); auto conf = ptrToUserGroups(env, thiz);
bool isFin = conf->begin().done();
LOGD("Group iterator has more: %d", isFin);
jobject all_stack = iterator_as_java_stack(env, conf->begin(), conf->end()); jobject all_stack = iterator_as_java_stack(env, conf->begin(), conf->end());
return all_stack; return all_stack;
} }

View File

@ -2,6 +2,7 @@
#define SESSION_ANDROID_UTIL_H #define SESSION_ANDROID_UTIL_H
#include <jni.h> #include <jni.h>
#include <mutex>
#include <array> #include <array>
#include <optional> #include <optional>
#include "session/types.hpp" #include "session/types.hpp"
@ -11,13 +12,13 @@
#include "session/config/profile_pic.hpp" #include "session/config/profile_pic.hpp"
#include "session/config/user_groups.hpp" #include "session/config/user_groups.hpp"
#include "session/config/expiring.hpp" #include "session/config/expiring.hpp"
#include <android/log.h> //#include <android/log.h>
#define LOG_TAG "libsession-jni" //#define LOG_TAG "libsession-jni"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) //#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) //#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) //#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) //#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
namespace util { namespace util {
extern std::mutex util_mutex_; extern std::mutex util_mutex_;

View File

@ -34,6 +34,11 @@ interface ConfigFactoryProtocol {
) )
fun scheduleUpdate(destination: Destination) fun scheduleUpdate(destination: Destination)
fun constructGroupKeysConfig(
groupSessionId: SessionId,
info: GroupInfoConfig,
members: GroupMembersConfig
): GroupKeysConfig?
} }
interface ConfigFactoryUpdateListener { interface ConfigFactoryUpdateListener {