Merge pull request #66 from vector-im/feature/bma/sonar

Sonar / Kover / Codecov
This commit is contained in:
Benoit Marty 2023-02-02 11:33:12 +01:00 committed by GitHub
commit a4f9354e8a
14 changed files with 156 additions and 16 deletions

View file

@ -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

View file

@ -1,3 +1,11 @@
[![Latest build](https://github.com/vector-im/element-x-android/actions/workflows/build.yml/badge.svg?query=branch%3Adevelop)](https://github.com/vector-im/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[![codecov](https://codecov.io/github/vector-im/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android)
[![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org)
[![Weblate](https://translate.element.io/widgets/element-android/-/svg-badge.svg)](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.

View file

@ -1,5 +1,3 @@
/*
* Copyright (c) 2022 New Vector Ltd
*

View file

@ -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\$*",
*/
)
)
}
}
}

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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"

View file

@ -26,4 +26,5 @@ android {
dependencies {
implementation(libs.timber)
implementation(libs.androidx.corektx)
implementation(projects.libraries.core)
}

View file

@ -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")
}
}
)
}

View file

@ -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 {

View file

@ -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,

View file

@ -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)
}
}