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

Introduce instrumented espresso CI tests #141

Merged
merged 3 commits into from
Feb 9, 2024
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
79 changes: 79 additions & 0 deletions .github/workflows/android-instrumented-tests-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Instrumented CI Tests

on:
push:
branches:
- '*'
pull_request:
branches:
- master

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
api-level: [17, 19, 34]
steps:
- name: checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Restore Android virtual device
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: pfa-pedometer-${{ runner.os }}-avd-api${{ matrix.api-level }}

- name: set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'zulu'

- name: Set up Android virtual device if not cached
uses: reactivecircus/android-emulator-runner@v2
if: steps.avd-cache.outputs.cache-hit != 'true'
with:
api-level: ${{ matrix.api-level }}
arch: ${{ matrix.api-level < 21 && 'x86' || 'x86_64' }}
target: ${{ matrix.api-level >= 30 && 'google_apis' || 'default' }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
sdcard-path-or-size: 64M
script: echo "Generated AVD snapshot for caching."

- name: Run instrumented tests on Android virtual device
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: ${{ matrix.api-level < 21 && 'x86' || 'x86_64' }}
target: ${{ matrix.api-level >= 30 && 'google_apis' || 'default' }}
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
sdcard-path-or-size: 64M
script: |
adb uninstall org.secuso.privacyfriendlyactivitytracker.test || true
touch emulator.log # create log file
chmod 777 emulator.log # allow writing to log file
adb logcat >> emulator.log & # pipe all logcat messages into log file as a background process
./gradlew connectedAndroidTest --no-build-cache --no-daemon
- name: Upload logs
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.api-level }}-${{ matrix.arch }}-instrumentation-test-results
path: |
emulator.log
./**/build/reports/androidTests/connected/**
44 changes: 44 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'


def pfaFile = rootProject.file('pfa.properties')

def getAvailableLocales() {
def tree = fileTree(dir: 'src/main/res', include: '**/strings.xml')
return tree.collect {
def lang = it.getParentFile().getName() - "values-" - "values"

// We want a name which would be understood by Locale::forLanguageTag, so we should do
// do a simple conversion for an edge case:
// "pt-rBR" -> "pt-BR"
// "zh-rCN" -> "zh-CN"
// and so on
lang = lang.replace("-r", "-")

if (lang.empty) {
lang = "en"
}

lang
}
}

android {
namespace 'org.secuso.privacyfriendlyactivitytracker'

Expand All @@ -28,6 +49,11 @@ android {
versionCode 16
versionName "3.1.0"
multiDexEnabled true

def escapedLocales = getAvailableLocales().collect { "\"" + it + "\"" }
buildConfigField "String[]", "AVAILABLE_LOCALES", String.format("{ %s }", escapedLocales.join(","))

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

applicationVariants.all { variant ->
Expand All @@ -54,8 +80,20 @@ android {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
}

gradle.taskGraph.whenReady { taskGraph ->
project(':backup-api') {
tasks.named('compileDebugAndroidTestKotlin').configure {
enabled = false
}
}
}


repositories {
maven { url "https://jitpack.io" }
}
Expand All @@ -70,6 +108,8 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1'
implementation 'androidx.multidex:multidex:2.0.1' //with androidx libraries
implementation 'androidx.test.ext:junit:1.1.5'
implementation 'androidx.test.espresso:espresso-contrib:3.5.1'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
Expand All @@ -81,5 +121,9 @@ dependencies {

implementation 'androidx.sqlite:sqlite-ktx:2.4.0'
implementation 'androidx.sqlite:sqlite-framework:2.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
androidTestImplementation("com.github.YarikSOffice:lingver:1.3.0")
}

Original file line number Diff line number Diff line change
@@ -1,20 +1,53 @@
/*
Privacy Friendly Pedometer is licensed under the GPLv3.
Copyright (C) 2017 Tobias Neidig

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.secuso.privacyfriendlyactivitytracker;

import static junit.framework.TestCase.assertEquals;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import android.Manifest;

import androidx.test.espresso.action.ViewActions;
import androidx.test.espresso.assertion.ViewAssertions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.rule.GrantPermissionRule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.secuso.privacyfriendlyactivitytracker.tutorial.TutorialActivity;

/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
@RunWith(AndroidJUnit4.class)
public class ApplicationTest {
@Rule
public ActivityScenarioRule<TutorialActivity> activityRule =
new ActivityScenarioRule<>(TutorialActivity.class);

@Rule
public GrantPermissionRule activityRecognitionPermission = (android.os.Build.VERSION.SDK_INT >= 29 ? GrantPermissionRule.grant(Manifest.permission.ACTIVITY_RECOGNITION) : null);
@Rule
public GrantPermissionRule foregroundServicePermission = (android.os.Build.VERSION.SDK_INT >= 34 ? GrantPermissionRule.grant(Manifest.permission.FOREGROUND_SERVICE_HEALTH) : null);
@Rule
public GrantPermissionRule postNotificatuionsPermission = (android.os.Build.VERSION.SDK_INT >= 32 ? GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS) : null);

@Test
public void instrumentationTest() throws Exception {
assertEquals("org.secuso.privacyfriendlyactivitytracker", InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName());
public void canStartApp() {
onView(withText(R.string.skip)).perform(ViewActions.click());
onView(withText(R.string.day)).perform(ViewActions.click());
onView(withText(R.string.day)).check(ViewAssertions.matches(ViewMatchers.isSelected()));
}
}
Loading
Loading