Merge pull request #66 from vector-im/feature/bma/sonar
Sonar / Kover / Codecov
This commit is contained in:
commit
a4f9354e8a
14 changed files with 156 additions and 16 deletions
25
.github/workflows/tests.yml
vendored
25
.github/workflows/tests.yml
vendored
|
|
@ -23,6 +23,17 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- name: Run tests
|
||||
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Generate kover report
|
||||
if: always()
|
||||
run: ./gradlew koverMergedReport $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
- name: Archive kover report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kover-results
|
||||
path: |
|
||||
**/build/reports/kover/merged
|
||||
|
||||
- name: Archive test results on error
|
||||
if: failure()
|
||||
|
|
@ -32,3 +43,17 @@ jobs:
|
|||
path: |
|
||||
**/out/failures/
|
||||
**/build/reports/tests/*UnitTest/
|
||||
|
||||
- name: Publish results to Sonar
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
|
||||
if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
|
||||
run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
# https://github.com/codecov/codecov-action
|
||||
- name: Upload coverage reports to codecov
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v3
|
||||
# with:
|
||||
# files: build/reports/kover/merged/xml/report.xml
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
[](https://github.com/vector-im/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop)
|
||||
[](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
|
||||
[](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
|
||||
[](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
|
||||
[](https://codecov.io/github/vector-im/element-x-android)
|
||||
[](https://matrix.to/#/#element-android:matrix.org)
|
||||
[](https://translate.element.io/engage/element-android/?utm_source=widget)
|
||||
|
||||
# element-x-android-poc
|
||||
|
||||
Proof Of Concept to run a Matrix client on Android devices using the Matrix Rust Sdk and Jetpack compose.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ plugins {
|
|||
alias(libs.plugins.detekt)
|
||||
alias(libs.plugins.ktlint)
|
||||
alias(libs.plugins.dependencygraph)
|
||||
alias(libs.plugins.sonarqube)
|
||||
alias(libs.plugins.kover)
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean").configure {
|
||||
|
|
@ -108,3 +110,68 @@ allprojects {
|
|||
plugin("org.owasp.dependencycheck")
|
||||
}
|
||||
}
|
||||
|
||||
// To run a sonar analysis:
|
||||
// Run './gradlew sonar -Dsonar.login=<SONAR_LOGIN>'
|
||||
// The SONAR_LOGIN is stored in passbolt as Token Sonar Cloud Bma
|
||||
// Sonar result can be found here: https://sonarcloud.io/project/overview?id=vector-im_element-x-android
|
||||
sonar {
|
||||
properties {
|
||||
property("sonar.projectName", "element-x-android")
|
||||
property("sonar.projectKey", "vector-im_element-x-android")
|
||||
property("sonar.host.url", "https://sonarcloud.io")
|
||||
property("sonar.projectVersion", "1.0") // TODO project(":app").android.defaultConfig.versionName)
|
||||
property("sonar.sourceEncoding", "UTF-8")
|
||||
property("sonar.links.homepage", "https://github.com/vector-im/element-x-android/")
|
||||
property("sonar.links.ci", "https://github.com/vector-im/element-x-android/actions")
|
||||
property("sonar.links.scm", "https://github.com/vector-im/element-x-android/")
|
||||
property("sonar.links.issue", "https://github.com/vector-im/element-x-android/issues")
|
||||
property("sonar.organization", "new_vector_ltd_organization")
|
||||
property("sonar.login", if (project.hasProperty("SONAR_LOGIN")) project.property("SONAR_LOGIN")!! else "invalid")
|
||||
|
||||
// exclude source code from analyses separated by a colon (:)
|
||||
// Exclude Java source
|
||||
property("sonar.exclusions", "**/BugReporterMultipartBody.java")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
val projectDir = projectDir.toString()
|
||||
sonar {
|
||||
properties {
|
||||
// Note: folders `kotlin` are not supported (yet), I asked on their side: https://community.sonarsource.com/t/82824
|
||||
// As a workaround provide the path in `sonar.sources` property.
|
||||
if (File("$projectDir/src/main/kotlin").exists()) {
|
||||
property("sonar.sources", "src/main/kotlin")
|
||||
}
|
||||
if (File("$projectDir/src/test/kotlin").exists()) {
|
||||
property("sonar.tests", "src/test/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply(plugin = "kover")
|
||||
}
|
||||
|
||||
// Run `./gradlew koverMergedHtmlReport` to get report at ./build/reports/kover
|
||||
// Run `./gradlew koverMergedReport` to also get XML report
|
||||
koverMerged {
|
||||
enable()
|
||||
|
||||
filters {
|
||||
classes {
|
||||
excludes.addAll(
|
||||
listOf(
|
||||
/*
|
||||
"*Fragment",
|
||||
"*Fragment\$*",
|
||||
"*Activity",
|
||||
"*Activity\$*",
|
||||
*/
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(libs.accompanist.pager)
|
||||
implementation(libs.accompanist.pagerindicator)
|
||||
testImplementation(libs.test.junit)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ package io.element.android.features.rageshake.logs
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
|
@ -37,6 +39,7 @@ import java.util.logging.Logger
|
|||
class VectorFileLogger(
|
||||
context: Context,
|
||||
// private val vectorPreferences: VectorPreferences
|
||||
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
) : Timber.Tree() {
|
||||
|
||||
companion object {
|
||||
|
|
@ -82,7 +85,7 @@ class VectorFileLogger(
|
|||
|
||||
for (i in 0..15) {
|
||||
val file = File(cacheDirectory, "elementLogs.${i}.txt")
|
||||
tryOrNull { file.delete() }
|
||||
file.safeDelete()
|
||||
}
|
||||
|
||||
fileHandler = tryOrNull(
|
||||
|
|
@ -101,14 +104,14 @@ class VectorFileLogger(
|
|||
fun reset() {
|
||||
// Delete all files
|
||||
getLogFiles().map {
|
||||
tryOrNull { it.delete() }
|
||||
it.safeDelete()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
fileHandler ?: return
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
GlobalScope.launch(dispatcher) {
|
||||
if (skipLog(priority)) return@launch
|
||||
if (t != null) {
|
||||
logToFile(t)
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@ import io.element.android.features.rageshake.crash.CrashDataStore
|
|||
import io.element.android.features.rageshake.logs.VectorFileLogger
|
||||
import io.element.android.features.rageshake.screenshot.ScreenshotHolder
|
||||
import io.element.android.libraries.androidutils.file.compressFile
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.extensions.toOnOff
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -54,6 +55,7 @@ class BugReporter @Inject constructor(
|
|||
@ApplicationContext private val context: Context,
|
||||
private val screenshotHolder: ScreenshotHolder,
|
||||
private val crashDataStore: CrashDataStore,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
/*
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val versionProvider: VersionProvider,
|
||||
|
|
@ -168,7 +170,7 @@ class BugReporter @Inject constructor(
|
|||
coroutineScope.launch {
|
||||
var serverError: String? = null
|
||||
var reportURL: String? = null
|
||||
withContext(Dispatchers.IO) {
|
||||
withContext(coroutineDispatchers.io) {
|
||||
var bugDescription = theBugDescription
|
||||
val crashCallStack = crashDataStore.crashInfo().first()
|
||||
|
||||
|
|
@ -418,12 +420,12 @@ class BugReporter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
withContext(coroutineDispatchers.main) {
|
||||
mBugReportCall = null
|
||||
|
||||
// delete when the bug report has been successfully sent
|
||||
for (file in mBugReportFiles) {
|
||||
file.delete()
|
||||
file.safeDelete()
|
||||
}
|
||||
|
||||
if (null != listener) {
|
||||
|
|
@ -498,7 +500,7 @@ class BugReporter @Inject constructor(
|
|||
val logCatErrFile = File(context.cacheDir.absolutePath, if (isErrorLogcat) LOG_CAT_ERROR_FILENAME else LOG_CAT_FILENAME)
|
||||
|
||||
if (logCatErrFile.exists()) {
|
||||
logCatErrFile.delete()
|
||||
logCatErrFile.safeDelete()
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.rageshake.screenshot
|
|||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import io.element.android.libraries.androidutils.bitmap.writeBitmap
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
|
|
@ -38,6 +39,6 @@ class ScreenshotHolder @Inject constructor(
|
|||
fun getFile() = file.takeIf { it.exists() && it.length() > 0 }
|
||||
|
||||
fun reset() {
|
||||
file.delete()
|
||||
file.safeDelete()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,3 +144,5 @@ dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencyc
|
|||
stem = { id = "com.likethesalad.stem", version.ref = "stem" }
|
||||
stemlibrary = { id = "com.likethesalad.stem-library", version.ref = "stem" }
|
||||
paparazzi = "app.cash.paparazzi:1.2.0"
|
||||
sonarqube = "org.sonarqube:3.5.0.2730"
|
||||
kover = "org.jetbrains.kotlinx.kover:0.6.1"
|
||||
|
|
|
|||
|
|
@ -26,4 +26,5 @@ android {
|
|||
dependencies {
|
||||
implementation(libs.timber)
|
||||
implementation(libs.androidx.corektx)
|
||||
implementation(projects.libraries.core)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.file
|
||||
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
fun File.safeDelete() {
|
||||
tryOrNull(
|
||||
onError = {
|
||||
Timber.e(it, "Error, unable to delete file $path")
|
||||
},
|
||||
operation = {
|
||||
if (delete().not()) {
|
||||
Timber.w("Warning, unable to delete file $path")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ fun compressFile(file: File): File? {
|
|||
val dstFile = file.resolveSibling(file.name + ".gz")
|
||||
|
||||
if (dstFile.exists()) {
|
||||
dstFile.delete()
|
||||
dstFile.safeDelete()
|
||||
}
|
||||
|
||||
return try {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ internal class RustMatrixClient internal constructor(
|
|||
.addView(slidingSyncView)
|
||||
.build()
|
||||
|
||||
private val slidingSyncObserverProxy = SlidingSyncObserverProxy(coroutineScope, dispatchers)
|
||||
private val slidingSyncObserverProxy = SlidingSyncObserverProxy(coroutineScope)
|
||||
private val roomSummaryDataSource: RustRoomSummaryDataSource =
|
||||
RustRoomSummaryDataSource(
|
||||
slidingSyncObserverProxy.updateSummaryFlow,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.sync
|
||||
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
|
@ -30,7 +29,6 @@ private const val BUFFER_SIZE = 64
|
|||
|
||||
class SlidingSyncObserverProxy(
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers
|
||||
) : SlidingSyncObserver {
|
||||
|
||||
private val updateSummaryMutableFlow =
|
||||
|
|
@ -39,7 +37,7 @@ class SlidingSyncObserverProxy(
|
|||
|
||||
override fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||
if (summary.rooms.isEmpty()) return
|
||||
coroutineScope.launch(coroutineDispatchers.io) {
|
||||
coroutineScope.launch {
|
||||
updateSummaryMutableFlow.emit(summary)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue