![3821-ryan_harg@users.noreply.dev.funkwhale.audio](/assets/img/avatar_default.png)
21 changed files with 465 additions and 131 deletions
@ -1,51 +1,82 @@ |
|||||||
image: jangrewe/gitlab-ci-android |
image: jangrewe/gitlab-ci-android |
||||||
|
|
||||||
stages: |
variables: |
||||||
- build |
JACOCO_CSV_LOCATION: '$CI_PROJECT_DIR/app/build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.csv' |
||||||
- deploy |
|
||||||
|
|
||||||
|
stages: |
||||||
|
- test |
||||||
|
- visualize |
||||||
|
- build |
||||||
|
- deploy |
||||||
|
|
||||||
.build: |
.gradle-default: |
||||||
stage: build |
stage: build |
||||||
before_script: |
before_script: |
||||||
- export GRADLE_USER_HOME=$(pwd)/.gradle |
- export GRADLE_USER_HOME=$(pwd)/.gradle |
||||||
- chmod +x ./gradlew |
- chmod +x ./gradlew |
||||||
- mkdir -p .android && touch .android/repositories.cfg |
- mkdir -p .android && touch .android/repositories.cfg |
||||||
script: |
script: |
||||||
- echo "Overwrite me" |
- echo "Overwrite me" |
||||||
|
|
||||||
cache: |
cache: |
||||||
key: ${CI_PROJECT_ID} |
key: ${CI_PROJECT_ID} |
||||||
paths: |
paths: |
||||||
- .gradle/ |
- .gradle/ |
||||||
|
|
||||||
|
.build: |
||||||
|
extends: .gradle-default |
||||||
|
artifacts: |
||||||
|
paths: |
||||||
|
- app/build/outputs/apk/debug/app-debug.apk |
||||||
|
|
||||||
|
test: |
||||||
|
extends: .gradle-default |
||||||
|
stage: test |
||||||
|
script: |
||||||
|
- ./gradlew test testDebugUnitTestCoverage |
||||||
|
- awk -F"," '{ instructions += $4 + $5; covered += $5 } END { print covered, "/", instructions, " instructions covered"; print 100*covered/instructions, "% covered" }' $JACOCO_CSV_LOCATION |
||||||
artifacts: |
artifacts: |
||||||
|
reports: |
||||||
|
junit: app/build/test-results/test**/TEST-*.xml |
||||||
paths: |
paths: |
||||||
- app/build/outputs/apk/debug/app-debug.apk |
- app/build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.xml |
||||||
|
|
||||||
|
coverage: |
||||||
|
stage: visualize |
||||||
|
image: gjrtimmer/jacoco2cobertura:1.0.8 |
||||||
|
script: |
||||||
|
# convert report from jacoco to cobertura, use relative project path |
||||||
|
- 'python /opt/cover2cover.py app/build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.xml $CI_PROJECT_DIR/app/src/main/java > app/build/reports/cobertura.xml' |
||||||
|
needs: [ "test" ] |
||||||
|
dependencies: |
||||||
|
- test |
||||||
|
artifacts: |
||||||
|
reports: |
||||||
|
cobertura: app/build/reports/cobertura.xml |
||||||
|
|
||||||
build-develop: |
build-develop: |
||||||
extends: .build |
extends: .build |
||||||
script: |
script: |
||||||
- echo -n $SIGNING_KEY_STORE | base64 -d > app/android.keystore |
- echo -n $SIGNING_KEY_STORE | base64 -d > app/android.keystore |
||||||
- ./gradlew assembleDebug -Psigning.store=android.keystore -Psigning.store_passphrase=$SIGNING_KEY_PASS -Psigning.key_passphrase=$SIGNING_KEY_PASS |
- ./gradlew assembleDebug -Psigning.store=android.keystore -Psigning.store_passphrase=$SIGNING_KEY_PASS -Psigning.key_passphrase=$SIGNING_KEY_PASS |
||||||
only: |
only: |
||||||
- develop |
- develop |
||||||
|
|
||||||
build-bleeding-edge: |
build-bleeding-edge: |
||||||
extends: .build |
extends: .build |
||||||
script: |
script: |
||||||
- ./gradlew assembleDebug |
- ./gradlew assembleDebug |
||||||
except: |
except: |
||||||
- develop |
- develop |
||||||
|
|
||||||
deploy-develop: |
deploy-develop: |
||||||
stage: deploy |
stage: deploy |
||||||
only: |
only: |
||||||
- develop |
- develop |
||||||
script: |
script: |
||||||
- eval `ssh-agent -s` |
- eval `ssh-agent -s` |
||||||
- ssh-add <(echo "$SSH_PRIVATE_KEY") |
- ssh-add <(echo "$SSH_PRIVATE_KEY") |
||||||
- scp -o StrictHostKeyChecking=no app/build/outputs/apk/debug/app-debug.apk fdroid@apps.funkwhale.audio:/srv/fdroid/fdroid/develop/repo/audio.funkwhale.ffa.dev-$CI_COMMIT_SHORT_SHA.apk |
- scp -o StrictHostKeyChecking=no app/build/outputs/apk/debug/app-debug.apk fdroid@apps.funkwhale.audio:/srv/fdroid/fdroid/develop/repo/audio.funkwhale.ffa.dev-$CI_COMMIT_SHORT_SHA.apk |
||||||
- ssh -o StrictHostKeyChecking=no fdroid@apps.funkwhale.audio 'docker run --rm -u $(id -u):$(id -g) -v /srv/fdroid/fdroid/develop:/repo registry.gitlab.com/fdroid/docker-executable-fdroidserver:master update' |
- ssh -o StrictHostKeyChecking=no fdroid@apps.funkwhale.audio 'docker run --rm -u $(id -u):$(id -g) -v /srv/fdroid/fdroid/develop:/repo registry.gitlab.com/fdroid/docker-executable-fdroidserver:master update' |
||||||
tags: |
tags: |
||||||
- shell |
- shell |
||||||
|
@ -0,0 +1,75 @@ |
|||||||
|
package audio.funkwhale.ffa.playback |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.net.Uri |
||||||
|
import audio.funkwhale.ffa.utils.OAuth |
||||||
|
import audio.funkwhale.util.MockKJUnitRunner |
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec |
||||||
|
import com.google.android.exoplayer2.upstream.HttpDataSource |
||||||
|
import com.google.android.exoplayer2.upstream.TransferListener |
||||||
|
import io.mockk.every |
||||||
|
import io.mockk.impl.annotations.InjectMockKs |
||||||
|
import io.mockk.impl.annotations.MockK |
||||||
|
import io.mockk.mockk |
||||||
|
import io.mockk.verify |
||||||
|
import org.junit.Test |
||||||
|
import org.junit.runner.RunWith |
||||||
|
import strikt.api.expectThat |
||||||
|
import strikt.assertions.isEqualTo |
||||||
|
|
||||||
|
@RunWith(MockKJUnitRunner::class) |
||||||
|
class OAuthDatasourceTest { |
||||||
|
|
||||||
|
@InjectMockKs |
||||||
|
private lateinit var datasource: OAuthDatasource |
||||||
|
|
||||||
|
@MockK |
||||||
|
private lateinit var context: Context |
||||||
|
|
||||||
|
@MockK |
||||||
|
private lateinit var http: HttpDataSource |
||||||
|
|
||||||
|
@MockK |
||||||
|
private lateinit var oAuth: OAuth |
||||||
|
|
||||||
|
private var dataSpec: DataSpec = DataSpec(Uri.EMPTY) |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `open() should set accessToken and delegate to http dataSource`() { |
||||||
|
every { http.open(any()) } returns 0 |
||||||
|
every { oAuth.tryRefreshAccessToken(any(), any()) } returns true |
||||||
|
every { oAuth.state().accessToken } returns "accessToken" |
||||||
|
|
||||||
|
datasource.open(dataSpec) |
||||||
|
verify { http.open(dataSpec) } |
||||||
|
verify { http.setRequestProperty("Authorization", "Bearer accessToken") } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `close() should delegate to http dataSource`() { |
||||||
|
datasource.close() |
||||||
|
verify { http.close() } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `addTransferListener() should delegate to http dataSource`() { |
||||||
|
val transferListener = mockk<TransferListener>() |
||||||
|
datasource.addTransferListener(transferListener) |
||||||
|
verify { http.addTransferListener(transferListener) } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `read() should delegate to http dataSource`() { |
||||||
|
every { http.read(any(), any(), any()) } returns 0 |
||||||
|
datasource.read("123".encodeToByteArray(), 1, 2) |
||||||
|
verify { http.read("123".encodeToByteArray(), 1, 2) } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `getUri() should delegate to http dataSource`() { |
||||||
|
every { http.uri } returns Uri.EMPTY |
||||||
|
val result = datasource.uri |
||||||
|
verify { http.uri } |
||||||
|
expectThat(result).isEqualTo(Uri.EMPTY) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package audio.funkwhale.util |
||||||
|
|
||||||
|
import io.mockk.MockKAnnotations |
||||||
|
import io.mockk.clearAllMocks |
||||||
|
import org.junit.Test |
||||||
|
import org.junit.runner.Description |
||||||
|
import org.junit.runner.Runner |
||||||
|
import org.junit.runner.notification.Failure |
||||||
|
import org.junit.runner.notification.RunNotifier |
||||||
|
import java.lang.reflect.Method |
||||||
|
|
||||||
|
class MockKJUnitRunner(private val testClass: Class<*>) : Runner() { |
||||||
|
|
||||||
|
private val methodDescriptions: MutableMap<Method, Description> = mutableMapOf() |
||||||
|
|
||||||
|
init { |
||||||
|
// Build method/descriptions map |
||||||
|
testClass.methods |
||||||
|
.map { method -> |
||||||
|
val annotation: Annotation? = method.getAnnotation(Test::class.java) |
||||||
|
method to annotation |
||||||
|
} |
||||||
|
.filter { (_, annotation) -> |
||||||
|
annotation != null |
||||||
|
} |
||||||
|
.map { (method, annotation) -> |
||||||
|
val desc = Description.createTestDescription(testClass, method.name, annotation) |
||||||
|
method to desc |
||||||
|
} |
||||||
|
.forEach { (method, desc) -> methodDescriptions[method] = desc } |
||||||
|
} |
||||||
|
|
||||||
|
override fun getDescription(): Description { |
||||||
|
val description = Description.createSuiteDescription( |
||||||
|
testClass.name, *testClass.annotations |
||||||
|
) |
||||||
|
methodDescriptions.values.forEach { description.addChild(it) } |
||||||
|
return description |
||||||
|
} |
||||||
|
|
||||||
|
override fun run(notifier: RunNotifier?) { |
||||||
|
val testObject = testClass.newInstance() |
||||||
|
MockKAnnotations.init(testObject, relaxUnitFun = true) |
||||||
|
|
||||||
|
methodDescriptions |
||||||
|
.onEach { (_, _) -> clearAllMocks() } |
||||||
|
.onEach { (_, desc) -> notifier!!.fireTestStarted(desc) } |
||||||
|
.forEach { (method, desc) -> |
||||||
|
try { |
||||||
|
method.invoke(testObject) |
||||||
|
} catch (e: Throwable) { |
||||||
|
notifier!!.fireTestFailure(Failure(desc, e.cause)) |
||||||
|
} finally { |
||||||
|
notifier!!.fireTestFinished(desc) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue