Skip to content

Commit

Permalink
additional improvements to waits in tests (odk-x#299)
Browse files Browse the repository at this point in the history
* additional improvements to waits in tests

* add onIdle and clean up imports

* add dismiss system dialogs

* add additional fixes

* attempt to use orchestrator

* add base class for tests requiring files and improve reporting

* additional fixes

* move away from activity rule and other fixes

* remove orchestration and add warmup test

* add new ignores

* add an ignore

* add additional waits
  • Loading branch information
wbrunette authored Aug 19, 2024
1 parent 1d8bba0 commit 3304877
Show file tree
Hide file tree
Showing 61 changed files with 572 additions and 353 deletions.
29 changes: 11 additions & 18 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ jobs:
command: sudo chmod +x gradlew

- android/create-avd:
avd-name: Pixel_7_Pro
avd-name: testEmulator
system-image: system-images;android-30;google_apis;x86
install: true

additional-args: -c 2048M
- run:
name: Start ADB server
command: adb start-server

- android/start-emulator:
avd-name: Pixel_7_Pro
avd-name: testEmulator
no-window: true
restore-gradle-cache-prefix: v1
memory: 4096
post-emulator-launch-assemble-command: ./gradlew installSnapshotDebug

Expand All @@ -46,16 +45,16 @@ jobs:
- android/run-tests:
test-command: ./gradlew createSnapshotDebugUnitTestCoverageReport
- android/run-tests:
test-command: ./gradlew grantPermissionForODKXApp createSnapshotDebugAndroidTestCoverageReport --info
- android/save-gradle-cache:
cache-prefix: v1

- store_artifacts:
name: Store Test Results
test-command: ./gradlew createSnapshotDebugAndroidTestCoverageReport
no-output-timeout: 40m
- store_test_results:
name: Store Unit Tests Results
path: services_app/build/test-results/testSnapshotDebugUnitTest
- store_test_results:
name: Store Android Tests Results
path: services_app/build/outputs/androidTest-results

- store_artifacts:
name: Store Test Reports
name: Store HTML Reports
path: services_app/build/reports

build:
Expand All @@ -69,16 +68,10 @@ jobs:
name: Chmod Permissions
command: sudo chmod +x gradlew

- android/restore-gradle-cache:
cache-prefix: v1

- run:
name: Download Dependencies
command: ./gradlew androidDependencies

- android/save-gradle-cache:
cache-prefix: v1

- run:
name: Build Services
command: ./gradlew assembleSnapshotDebug
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.2'
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.2'
}
}

Expand Down
11 changes: 4 additions & 7 deletions services_app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ android {
includeAndroidResources = true
}
}
// The following are set in the common.gradle file

// The following are set in the common.gradle file
namespace(sevicesNamespace)
compileSdk(compileVersion)

defaultConfig {
minSdkVersion(minVersion)
targetSdkVersion(targetVersion)
testInstrumentationRunner(instrumentationRunner)

}

buildFeatures {
Expand Down Expand Up @@ -157,6 +157,8 @@ dependencies {
androidTestImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.1'

androidTestUtil 'androidx.test:orchestrator:1.5.0'

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.19.0'
testImplementation 'androidx.annotation:annotation:1.7.1'
Expand All @@ -166,8 +168,3 @@ dependencies {

}

task grantPermissionForODKXApp {
dependsOn grantServicesReadExternalStoragePermission
dependsOn grantServicesWriteExternalStoragePermission
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.opendatakit;

import android.Manifest;
import android.app.Application;
import android.content.Context;
import android.content.Intent;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;

import org.junit.Rule;
import org.opendatakit.utilities.RuntimePermissionUtils;

public abstract class BaseFileTest {
@Rule
public GrantPermissionRule writeRuntimePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
@Rule
public GrantPermissionRule readtimePermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);


protected void verifyReady() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));

Application app = ApplicationProvider.getApplicationContext();
boolean permissions = RuntimePermissionUtils.checkPackageAllPermission(context, app.getPackageName(),
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
);
if (!permissions) {
throw new RuntimeException("FILE PERMISSIONS MISSING");
}
}
}
73 changes: 57 additions & 16 deletions services_app/src/androidTest/java/org/opendatakit/BaseUITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
import static com.google.android.gms.common.internal.Preconditions.checkNotNull;
import static org.hamcrest.Matchers.isA;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.widget.Checkable;

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.PerformException;
Expand All @@ -30,14 +30,12 @@
import androidx.test.espresso.util.HumanReadables;
import androidx.test.espresso.util.TreeIterables;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.opendatakit.properties.CommonToolProperties;
import org.opendatakit.properties.PropertiesSingleton;
import org.opendatakit.services.R;
Expand All @@ -47,8 +45,8 @@
import java.io.File;
import java.util.concurrent.TimeoutException;

public abstract class BaseUITest<T extends Activity> {
private static boolean isInitialized = false;
public abstract class BaseUITest<T extends Activity> extends BaseFileTest {

protected final static String APP_NAME = "testAppName";
protected final static String TEST_SERVER_URL = "https://testUrl.com";
protected final static String TEST_PASSWORD = "testPassword";
Expand All @@ -58,24 +56,27 @@ public abstract class BaseUITest<T extends Activity> {
protected final static String FONT_SIZE_M = "Medium";
protected final static String FONT_SIZE_S = "Small";
protected final static String FONT_SIZE_XS = "Extra Small";
protected static final String SERVER_URL = "https://tables-demo.odk-x.org";
protected ActivityScenario<T> activityScenario;
protected static final String DEFAULT_SERVER_URL = "https://tables-demo.odk-x.org";

@Rule
public GrantPermissionRule writeRuntimePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
private boolean isInitialized = false;

protected ActivityScenario<T> activityScenario;

@Rule
public GrantPermissionRule readtimePermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);

@Before
public void setUp() {
if (!isInitialized) {
System.out.println("Intents.init() called");
Intents.init();
isInitialized = true;
} else {
throw new RuntimeException("Attempting to do init intents when already true");
}

verifyReady();
activityScenario = ActivityScenario.launch(getLaunchIntent());
Lifecycle.State state = activityScenario.getState();
if(state != Lifecycle.State.RESUMED) {
throw new RuntimeException("State Not Resumed");
}
setUpPostLaunch();
}

Expand All @@ -87,9 +88,10 @@ public void tearDown() throws Exception {
}

if (isInitialized) {
System.out.println("Intents.release() called");
Intents.release();
isInitialized = false;
} else {
throw new RuntimeException("Attempting to do release intents when not initialized");
}
}

Expand Down Expand Up @@ -208,11 +210,49 @@ public void perform(final UiController uiController, final View view) {
do {
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
if (viewMatcher.matches(child)) {
return;
if(child.isAttachedToWindow())
return;
}
}

uiController.loopMainThreadForAtLeast(10);
} while (System.currentTimeMillis() < endTime);

throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new TimeoutException())
.build();
}
};
}

public static ViewAction waitForViewToBeShown(final Matcher<View> viewMatcher, final long millis) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}

@Override
public String getDescription() {
return "Wait for a specific view with id <" + viewMatcher + "> during " + millis + " millis.";
}

@Override
public void perform(final UiController uiController, final View view) {
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;

do {
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
if (viewMatcher.matches(child)) {
if(child.isShown())
return;
}
}

uiController.loopMainThreadForAtLeast(50);
uiController.loopMainThreadForAtLeast(10);
} while (System.currentTimeMillis() < endTime);

throw new PerformException.Builder()
Expand All @@ -223,6 +263,7 @@ public void perform(final UiController uiController, final View view) {
}
};
}

public static void enableAdminMode() {
onView(withId(androidx.preference.R.id.recycler_view))
.perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.user_restrictions)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
public class TestConsts {
public static final String APPNAME = "unittestTMP";

public static final long WAIT_TIME = 2000;
public static final long TIMEOUT_WAIT = 30000;
public static final long SHORT_WAIT = 100;

}
32 changes: 32 additions & 0 deletions services_app/src/androidTest/java/org/opendatakit/WarmUpTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.opendatakit;

import static org.junit.Assert.assertTrue;

import android.content.Intent;

import androidx.test.espresso.Espresso;

import org.junit.Test;
import org.opendatakit.consts.IntentConsts;
import org.opendatakit.services.MainActivity;

public class WarmUpTest extends BaseUITest<MainActivity> {

@Test
public void warmUpTest() {
Espresso.onIdle();
assertTrue(true);
}

@Override
protected void setUpPostLaunch() {
// do nothing
}

@Override
protected Intent getLaunchIntent() {
Intent intent = new Intent(getContext(), MainActivity.class);
intent.putExtra(IntentConsts.INTENT_KEY_APP_NAME, APP_NAME);
return intent;
}
}
Loading

0 comments on commit 3304877

Please sign in to comment.