-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce instrumented espresso CI tests
* all provided locales on all fragments and preferences sections * csv export * step count test
- Loading branch information
Showing
13 changed files
with
714 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 26 additions & 10 deletions
36
app/src/androidTest/java/org/secuso/privacyfriendlyactivitytracker/ApplicationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,36 @@ | ||
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())); | ||
} | ||
} |
169 changes: 169 additions & 0 deletions
169
app/src/androidTest/java/org/secuso/privacyfriendlyactivitytracker/LocalesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package org.secuso.privacyfriendlyactivitytracker; | ||
|
||
import static androidx.test.espresso.Espresso.onView; | ||
import static androidx.test.espresso.action.ViewActions.click; | ||
import static androidx.test.espresso.action.ViewActions.swipeLeft; | ||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot; | ||
import static androidx.test.espresso.matcher.ViewMatchers.withId; | ||
import static androidx.test.espresso.matcher.ViewMatchers.withText; | ||
|
||
import android.Manifest; | ||
import android.os.Build; | ||
|
||
import androidx.test.core.app.ApplicationProvider; | ||
import androidx.test.espresso.IdlingRegistry; | ||
import androidx.test.espresso.IdlingResource; | ||
import androidx.test.espresso.action.ViewActions; | ||
import androidx.test.espresso.assertion.ViewAssertions; | ||
import androidx.test.espresso.contrib.DrawerActions; | ||
import androidx.test.espresso.matcher.ViewMatchers; | ||
import androidx.test.filters.LargeTest; | ||
import androidx.test.filters.SdkSuppress; | ||
import androidx.test.rule.ActivityTestRule; | ||
import androidx.test.rule.GrantPermissionRule; | ||
import androidx.viewpager.widget.ViewPager; | ||
|
||
import com.yariksoffice.lingver.Lingver; | ||
|
||
import org.hamcrest.core.Is; | ||
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.Parameterized; | ||
import org.secuso.privacyfriendlyactivitytracker.activities.MainActivity; | ||
|
||
import java.util.Locale; | ||
import java.util.concurrent.CopyOnWriteArrayList; | ||
|
||
@LargeTest | ||
@RunWith(Parameterized.class) | ||
|
||
public class LocalesTest { | ||
@Parameterized.Parameter(value = 0) | ||
public static Locale locale = Locale.ENGLISH; | ||
@Rule | ||
public ActivityTestRule<MainActivity> activityRule | ||
= new ActivityTestRule<MainActivity>(MainActivity.class) { | ||
@Override | ||
protected void beforeActivityLaunched() { | ||
Lingver.getInstance().setLocale(ApplicationProvider.getApplicationContext(), locale); | ||
super.beforeActivityLaunched(); | ||
} | ||
}; | ||
@Rule | ||
public GrantPermissionRule activityRecognitionPermission = (android.os.Build.VERSION.SDK_INT >= 29 ? GrantPermissionRule.grant(Manifest.permission.ACTIVITY_RECOGNITION) : null); | ||
|
||
@BeforeClass | ||
public static void initAll() { | ||
Lingver.init(ApplicationProvider.getApplicationContext(), locale); | ||
} | ||
|
||
@Parameterized.Parameters(name = "locale={0}") | ||
public static CopyOnWriteArrayList<Object[]> initParameters() { | ||
CopyOnWriteArrayList<Object[]> params = new CopyOnWriteArrayList<>(); | ||
for (String availableLocale : BuildConfig.AVAILABLE_LOCALES) { | ||
params.add(new Object[]{parseLocale(availableLocale)}); | ||
} | ||
|
||
return params; | ||
} | ||
|
||
private static Locale parseLocale(String str) { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||
return Locale.forLanguageTag(str); | ||
} else { | ||
if (str.contains("-")) { | ||
String[] args = str.split("-"); | ||
if (args.length > 2) { | ||
return new Locale(args[0], args[1], args[3]); | ||
} else if (args.length > 1) { | ||
return new Locale(args[0], args[1]); | ||
} else if (args.length == 1) { | ||
return new Locale(args[0]); | ||
} | ||
} | ||
|
||
return new Locale(str); | ||
} | ||
} | ||
|
||
Locale getCurrentLocale() { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { | ||
return ApplicationProvider.getApplicationContext() | ||
.getResources().getConfiguration().getLocales() | ||
.get(0); | ||
} else { | ||
return ApplicationProvider.getApplicationContext() | ||
.getResources().getConfiguration().locale; | ||
} | ||
} | ||
|
||
@Before | ||
public void setUp() { | ||
ViewMatchers.assertThat("Locale is not supported", getCurrentLocale(), Is.is(locale)); | ||
} | ||
|
||
// works on local emulators > 31, but not in GH workflow | ||
@SdkSuppress(maxSdkVersion = 31) | ||
@Test | ||
public void application_ShouldNotCrash_WithLanguage() { | ||
ViewPagerIdlingResource idlingResource = new ViewPagerIdlingResource(activityRule.getActivity().findViewById(R.id.pager), "ViewPager"); | ||
IdlingRegistry.getInstance().register(idlingResource); | ||
|
||
onView(withText(R.string.day)).perform(ViewActions.click()); | ||
onView(withText(R.string.day)).check(ViewAssertions.matches(ViewMatchers.isSelected())); | ||
|
||
onView(withId(R.id.pager)).perform(swipeLeft()); | ||
onView(withText(R.string.week)).check(ViewAssertions.matches(ViewMatchers.isSelected())); | ||
onView(withId(R.id.pager)).perform(swipeLeft()); | ||
onView(withText(R.string.month)).check(ViewAssertions.matches(ViewMatchers.isSelected())); | ||
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); | ||
onView(withId(R.id.menu_settings)).perform(click()); | ||
onView(withText(R.string.pref_header_general)).perform(click()); | ||
onView(isRoot()).perform(ViewActions.pressBack()); | ||
onView(withText(R.string.pref_header_notifications)).perform(click()); | ||
onView(isRoot()).perform(ViewActions.pressBack()); | ||
onView(withText(R.string.pref_header_walking_modes)).perform(click()); | ||
} | ||
|
||
public static class ViewPagerIdlingResource implements IdlingResource { | ||
private final String resourceName; | ||
|
||
private boolean isIdle = true; | ||
|
||
private ResourceCallback resourceCallback; | ||
|
||
public ViewPagerIdlingResource(ViewPager viewPager, String name) { | ||
viewPager.addOnPageChangeListener(new ViewPagerListener()); | ||
resourceName = name; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return resourceName; | ||
} | ||
|
||
@Override | ||
public boolean isIdleNow() { | ||
return isIdle; | ||
} | ||
|
||
@Override | ||
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { | ||
this.resourceCallback = resourceCallback; | ||
} | ||
|
||
private class ViewPagerListener extends ViewPager.SimpleOnPageChangeListener { | ||
|
||
@Override | ||
public void onPageScrollStateChanged(int state) { | ||
isIdle = (state == ViewPager.SCROLL_STATE_IDLE); | ||
if (isIdle && resourceCallback != null) { | ||
resourceCallback.onTransitionToIdle(); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.