Skip to content
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

add workflow for rust test #8366

Merged
merged 9 commits into from
May 5, 2023
Merged
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
102 changes: 102 additions & 0 deletions .github/workflows/tests-rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Test

on:
pull_request: { }
push:
branches: [ main, develop ]
paths-ignore:
- '.github/**'

# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 4 --no-daemon

jobs:
tests:
name: Runs all tests with rust crypto
runs-on: buildjet-4vcpu-ubuntu-2204
timeout-minutes: 90 # We might need to increase it if the time for tests grows
strategy:
matrix:
api-level: [28]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-rust-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-rust-{0}', github.sha) || format('unit-tests-rust-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
with:
lfs: true
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '11'
- uses: gradle/gradle-build-action@v2
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/develop' }}

# - name: Run screenshot tests
# run: ./gradlew verifyScreenshots $CI_GRADLE_ARG_PROPERTIES

# - name: Archive Screenshot Results on Error
# if: failure()
# uses: actions/upload-artifact@v3
# with:
# name: screenshot-results
# path: |
# **/out/failures/
# **/build/reports/tests/*UnitTest/

- uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: michaelkaye/[email protected]
with:
uploadLogs: true
httpPort: 8080
disableRateLimiting: true
public_baseurl: "http://10.0.2.2:8080/"

- name: Run all the codecoverage tests at once
uses: reactivecircus/android-emulator-runner@v2
# continue-on-error: true
with:
api-level: ${{ matrix.api-level }}
arch: x86
profile: Nexus 5X
target: playstore
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
# emulator-build: 7425822
script: |
./gradlew gatherGplayRustCryptoDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
./gradlew instrumentationTestsRustWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES

- name: Upload Rust Integration Test Report Log
uses: actions/upload-artifact@v3
if: always()
with:
name: integration-test-rust-error-results
path: |
*/build/outputs/androidTest-results/connected/
*/build/reports/androidTests/connected/

# For now ignore sonar
# - name: Publish results to Sonar
# env:
# GITHUB_TOKEN: ${{ secrets.SONARQUBE_GITHUB_API_TOKEN }} # Needed to get PR information, if any
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
# if: ${{ always() && env.GITHUB_TOKEN != '' && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
# run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES

- name: Format unit test results
if: always()
run: python3 ./tools/ci/render_test_output.py unit ./**/build/test-results/**/*.xml


1 change: 1 addition & 0 deletions changelog.d/8366.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CI: Add workflow to run test with crypto flavor
6 changes: 6 additions & 0 deletions coverage.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,9 @@ task instrumentationTestsWithCoverage(type: GradleBuild) {
startParameter.projectProperties['android.testInstrumentationRunnerArguments.notPackage'] = 'im.vector.app.ui'
tasks = [':vector-app:connectedGplayKotlinCryptoDebugAndroidTest', ':vector:connectedKotlinCryptoDebugAndroidTest', 'matrix-sdk-android:connectedKotlinCryptoDebugAndroidTest']
}

task instrumentationTestsRustWithCoverage(type: GradleBuild) {
startParameter.projectProperties.coverage = "true"
startParameter.projectProperties['android.testInstrumentationRunnerArguments.notPackage'] = 'im.vector.app.ui'
tasks = [':vector-app:connectedGplayRustCryptoDebugAndroidTest', ':vector:connectedRustCryptoDebugAndroidTest', 'matrix-sdk-android:connectedRustCryptoDebugAndroidTest']
}
2 changes: 1 addition & 1 deletion matrix-sdk-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ dependencies {

implementation libs.google.phonenumber

rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.5")
rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.7")
// rustCryptoApi project(":library:rustCrypto")

testImplementation libs.tests.junit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
Expand Down Expand Up @@ -128,6 +129,7 @@ class KeysBackupTest : InstrumentedTest {
@Test
fun createKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
Log.d("#E2E", "Initializing crosssigning for ${bobSession.myUserId.take(8)}")
cryptoTestHelper.initializeCrossSigning(bobSession)

val keysBackup = bobSession.cryptoService().keysBackupService()
Expand All @@ -136,12 +138,14 @@ class KeysBackupTest : InstrumentedTest {

assertFalse(keysBackup.isEnabled())

Log.d("#E2E", "prepareKeysBackupVersion")
val megolmBackupCreationInfo =
keysBackup.prepareKeysBackupVersion(null, null)

assertFalse(keysBackup.isEnabled())

// Create the version
Log.d("#E2E", "createKeysBackupVersion")
val version = keysBackup.createKeysBackupVersion(megolmBackupCreationInfo)

// Backup must be enable now
Expand All @@ -151,6 +155,7 @@ class KeysBackupTest : InstrumentedTest {
val versionResult = keysBackup.getVersion(version.version)
val trust = keysBackup.getKeysBackupTrust(versionResult!!)

Log.d("#E2E", "Check backup signatures")
assertEquals("Should have 2 signatures", 2, trust.signatures.size)

trust.signatures
Expand Down Expand Up @@ -432,9 +437,13 @@ class KeysBackupTest : InstrumentedTest {
.keysBackupService()
.getKeysBackupTrust(keysVersionResult)

// - It must be trusted and must have 2 signatures now
// The backup should have a valid signature from that device now
assertTrue(keysBackupVersionTrust.usable)
assertEquals(2, keysBackupVersionTrust.signatures.size)
val signature = keysBackupVersionTrust.signatures
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
assertNotNull(signature)
assertTrue(signature!!.valid)

stateObserver.stopAndCheckStates(null)
}
Expand Down Expand Up @@ -492,9 +501,16 @@ class KeysBackupTest : InstrumentedTest {
.keysBackupService()
.getKeysBackupTrust(keysVersionResult)

// - It must be trusted and must have 2 signatures now
// // - It must be trusted and must have 2 signatures now
// assertTrue(keysBackupVersionTrust.usable)
// assertEquals(2, keysBackupVersionTrust.signatures.size)
// The backup should have a valid signature from that device now
assertTrue(keysBackupVersionTrust.usable)
assertEquals(2, keysBackupVersionTrust.signatures.size)
val signature = keysBackupVersionTrust.signatures
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
assertNotNull(signature)
assertTrue(signature!!.valid)

stateObserver.stopAndCheckStates(null)
}
Expand Down Expand Up @@ -590,9 +606,17 @@ class KeysBackupTest : InstrumentedTest {
.keysBackupService()
.getKeysBackupTrust(keysVersionResult)

// - It must be trusted and must have 2 signatures now
// // - It must be trusted and must have 2 signatures now
// assertTrue(keysBackupVersionTrust.usable)
// assertEquals(2, keysBackupVersionTrust.signatures.size)

// - It must be trusted and signed by current device
assertTrue(keysBackupVersionTrust.usable)
assertEquals(2, keysBackupVersionTrust.signatures.size)
val signature = keysBackupVersionTrust.signatures
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
assertNotNull(signature)
assertTrue(signature!!.valid)

stateObserver.stopAndCheckStates(null)
}
Expand Down Expand Up @@ -672,11 +696,16 @@ class KeysBackupTest : InstrumentedTest {
*/
@Test
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->

val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)

val password = "password"

val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
Assume.assumeTrue(
"Can't report progress same way in rust",
testData.cryptoTestData.firstSession.cryptoService().name() != "rust-sdk"
)

// - Restore the e2e backup with the password
val steps = ArrayList<StepProgressListener.Step>()
Expand Down Expand Up @@ -887,6 +916,7 @@ class KeysBackupTest : InstrumentedTest {
* -> It must success
*/
@Test
@Ignore("Instable on both flavors")
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)

Expand Down Expand Up @@ -930,9 +960,13 @@ class KeysBackupTest : InstrumentedTest {
assertEquals("Backup state must be NotTrusted", KeysBackupState.NotTrusted, keysBackup2.getState())
assertFalse("Backup should not be enabled", keysBackup2.isEnabled())

val signatures = keysBackup2.getCurrentVersion()?.toKeysVersionResult()?.getAuthDataAsMegolmBackupAuthData()?.signatures
Log.d("#E2E", "keysBackup2 signatures: $signatures")

// - Validate the old device from the new one
cryptoTestHelper.verifyNewSession(cryptoTestData.firstSession, aliceSession2)

cryptoTestData.firstSession.cryptoService().keysBackupService().checkAndStartKeysBackup()
// -> Backup should automatically enable on the new device
suspendCancellableCoroutine<Unit> { continuation ->
val listener = object : KeysBackupStateListener {
Expand All @@ -954,11 +988,11 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)

// aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
testHelper.retryPeriodically {
testHelper.retryWithBackoff {
keysBackup2.getTotalNumbersOfKeys() == keysBackup2.getTotalNumbersOfBackedUpKeys()
}

testHelper.retryPeriodically {
testHelper.retryWithBackoff {
aliceSession2.cryptoService().keysBackupService().getState() == KeysBackupState.ReadyToBackUp
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -65,6 +66,16 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {

@Test
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() {
testMigrate(false)
}

@Test
@Ignore("We don't migrate group session for now, and it makes test suite unstable")
fun given_a_valid_crypto_store_realm_file_no_lazy_then_migration_should_be_successful() {
testMigrate(true)
}

private fun testMigrate(migrateGroupSessions: Boolean) {
val realmName = "crypto_store_migration_16.realm"
val migration = RealmCryptoStoreMigration(object : Clock {
override fun epochMillis() = 0L
Expand All @@ -86,7 +97,7 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {
val deviceId = metaData.deviceId!!
val olmAccount = metaData.getOlmAccount()!!

val extractor = MigrateEAtoEROperation()
val extractor = MigrateEAtoEROperation(migrateGroupSessions)

val targetFile = File(configurationFactory.root, "rust-sdk")

Expand All @@ -101,14 +112,16 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {
assertTrue(crossSigningStatus.hasSelfSigning)
assertTrue(crossSigningStatus.hasUserSigning)

val inboundGroupSessionEntities = realm!!.where<OlmInboundGroupSessionEntity>().findAll()
assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt())
if (migrateGroupSessions) {
val inboundGroupSessionEntities = realm!!.where<OlmInboundGroupSessionEntity>().findAll()
assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt())

val backedUpInboundGroupSessionEntities = realm!!
.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true)
.findAll()
assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt())
val backedUpInboundGroupSessionEntities = realm!!
.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true)
.findAll()
assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt())
}
}

// @Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import io.realm.RealmConfiguration
import timber.log.Timber
import java.io.File

class MigrateEAtoEROperation {
class MigrateEAtoEROperation(private val migrateGroupSessions: Boolean = false) {

fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File, passphrase: String?): File {
// to remove unused warning
Timber.v("Not used in kotlin crypto $cryptoRealm ${"*".repeat(passphrase?.length ?: 0)}")
Timber.v("Not used in kotlin crypto $cryptoRealm ${"*".repeat(passphrase?.length ?: 0)} lazy:$migrateGroupSessions")
// no op in kotlinCrypto
return sessionFilesDir
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import org.matrix.rustcomponents.sdk.crypto.RequestType
import org.matrix.rustcomponents.sdk.crypto.RoomKeyCounts
import org.matrix.rustcomponents.sdk.crypto.ShieldColor
import org.matrix.rustcomponents.sdk.crypto.ShieldState
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
import org.matrix.rustcomponents.sdk.crypto.setLogger
import timber.log.Timber
import java.io.File
Expand Down Expand Up @@ -916,7 +917,7 @@ internal class OlmMachine @Inject constructor(
}

@Throws(CryptoStoreException::class)
suspend fun checkAuthDataSignature(authData: KeysAlgorithmAndData): Boolean {
suspend fun checkAuthDataSignature(authData: KeysAlgorithmAndData): SignatureVerification {
return withContext(coroutineDispatchers.computation) {
val adapter = moshi
.newBuilder()
Expand All @@ -929,7 +930,7 @@ internal class OlmMachine @Inject constructor(
)
)

inner.verifyBackup(serializedAuthData).trusted
inner.verifyBackup(serializedAuthData)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
import org.matrix.rustcomponents.sdk.crypto.Request
import javax.inject.Inject

internal class RustCrossSigningService @Inject constructor(
private val olmMachine: OlmMachine,
private val outgoingRequestsProcessor: OutgoingRequestsProcessor,
private val computeShieldForGroup: ComputeShieldForGroupUseCase
) : CrossSigningService {

Expand Down Expand Up @@ -78,6 +81,10 @@ internal class RustCrossSigningService @Inject constructor(
* Users needs to enter credentials
*/
override suspend fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?) {
// ensure our keys are sent before initialising
outgoingRequestsProcessor.processOutgoingRequests(olmMachine) {
it is Request.KeysUpload
}
olmMachine.bootstrapCrossSigning(uiaInterceptor)
}

Expand Down
Loading