From 6d3c251bbb0737c3fcc946baec8b379c575d00b9 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 7 Aug 2023 17:45:28 +0100 Subject: [PATCH 01/35] Make sure process restore is simulated correctly --- .../feature/formentry/ProcessRestoreTest.kt | 9 +++------ .../android/feature/formentry/SavePointTest.kt | 10 +++------- .../support/rules/FormEntryActivityTestRule.kt | 17 ++++++++++++++--- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt index a5a9df2b2f1..4824a4afe7d 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt @@ -5,8 +5,6 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import org.junit.runner.RunWith -import org.odk.collect.android.R -import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.pages.FormEntryPage import org.odk.collect.android.support.pages.FormHierarchyPage import org.odk.collect.android.support.pages.Page @@ -54,10 +52,9 @@ class ProcessRestoreTest { */ private fun > simulateProcessRestore(destination: Page): Page { rule.navigateAwayFromActivity() - rule.destroyActivity() - - CollectHelpers.simulateProcessRestart() - rule.restoreActivity() + .destroyActivity() + .simulateProcessRestart() + .restoreActivity() return destination.assertOnPage() } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/SavePointTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/SavePointTest.kt index acc73012fab..40179c9a395 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/SavePointTest.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/SavePointTest.kt @@ -7,7 +7,6 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import org.junit.runner.RunWith -import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.StorageUtils import org.odk.collect.android.support.pages.AppClosedPage import org.odk.collect.android.support.pages.FormEntryPage @@ -223,18 +222,15 @@ class SavePointTest { * being battery dying). */ private fun simulateBatteryDeath(): FormEntryActivityTestRule { - CollectHelpers.simulateProcessRestart() - return rule + return rule.simulateProcessRestart() } /** * Simulate a "process death" case where an app in the background is killed */ private fun simulateProcessDeath(): FormEntryActivityTestRule { - rule.navigateAwayFromActivity() + return rule.navigateAwayFromActivity() .destroyActivity() - - CollectHelpers.simulateProcessRestart() - return rule + .simulateProcessRestart() } } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt index b40f544fdb5..0377ba1a169 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt @@ -12,10 +12,10 @@ import org.odk.collect.android.activities.FormFillingActivity import org.odk.collect.android.external.FormsContract import org.odk.collect.android.formmanagement.FormFillingIntentFactory import org.odk.collect.android.injection.DaggerUtils -import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.StorageUtils +import org.odk.collect.android.support.TestDependencies import org.odk.collect.android.support.pages.FormEntryPage import org.odk.collect.android.support.pages.FormHierarchyPage import org.odk.collect.android.support.pages.Page @@ -36,7 +36,7 @@ class FormEntryActivityTestRule : ExternalResource() { override fun before() { super.before() - CollectHelpers.overrideAppDependencyModule(object : AppDependencyModule() { + CollectHelpers.overrideAppDependencyModule(object : TestDependencies() { override fun providesSavedInstanceStateProvider(): SavedInstanceStateProvider { return savedInstanceStateProvider } @@ -90,9 +90,20 @@ class FormEntryActivityTestRule : ExternalResource() { return this } - fun restoreActivity() { + fun restoreActivity(): FormEntryActivityTestRule { savedInstanceStateProvider.setState(outState) scenario = ActivityScenario.launch(intent) + return this + } + + fun simulateProcessRestart(): FormEntryActivityTestRule { + CollectHelpers.simulateProcessRestart(object : TestDependencies() { + override fun providesSavedInstanceStateProvider(): SavedInstanceStateProvider { + return savedInstanceStateProvider + } + }) + + return this } private fun createNewFormIntent(formFilename: String): Intent { From 9277b64b4af33862418bb454e7db958a72ba1129 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 7 Aug 2023 18:08:43 +0100 Subject: [PATCH 02/35] Spike out being able to return external data after a process restore --- .../feature/formentry/ProcessRestoreTest.kt | 2 +- .../activities/FormFillingActivity.java | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt index 4824a4afe7d..24c031389ee 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt @@ -42,7 +42,7 @@ class ProcessRestoreTest { .let { simulateProcessRestore(FormHierarchyPage("All widgets")) } .pressBack(FormEntryPage("All widgets")) - .assertQuestion("Welcome to ODK Collect! This form showcases the different available question types (widgets).") + .assertQuestion("Select one from map widget") } /** diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java index e49c3af3d09..db378ad9f35 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java @@ -445,6 +445,14 @@ public void onCreate(Bundle savedInstanceState) { savedInstanceState = savedInstanceStateProvider.getState(savedInstanceState); if (ProcessRestoreDetector.isProcessRestoring(this, savedInstanceState)) { + if (savedInstanceState.containsKey(KEY_XPATH)) { + startingXPath = savedInstanceState.getString(KEY_XPATH); + } + + if (savedInstanceState.containsKey(KEY_XPATH_WAITING_FOR_DATA)) { + waitingXPath = savedInstanceState.getString(KEY_XPATH_WAITING_FOR_DATA); + } + savedInstanceState = null; } @@ -721,7 +729,7 @@ private void loadFromIntent(Intent intent) { instancePath = loadSavePoint(); } - formLoaderTask = new FormLoaderTask(instancePath, null, null, formEntryControllerFactory); + formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory); formLoaderTask.setFormLoaderListener(this); showIfNotShowing(FormLoadingDialogFragment.class, getSupportFragmentManager()); formLoaderTask.execute(formPath); @@ -2096,17 +2104,6 @@ public void loadingComplete(FormLoaderTask task, FormDef formDef, String warning } } - boolean pendingActivityResult = task.hasPendingActivityResult(); - - if (pendingActivityResult) { - Timber.w("Calling onActivityResult from loadingComplete"); - - formControllerAvailable(formController); - formEntryViewModel.refresh(); - onActivityResult(task.getRequestCode(), task.getResultCode(), task.getIntent()); - return; - } - // it can be a normal flow for a pending activity result to restore from a savepoint // (the call flow handled by the above if statement). For all other use cases, the // user should be notified, as it means they wandered off doing other things then @@ -2182,12 +2179,19 @@ && new PlayServicesChecker().isGooglePlayServicesAvailable(this)) { } } - formController.getAuditEventLogger().logEvent(AuditEvent.AuditEventType.FORM_RESUME, true, System.currentTimeMillis()); - formController.getAuditEventLogger().logEvent(AuditEvent.AuditEventType.HIERARCHY, true, System.currentTimeMillis()); - formControllerAvailable(formController); - Intent intent = new Intent(this, FormHierarchyActivity.class); - intent.putExtra(FormHierarchyActivity.EXTRA_SESSION_ID, sessionId); - startActivityForResult(intent, RequestCodes.HIERARCHY_ACTIVITY); + boolean pendingActivityResult = task.hasPendingActivityResult(); + if (pendingActivityResult) { + formControllerAvailable(formController); + formEntryViewModel.refresh(); + onActivityResult(task.getRequestCode(), task.getResultCode(), task.getIntent()); + } else { + formController.getAuditEventLogger().logEvent(AuditEvent.AuditEventType.FORM_RESUME, true, System.currentTimeMillis()); + formController.getAuditEventLogger().logEvent(AuditEvent.AuditEventType.HIERARCHY, true, System.currentTimeMillis()); + formControllerAvailable(formController); + Intent intent = new Intent(this, FormHierarchyActivity.class); + intent.putExtra(FormHierarchyActivity.EXTRA_SESSION_ID, sessionId); + startActivityForResult(intent, RequestCodes.HIERARCHY_ACTIVITY); + } } }); } else { From 1f2218d540778ad518add0d3be17fa55378429ef Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Tue, 8 Aug 2023 11:16:12 +0100 Subject: [PATCH 03/35] Stop restores loading first question instead of hierarchy if it was previously up --- .../android/activities/FormFillingActivity.java | 14 +++++++------- .../android/formentry/FormEntryViewModel.java | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java index db378ad9f35..e33b4ae94b1 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java @@ -860,6 +860,13 @@ protected void onSaveInstanceState(Bundle outState) { @Override protected void onActivityResult(int requestCode, int resultCode, final Intent intent) { super.onActivityResult(requestCode, resultCode, intent); + // If we're coming back from the hierarchy view, the user has either tapped the back + // button or another question to jump to so we need to rebuild the view. + if (requestCode == RequestCodes.HIERARCHY_ACTIVITY || requestCode == RequestCodes.CHANGE_SETTINGS) { + formEntryViewModel.refresh(); + return; + } + FormController formController = getFormController(); if (formController == null) { // we must be in the midst of a reload of the FormController. @@ -873,13 +880,6 @@ protected void onActivityResult(int requestCode, int resultCode, final Intent in return; } - // If we're coming back from the hierarchy view, the user has either tapped the back - // button or another question to jump to so we need to rebuild the view. - if (requestCode == RequestCodes.HIERARCHY_ACTIVITY || requestCode == RequestCodes.CHANGE_SETTINGS) { - formEntryViewModel.refresh(); - return; - } - if (resultCode == RESULT_CANCELED) { waitingForDataRegistry.cancelWaitingForData(); return; diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/FormEntryViewModel.java b/collect_app/src/main/java/org/odk/collect/android/formentry/FormEntryViewModel.java index 56e1fe76284..8d9dc11407f 100644 --- a/collect_app/src/main/java/org/odk/collect/android/formentry/FormEntryViewModel.java +++ b/collect_app/src/main/java/org/odk/collect/android/formentry/FormEntryViewModel.java @@ -285,7 +285,9 @@ protected void onCleared() { } public void refresh() { - currentIndex.setValue(formController.getFormIndex()); + if (formController != null) { + currentIndex.setValue(formController.getFormIndex()); + } } public void exit() { From 65a776a888c804c783ad40b40c2c7def583220ee Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 9 Aug 2023 15:40:44 +0100 Subject: [PATCH 04/35] Add Robolectric implementation of process restore simulation --- .../activities/FormFillingActivityTest.kt | 76 +++++++++++++++++++ .../org/odk/collect/formstest/FormUtils.kt | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt new file mode 100644 index 00000000000..34640431652 --- /dev/null +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -0,0 +1,76 @@ +package org.odk.collect.android.activities + +import android.app.Application +import android.content.ComponentName +import android.os.Bundle +import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.odk.collect.android.application.Collect +import org.odk.collect.android.external.FormsContract +import org.odk.collect.android.formmanagement.FormFillingIntentFactory +import org.odk.collect.android.injection.DaggerUtils +import org.odk.collect.android.injection.config.AppDependencyModule +import org.odk.collect.android.storage.StorageSubdirectory +import org.odk.collect.android.support.CollectHelpers +import org.odk.collect.formstest.FormUtils.buildForm +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf + +@RunWith(AndroidJUnit4::class) +class FormFillingActivityTest { + + private val application = ApplicationProvider.getApplicationContext() + private val dependencies = object : AppDependencyModule() {} + + @Before + fun setup() { + CollectHelpers.overrideAppDependencyModule(dependencies) + } + + @Test + fun processRestore() { + val projectId = CollectHelpers.setupDemoProject() + + val formsRepository = DaggerUtils.getComponent(application).formsRepositoryProvider().get() + val storagePathProvider = DaggerUtils.getComponent(application).storagePathProvider() + val formsDir = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + val newForm = + buildForm(formId = "id", version = "version", formFilesPath = formsDir).build() + val form = formsRepository.save(newForm) + + val intent = FormFillingIntentFactory.newInstanceIntent( + application, + FormsContract.getUri(projectId, form!!.dbId), + FormFillingActivity::class + ) + + // Start activity + val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + onView(withText("Test Form")).check(matches(isDisplayed())) + + // Destroy activity with saved instance state + val outState = Bundle() + initial.saveInstanceState(outState).pause().stop().destroy() + + // Reset process + ApplicationProvider.getApplicationContext().getState().clear() + val newComponent = CollectHelpers.overrideAppDependencyModule(dependencies) + newComponent.applicationInitializer().initialize() + + // Recreate + val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + assertThat( + shadowOf(initial.get()).nextStartedActivity.component, + equalTo(ComponentName(application, FormHierarchyActivity::class.java)) + ) + } +} diff --git a/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt b/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt index 0c388d33f16..5c558160fe8 100644 --- a/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt +++ b/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt @@ -9,7 +9,7 @@ import java.nio.charset.Charset object FormUtils { @JvmStatic @JvmOverloads - fun createXFormBody(formId: String, version: String?, title: String = "Form"): String { + fun createXFormBody(formId: String, version: String?, title: String = "Test Form"): String { return """ From db2b0935f5d36319a2ab8e3316396062c902e2d5 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 9 Aug 2023 16:31:21 +0100 Subject: [PATCH 05/35] Replace instrumentation with local test --- .../android/feature/formentry/ProcessRestoreTest.kt | 12 ------------ .../android/activities/FormFillingActivityTest.kt | 13 +++++++++++-- .../java/org/odk/collect/formstest/FormUtils.kt | 5 +++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt index 24c031389ee..393e8b40f2f 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt @@ -19,18 +19,6 @@ class ProcessRestoreTest { @get:Rule val ruleChain: RuleChain = TestRuleChain.chain().around(rule) - @Test - fun whenProcessIsKilledAndRestoredDuringFormEntry_returnsToHierarchy() { - rule.setUpProjectAndCopyForm("one-question.xml") - .fillNewForm("one-question.xml", "One Question") - .answerQuestion("what is your age", "123") - .let { simulateProcessRestore(FormHierarchyPage("One Question")) } - - .assertText("123") - .pressBack(FormEntryPage("One Question")) - .assertQuestion("what is your age") - } - @Test fun whenProcessIsKilledAndRestoredDuringFormEntry_andThereADialogFragmentOpen_returnsToHierarchy() { rule.setUpProjectAndCopyForm("all-widgets.xml") diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 34640431652..b9d9e8fa9eb 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -1,5 +1,6 @@ package org.odk.collect.android.activities +import android.app.Activity import android.app.Application import android.content.ComponentName import android.os.Bundle @@ -21,6 +22,7 @@ import org.odk.collect.android.injection.DaggerUtils import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers +import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.formstest.FormUtils.buildForm import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf @@ -37,7 +39,7 @@ class FormFillingActivityTest { } @Test - fun processRestore() { + fun whenProcessIsKilledAndRestoredDuringFormEntry_returnsToHierarchy() { val projectId = CollectHelpers.setupDemoProject() val formsRepository = DaggerUtils.getComponent(application).formsRepositoryProvider().get() @@ -56,6 +58,7 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() onView(withText("Test Form")).check(matches(isDisplayed())) + onView(withText("question label")).check(matches(isDisplayed())) // Destroy activity with saved instance state val outState = Bundle() @@ -66,11 +69,17 @@ class FormFillingActivityTest { val newComponent = CollectHelpers.overrideAppDependencyModule(dependencies) newComponent.applicationInitializer().initialize() - // Recreate + // Recreate and assert we start FormHierarchyActivity val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) + + // Return to FormFillingActivity from FormHierarchyActivity + recreated.recreate().get() + .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + onView(withText("Test Form")).check(matches(isDisplayed())) + onView(withText("question label")).check(matches(isDisplayed())) } } diff --git a/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt b/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt index 5c558160fe8..9602d12bf0a 100644 --- a/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt +++ b/formstest/src/main/java/org/odk/collect/formstest/FormUtils.kt @@ -17,11 +17,16 @@ object FormUtils { + + + + + """ } From a40450e344f43231db8146f056cd201c24a49cea Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 9 Aug 2023 17:05:50 +0100 Subject: [PATCH 06/35] Duplicate class and remove commonTest Sharing source sets this way is no longer supported: https://issuetracker.google.com/issues/232007221#comment17 --- collect_app/build.gradle | 9 -------- .../collect/android/TestSettingsProvider.kt | 21 +++++++++++++++++++ .../collect/android/TestSettingsProvider.kt | 12 ----------- 3 files changed, 21 insertions(+), 21 deletions(-) create mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt rename collect_app/src/{commonTest => test}/java/org/odk/collect/android/TestSettingsProvider.kt (65%) diff --git a/collect_app/build.gradle b/collect_app/build.gradle index 98e781c2c00..e18324a7949 100644 --- a/collect_app/build.gradle +++ b/collect_app/build.gradle @@ -200,15 +200,6 @@ android { } } - - sourceSets { - androidTest { - java.srcDirs += "src/commonTest/java" - } - test { - java.srcDirs += "src/commonTest/java" - } - } lint { abortOnError true checkDependencies true diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt b/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt new file mode 100644 index 00000000000..69317ff774a --- /dev/null +++ b/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt @@ -0,0 +1,21 @@ +package org.odk.collect.android + +import androidx.test.core.app.ApplicationProvider +import org.odk.collect.android.application.Collect +import org.odk.collect.android.injection.DaggerUtils +import org.odk.collect.settings.SettingsProvider +import org.odk.collect.shared.settings.Settings + +// Use just for testing +object TestSettingsProvider { + @JvmStatic + fun getSettingsProvider(): SettingsProvider { + return DaggerUtils.getComponent(ApplicationProvider.getApplicationContext()).settingsProvider() + } + + @JvmStatic + @JvmOverloads + fun getUnprotectedSettings(uuid: String? = null): Settings { + return getSettingsProvider().getUnprotectedSettings(uuid) + } +} diff --git a/collect_app/src/commonTest/java/org/odk/collect/android/TestSettingsProvider.kt b/collect_app/src/test/java/org/odk/collect/android/TestSettingsProvider.kt similarity index 65% rename from collect_app/src/commonTest/java/org/odk/collect/android/TestSettingsProvider.kt rename to collect_app/src/test/java/org/odk/collect/android/TestSettingsProvider.kt index f791a3df563..111f7e70aaa 100644 --- a/collect_app/src/commonTest/java/org/odk/collect/android/TestSettingsProvider.kt +++ b/collect_app/src/test/java/org/odk/collect/android/TestSettingsProvider.kt @@ -1,10 +1,8 @@ package org.odk.collect.android -import android.content.Context import androidx.test.core.app.ApplicationProvider import org.odk.collect.android.application.Collect import org.odk.collect.android.injection.DaggerUtils -import org.odk.collect.android.preferences.source.SharedPreferencesSettings import org.odk.collect.settings.SettingsProvider import org.odk.collect.shared.settings.Settings @@ -25,14 +23,4 @@ object TestSettingsProvider { fun getProtectedSettings(): Settings { return getSettingsProvider().getProtectedSettings() } - - @JvmStatic - fun getMetaSettings(): Settings { - return getSettingsProvider().getMetaSettings() - } - - @JvmStatic - fun getTestSettings(name: String?): Settings { - return SharedPreferencesSettings(ApplicationProvider.getApplicationContext().getSharedPreferences(name, Context.MODE_PRIVATE)) - } } From c38bdba06e3b70244b9584212004da57ab0fe3dc Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 9 Aug 2023 18:25:19 +0100 Subject: [PATCH 07/35] Move test forms to dedicated module --- collect_app/build.gradle | 2 ++ .../collect/android/support/FileUtils.java | 25 ++++++++++++++++--- .../collect/android/support/StorageUtils.kt | 2 +- .../android/support/StubOpenRosaServer.java | 5 ++-- settings.gradle | 1 + test-forms/.gitignore | 1 + test-forms/build.gradle.kts | 9 +++++++ .../main/resources}/forms/1560_DateData.xml | 0 .../resources}/forms/1560_IntegerData.xml | 0 .../forms/1560_IntegerData_instanceID.xml | 0 .../src/main/resources}/forms/3403.xml | 0 .../forms/Automated_guidance_hint_form.xml | 0 .../main/resources}/forms/Birds-encrypted.xml | 0 .../main/resources}/forms/CSVerrorForm.xml | 0 .../src/main/resources}/forms/CalcTest.xml | 0 .../resources}/forms/Empty First Repeat.xml | 0 .../resources}/forms/OnePageFormShort.xml | 0 .../resources}/forms/OnePageFormValid2.xml | 0 .../src/main/resources}/forms/RepeatCount.xml | 0 .../resources}/forms/RepeatGroupAndGroup.xml | 0 .../resources}/forms/RepeatTitles_1648.xml | 0 .../src/main/resources}/forms/TestRepeat.xml | 0 .../src/main/resources}/forms/all-widgets.xml | 0 .../main/resources}/forms/audio-question.xml | 0 .../src/main/resources}/forms/basic.xml | 0 .../resources}/forms/default_image_answer.xml | 0 .../forms/different-search-appearances.xml | 0 .../resources}/forms/emptyGroupFieldList.xml | 0 .../resources}/forms/emptyGroupFieldList2.xml | 0 .../forms/encrypted-no-instanceID.xml | 0 .../src/main/resources}/forms/encrypted.xml | 0 .../resources}/forms/event-odk-new-repeat.xml | 0 .../forms/external-audio-question.xml | 0 .../resources}/forms/external-csv-search.xml | 0 .../resources}/forms/external_csv_form.xml | 0 .../forms/external_data_questions.xml | 0 .../resources}/forms/external_select_10.xml | 0 .../resources}/forms/field-list-repeat.xml | 0 .../resources}/forms/fieldListInFieldList.xml | 0 ...dListWithQuestionAndRegularGroupInside.xml | 0 ...istWithQuestionsAndRegularGroupsInside.xml | 0 .../resources}/forms/fieldlist-updates.xml | 0 .../forms/fieldlist-updates_nocsv.xml | 0 .../resources}/forms/fixed-count-repeat.xml | 0 .../src/main/resources}/forms/form1.xml | 0 .../src/main/resources}/forms/form2.xml | 0 .../src/main/resources}/forms/form3.xml | 0 .../src/main/resources}/forms/form4.xml | 0 .../src/main/resources}/forms/form5.xml | 0 .../src/main/resources}/forms/form6.xml | 0 .../src/main/resources}/forms/form7.xml | 0 .../src/main/resources}/forms/form8.xml | 0 .../src/main/resources}/forms/form9.xml | 0 .../main/resources}/forms/formHierarchy1.xml | 0 .../main/resources}/forms/formHierarchy2.xml | 0 .../main/resources}/forms/formHierarchy3.xml | 0 .../forms/formWithExternalFiles.xml | 0 .../resources}/forms/form_design_error.xml | 0 .../main/resources}/forms/form_styling.xml | 0 .../resources}/forms/formulaire_adherent.xml | 0 .../src/main/resources}/forms/g6Error.xml | 0 .../src/main/resources}/forms/g6Error2.xml | 0 .../resources}/forms/guidance_hint_form.xml | 0 .../src/main/resources}/forms/hints_textq.xml | 0 .../forms/identify-user-audit-false.xml | 0 .../resources}/forms/identify-user-audit.xml | 0 .../main/resources}/forms/image_widget.xml | 0 .../main/resources}/forms/intent-group.xml | 0 .../forms/internal-audio-question.xml | 0 .../resources}/forms/internal_select_10.xml | 0 .../main/resources}/forms/invalid-events.xml | 0 .../main/resources}/forms/invalid-form.xml | 0 .../src/main/resources}/forms/likert_test.xml | 0 .../main/resources}/forms/location-audit.xml | 0 .../src/main/resources}/forms/manyQ.xml | 0 .../src/main/resources}/forms/metadata.xml | 0 .../src/main/resources}/forms/metadata2.xml | 0 .../main/resources}/forms/multiple-events.xml | 0 .../forms/nested-repeats-complex.xml | 0 .../main/resources}/forms/nigeria-wards.xml | 0 .../forms/no-track-changes-reason.xml | 0 .../src/main/resources}/forms/numberInCSV.xml | 0 .../forms/one-question-audit-constraint.xml | 0 .../resources}/forms/one-question-audit.xml | 0 .../forms/one-question-autosend.xml | 0 ...one-question-background-audio-multiple.xml | 0 .../forms/one-question-background-audio.xml | 0 .../resources}/forms/one-question-entity.xml | 0 .../resources}/forms/one-question-google.xml | 0 .../resources}/forms/one-question-repeat.xml | 0 .../forms/one-question-translation.xml | 0 .../resources}/forms/one-question-updated.xml | 0 .../main/resources}/forms/one-question.xml | 0 .../resources}/forms/predicate-warning.xml | 0 .../src/main/resources}/forms/random.xml | 0 .../resources}/forms/randomTest_broken.xml | 0 .../main/resources}/forms/ranking_widget.xml | 0 .../regularGroupWithFieldListGroupInside.xml | 0 ...GroupWithQuestionAndRegularGroupInside.xml | 0 ...roupWithQuestionsAndRegularGroupInside.xml | 0 .../resources}/forms/repeat_group_form.xml | 0 .../resources}/forms/repeat_group_new.xml | 0 .../main/resources}/forms/repeat_groups.xml | 0 .../main/resources}/forms/requiredJR275.xml | 0 .../forms/requiredQuestionInFieldList.xml | 0 .../resources}/forms/search_and_select.xml | 0 .../resources}/forms/selectOneExternal.xml | 0 .../resources}/forms/select_one_external.xml | 0 .../resources}/forms/setgeopoint-action.xml | 0 .../setlocation-action-instance-load.xml | 0 .../forms/setlocation-and-audit-location.xml | 0 .../forms/simple-search-external-csv.xml | 0 .../main/resources}/forms/simpleFieldList.xml | 0 .../main/resources}/forms/single-geopoint.xml | 0 .../main/resources}/forms/start-geopoint.xml | 0 .../forms/string_widgets_in_field_list.xml | 0 .../src/main/resources}/forms/t21257.xml | 0 .../forms/test_multiselect_cleared.xml | 0 .../forms/threeNestedFieldListGroups.xml | 0 .../forms/track-changes-reason-on-edit.xml | 0 .../resources}/forms/two-question-audit.xml | 0 .../forms/two-question-required.xml | 0 .../two-question-save-incomplete-required.xml | 0 .../forms/two-question-save-incomplete.xml | 0 .../resources}/forms/two-question-updated.xml | 0 .../main/resources}/forms/two-question.xml | 0 .../forms/twoNestedRegularGroups.xml | 0 .../src/main/resources}/forms/validate.xml | 0 128 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 test-forms/.gitignore create mode 100644 test-forms/build.gradle.kts rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/1560_DateData.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/1560_IntegerData.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/1560_IntegerData_instanceID.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/3403.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/Automated_guidance_hint_form.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/Birds-encrypted.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/CSVerrorForm.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/CalcTest.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/Empty First Repeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/OnePageFormShort.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/OnePageFormValid2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/RepeatCount.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/RepeatGroupAndGroup.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/RepeatTitles_1648.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/TestRepeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/all-widgets.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/audio-question.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/basic.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/default_image_answer.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/different-search-appearances.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/emptyGroupFieldList.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/emptyGroupFieldList2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/encrypted-no-instanceID.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/encrypted.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/event-odk-new-repeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/external-audio-question.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/external-csv-search.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/external_csv_form.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/external_data_questions.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/external_select_10.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/field-list-repeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fieldListInFieldList.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fieldListWithQuestionAndRegularGroupInside.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fieldListWithQuestionsAndRegularGroupsInside.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fieldlist-updates.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fieldlist-updates_nocsv.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/fixed-count-repeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form1.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form3.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form4.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form5.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form6.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form7.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form8.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form9.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/formHierarchy1.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/formHierarchy2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/formHierarchy3.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/formWithExternalFiles.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form_design_error.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/form_styling.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/formulaire_adherent.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/g6Error.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/g6Error2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/guidance_hint_form.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/hints_textq.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/identify-user-audit-false.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/identify-user-audit.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/image_widget.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/intent-group.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/internal-audio-question.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/internal_select_10.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/invalid-events.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/invalid-form.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/likert_test.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/location-audit.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/manyQ.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/metadata.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/metadata2.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/multiple-events.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/nested-repeats-complex.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/nigeria-wards.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/no-track-changes-reason.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/numberInCSV.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-audit-constraint.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-audit.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-autosend.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-background-audio-multiple.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-background-audio.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-entity.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-google.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-repeat.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-translation.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question-updated.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/one-question.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/predicate-warning.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/random.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/randomTest_broken.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/ranking_widget.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/regularGroupWithFieldListGroupInside.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/regularGroupWithQuestionAndRegularGroupInside.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/regularGroupWithQuestionsAndRegularGroupInside.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/repeat_group_form.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/repeat_group_new.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/repeat_groups.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/requiredJR275.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/requiredQuestionInFieldList.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/search_and_select.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/selectOneExternal.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/select_one_external.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/setgeopoint-action.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/setlocation-action-instance-load.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/setlocation-and-audit-location.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/simple-search-external-csv.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/simpleFieldList.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/single-geopoint.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/start-geopoint.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/string_widgets_in_field_list.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/t21257.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/test_multiselect_cleared.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/threeNestedFieldListGroups.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/track-changes-reason-on-edit.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question-audit.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question-required.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question-save-incomplete-required.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question-save-incomplete.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question-updated.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/two-question.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/twoNestedRegularGroups.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/forms/validate.xml (100%) diff --git a/collect_app/build.gradle b/collect_app/build.gradle index e18324a7949..575bd9a4a0c 100644 --- a/collect_app/build.gradle +++ b/collect_app/build.gradle @@ -394,6 +394,8 @@ dependencies { androidTestImplementation Dependencies.androidx_work_testing androidTestImplementation Dependencies.uiautomator + androidTestImplementation project(':test-forms') + debugImplementation project(':fragmentstest') debugImplementation(project(':testshared')) { exclude group: 'org.robolectric' // Some tests in `collect_app` don't work with newer Robolectric diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java index 358fe9333b8..e1ce3bad76f 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java @@ -1,7 +1,7 @@ package org.odk.collect.android.support; -import android.content.res.AssetManager; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.platform.app.InstrumentationRegistry; import org.apache.commons.io.IOUtils; @@ -17,8 +17,25 @@ private FileUtils() { } public static void copyFileFromAssets(String fileSourcePath, String fileDestPath) throws IOException { - AssetManager assetManager = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); - try (InputStream input = assetManager.open(fileSourcePath); + copyStreamToPath(getAssetAsStream(fileSourcePath), fileDestPath); + } + + public static void copyFileFromResources(String fileSourcePath, String fileDestPath) throws IOException { + copyStreamToPath(getResourceAsStream(fileSourcePath), fileDestPath); + } + + @NonNull + public static InputStream getAssetAsStream(String fileSourcePath) throws IOException { + return InstrumentationRegistry.getInstrumentation().getContext().getAssets().open(fileSourcePath); + } + + @Nullable + public static InputStream getResourceAsStream(String fileSourcePath) { + return FileUtils.class.getResourceAsStream("/" + fileSourcePath); + } + + private static void copyStreamToPath(InputStream inputStream, String fileDestPath) throws IOException { + try (InputStream input = inputStream; OutputStream output = new FileOutputStream(fileDestPath)) { IOUtils.copy(input, output); } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt index e310f02dda1..9542778da80 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt @@ -89,7 +89,7 @@ object StorageUtils { @Throws(IOException::class) private fun copyForm(formFilename: String, copyTo: String, projectName: String): String { val pathname = getFormsDirPath(projectName) + copyTo - FileUtils.copyFileFromAssets("forms/$formFilename", pathname) + FileUtils.copyFileFromResources("forms/$formFilename", pathname) return pathname } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java index 7c5f5d0006b..2e76cac8876 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java @@ -1,5 +1,6 @@ package org.odk.collect.android.support; +import static org.odk.collect.android.support.FileUtils.getResourceAsStream; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -270,9 +271,7 @@ private InputStream getManifestResponse(@NonNull URI uri) throws IOException { @NotNull private InputStream getFormXML(String formID) throws IOException { String xmlPath = forms.get(Integer.parseInt(formID)).getFormXML(); - - AssetManager assetManager = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); - return assetManager.open("forms/" + xmlPath); + return getResourceAsStream("forms/" + xmlPath); } @NotNull diff --git a/settings.gradle b/settings.gradle index 97c248584f9..6e2f81723a1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -38,3 +38,4 @@ apply from: 'secrets.gradle' if (getSecrets().getProperty('MAPBOX_DOWNLOADS_TOKEN', '') != '') { include ':mapbox' } +include ':test-forms' diff --git a/test-forms/.gitignore b/test-forms/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/test-forms/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-forms/build.gradle.kts b/test-forms/build.gradle.kts new file mode 100644 index 00000000000..aaf27444adb --- /dev/null +++ b/test-forms/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("java-library") + id("org.jetbrains.kotlin.jvm") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 +} diff --git a/collect_app/src/androidTest/assets/forms/1560_DateData.xml b/test-forms/src/main/resources/forms/1560_DateData.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/1560_DateData.xml rename to test-forms/src/main/resources/forms/1560_DateData.xml diff --git a/collect_app/src/androidTest/assets/forms/1560_IntegerData.xml b/test-forms/src/main/resources/forms/1560_IntegerData.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/1560_IntegerData.xml rename to test-forms/src/main/resources/forms/1560_IntegerData.xml diff --git a/collect_app/src/androidTest/assets/forms/1560_IntegerData_instanceID.xml b/test-forms/src/main/resources/forms/1560_IntegerData_instanceID.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/1560_IntegerData_instanceID.xml rename to test-forms/src/main/resources/forms/1560_IntegerData_instanceID.xml diff --git a/collect_app/src/androidTest/assets/forms/3403.xml b/test-forms/src/main/resources/forms/3403.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/3403.xml rename to test-forms/src/main/resources/forms/3403.xml diff --git a/collect_app/src/androidTest/assets/forms/Automated_guidance_hint_form.xml b/test-forms/src/main/resources/forms/Automated_guidance_hint_form.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/Automated_guidance_hint_form.xml rename to test-forms/src/main/resources/forms/Automated_guidance_hint_form.xml diff --git a/collect_app/src/androidTest/assets/forms/Birds-encrypted.xml b/test-forms/src/main/resources/forms/Birds-encrypted.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/Birds-encrypted.xml rename to test-forms/src/main/resources/forms/Birds-encrypted.xml diff --git a/collect_app/src/androidTest/assets/forms/CSVerrorForm.xml b/test-forms/src/main/resources/forms/CSVerrorForm.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/CSVerrorForm.xml rename to test-forms/src/main/resources/forms/CSVerrorForm.xml diff --git a/collect_app/src/androidTest/assets/forms/CalcTest.xml b/test-forms/src/main/resources/forms/CalcTest.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/CalcTest.xml rename to test-forms/src/main/resources/forms/CalcTest.xml diff --git a/collect_app/src/androidTest/assets/forms/Empty First Repeat.xml b/test-forms/src/main/resources/forms/Empty First Repeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/Empty First Repeat.xml rename to test-forms/src/main/resources/forms/Empty First Repeat.xml diff --git a/collect_app/src/androidTest/assets/forms/OnePageFormShort.xml b/test-forms/src/main/resources/forms/OnePageFormShort.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/OnePageFormShort.xml rename to test-forms/src/main/resources/forms/OnePageFormShort.xml diff --git a/collect_app/src/androidTest/assets/forms/OnePageFormValid2.xml b/test-forms/src/main/resources/forms/OnePageFormValid2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/OnePageFormValid2.xml rename to test-forms/src/main/resources/forms/OnePageFormValid2.xml diff --git a/collect_app/src/androidTest/assets/forms/RepeatCount.xml b/test-forms/src/main/resources/forms/RepeatCount.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/RepeatCount.xml rename to test-forms/src/main/resources/forms/RepeatCount.xml diff --git a/collect_app/src/androidTest/assets/forms/RepeatGroupAndGroup.xml b/test-forms/src/main/resources/forms/RepeatGroupAndGroup.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/RepeatGroupAndGroup.xml rename to test-forms/src/main/resources/forms/RepeatGroupAndGroup.xml diff --git a/collect_app/src/androidTest/assets/forms/RepeatTitles_1648.xml b/test-forms/src/main/resources/forms/RepeatTitles_1648.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/RepeatTitles_1648.xml rename to test-forms/src/main/resources/forms/RepeatTitles_1648.xml diff --git a/collect_app/src/androidTest/assets/forms/TestRepeat.xml b/test-forms/src/main/resources/forms/TestRepeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/TestRepeat.xml rename to test-forms/src/main/resources/forms/TestRepeat.xml diff --git a/collect_app/src/androidTest/assets/forms/all-widgets.xml b/test-forms/src/main/resources/forms/all-widgets.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/all-widgets.xml rename to test-forms/src/main/resources/forms/all-widgets.xml diff --git a/collect_app/src/androidTest/assets/forms/audio-question.xml b/test-forms/src/main/resources/forms/audio-question.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/audio-question.xml rename to test-forms/src/main/resources/forms/audio-question.xml diff --git a/collect_app/src/androidTest/assets/forms/basic.xml b/test-forms/src/main/resources/forms/basic.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/basic.xml rename to test-forms/src/main/resources/forms/basic.xml diff --git a/collect_app/src/androidTest/assets/forms/default_image_answer.xml b/test-forms/src/main/resources/forms/default_image_answer.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/default_image_answer.xml rename to test-forms/src/main/resources/forms/default_image_answer.xml diff --git a/collect_app/src/androidTest/assets/forms/different-search-appearances.xml b/test-forms/src/main/resources/forms/different-search-appearances.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/different-search-appearances.xml rename to test-forms/src/main/resources/forms/different-search-appearances.xml diff --git a/collect_app/src/androidTest/assets/forms/emptyGroupFieldList.xml b/test-forms/src/main/resources/forms/emptyGroupFieldList.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/emptyGroupFieldList.xml rename to test-forms/src/main/resources/forms/emptyGroupFieldList.xml diff --git a/collect_app/src/androidTest/assets/forms/emptyGroupFieldList2.xml b/test-forms/src/main/resources/forms/emptyGroupFieldList2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/emptyGroupFieldList2.xml rename to test-forms/src/main/resources/forms/emptyGroupFieldList2.xml diff --git a/collect_app/src/androidTest/assets/forms/encrypted-no-instanceID.xml b/test-forms/src/main/resources/forms/encrypted-no-instanceID.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/encrypted-no-instanceID.xml rename to test-forms/src/main/resources/forms/encrypted-no-instanceID.xml diff --git a/collect_app/src/androidTest/assets/forms/encrypted.xml b/test-forms/src/main/resources/forms/encrypted.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/encrypted.xml rename to test-forms/src/main/resources/forms/encrypted.xml diff --git a/collect_app/src/androidTest/assets/forms/event-odk-new-repeat.xml b/test-forms/src/main/resources/forms/event-odk-new-repeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/event-odk-new-repeat.xml rename to test-forms/src/main/resources/forms/event-odk-new-repeat.xml diff --git a/collect_app/src/androidTest/assets/forms/external-audio-question.xml b/test-forms/src/main/resources/forms/external-audio-question.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/external-audio-question.xml rename to test-forms/src/main/resources/forms/external-audio-question.xml diff --git a/collect_app/src/androidTest/assets/forms/external-csv-search.xml b/test-forms/src/main/resources/forms/external-csv-search.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/external-csv-search.xml rename to test-forms/src/main/resources/forms/external-csv-search.xml diff --git a/collect_app/src/androidTest/assets/forms/external_csv_form.xml b/test-forms/src/main/resources/forms/external_csv_form.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/external_csv_form.xml rename to test-forms/src/main/resources/forms/external_csv_form.xml diff --git a/collect_app/src/androidTest/assets/forms/external_data_questions.xml b/test-forms/src/main/resources/forms/external_data_questions.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/external_data_questions.xml rename to test-forms/src/main/resources/forms/external_data_questions.xml diff --git a/collect_app/src/androidTest/assets/forms/external_select_10.xml b/test-forms/src/main/resources/forms/external_select_10.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/external_select_10.xml rename to test-forms/src/main/resources/forms/external_select_10.xml diff --git a/collect_app/src/androidTest/assets/forms/field-list-repeat.xml b/test-forms/src/main/resources/forms/field-list-repeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/field-list-repeat.xml rename to test-forms/src/main/resources/forms/field-list-repeat.xml diff --git a/collect_app/src/androidTest/assets/forms/fieldListInFieldList.xml b/test-forms/src/main/resources/forms/fieldListInFieldList.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fieldListInFieldList.xml rename to test-forms/src/main/resources/forms/fieldListInFieldList.xml diff --git a/collect_app/src/androidTest/assets/forms/fieldListWithQuestionAndRegularGroupInside.xml b/test-forms/src/main/resources/forms/fieldListWithQuestionAndRegularGroupInside.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fieldListWithQuestionAndRegularGroupInside.xml rename to test-forms/src/main/resources/forms/fieldListWithQuestionAndRegularGroupInside.xml diff --git a/collect_app/src/androidTest/assets/forms/fieldListWithQuestionsAndRegularGroupsInside.xml b/test-forms/src/main/resources/forms/fieldListWithQuestionsAndRegularGroupsInside.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fieldListWithQuestionsAndRegularGroupsInside.xml rename to test-forms/src/main/resources/forms/fieldListWithQuestionsAndRegularGroupsInside.xml diff --git a/collect_app/src/androidTest/assets/forms/fieldlist-updates.xml b/test-forms/src/main/resources/forms/fieldlist-updates.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fieldlist-updates.xml rename to test-forms/src/main/resources/forms/fieldlist-updates.xml diff --git a/collect_app/src/androidTest/assets/forms/fieldlist-updates_nocsv.xml b/test-forms/src/main/resources/forms/fieldlist-updates_nocsv.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fieldlist-updates_nocsv.xml rename to test-forms/src/main/resources/forms/fieldlist-updates_nocsv.xml diff --git a/collect_app/src/androidTest/assets/forms/fixed-count-repeat.xml b/test-forms/src/main/resources/forms/fixed-count-repeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/fixed-count-repeat.xml rename to test-forms/src/main/resources/forms/fixed-count-repeat.xml diff --git a/collect_app/src/androidTest/assets/forms/form1.xml b/test-forms/src/main/resources/forms/form1.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form1.xml rename to test-forms/src/main/resources/forms/form1.xml diff --git a/collect_app/src/androidTest/assets/forms/form2.xml b/test-forms/src/main/resources/forms/form2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form2.xml rename to test-forms/src/main/resources/forms/form2.xml diff --git a/collect_app/src/androidTest/assets/forms/form3.xml b/test-forms/src/main/resources/forms/form3.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form3.xml rename to test-forms/src/main/resources/forms/form3.xml diff --git a/collect_app/src/androidTest/assets/forms/form4.xml b/test-forms/src/main/resources/forms/form4.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form4.xml rename to test-forms/src/main/resources/forms/form4.xml diff --git a/collect_app/src/androidTest/assets/forms/form5.xml b/test-forms/src/main/resources/forms/form5.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form5.xml rename to test-forms/src/main/resources/forms/form5.xml diff --git a/collect_app/src/androidTest/assets/forms/form6.xml b/test-forms/src/main/resources/forms/form6.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form6.xml rename to test-forms/src/main/resources/forms/form6.xml diff --git a/collect_app/src/androidTest/assets/forms/form7.xml b/test-forms/src/main/resources/forms/form7.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form7.xml rename to test-forms/src/main/resources/forms/form7.xml diff --git a/collect_app/src/androidTest/assets/forms/form8.xml b/test-forms/src/main/resources/forms/form8.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form8.xml rename to test-forms/src/main/resources/forms/form8.xml diff --git a/collect_app/src/androidTest/assets/forms/form9.xml b/test-forms/src/main/resources/forms/form9.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form9.xml rename to test-forms/src/main/resources/forms/form9.xml diff --git a/collect_app/src/androidTest/assets/forms/formHierarchy1.xml b/test-forms/src/main/resources/forms/formHierarchy1.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/formHierarchy1.xml rename to test-forms/src/main/resources/forms/formHierarchy1.xml diff --git a/collect_app/src/androidTest/assets/forms/formHierarchy2.xml b/test-forms/src/main/resources/forms/formHierarchy2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/formHierarchy2.xml rename to test-forms/src/main/resources/forms/formHierarchy2.xml diff --git a/collect_app/src/androidTest/assets/forms/formHierarchy3.xml b/test-forms/src/main/resources/forms/formHierarchy3.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/formHierarchy3.xml rename to test-forms/src/main/resources/forms/formHierarchy3.xml diff --git a/collect_app/src/androidTest/assets/forms/formWithExternalFiles.xml b/test-forms/src/main/resources/forms/formWithExternalFiles.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/formWithExternalFiles.xml rename to test-forms/src/main/resources/forms/formWithExternalFiles.xml diff --git a/collect_app/src/androidTest/assets/forms/form_design_error.xml b/test-forms/src/main/resources/forms/form_design_error.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form_design_error.xml rename to test-forms/src/main/resources/forms/form_design_error.xml diff --git a/collect_app/src/androidTest/assets/forms/form_styling.xml b/test-forms/src/main/resources/forms/form_styling.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/form_styling.xml rename to test-forms/src/main/resources/forms/form_styling.xml diff --git a/collect_app/src/androidTest/assets/forms/formulaire_adherent.xml b/test-forms/src/main/resources/forms/formulaire_adherent.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/formulaire_adherent.xml rename to test-forms/src/main/resources/forms/formulaire_adherent.xml diff --git a/collect_app/src/androidTest/assets/forms/g6Error.xml b/test-forms/src/main/resources/forms/g6Error.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/g6Error.xml rename to test-forms/src/main/resources/forms/g6Error.xml diff --git a/collect_app/src/androidTest/assets/forms/g6Error2.xml b/test-forms/src/main/resources/forms/g6Error2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/g6Error2.xml rename to test-forms/src/main/resources/forms/g6Error2.xml diff --git a/collect_app/src/androidTest/assets/forms/guidance_hint_form.xml b/test-forms/src/main/resources/forms/guidance_hint_form.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/guidance_hint_form.xml rename to test-forms/src/main/resources/forms/guidance_hint_form.xml diff --git a/collect_app/src/androidTest/assets/forms/hints_textq.xml b/test-forms/src/main/resources/forms/hints_textq.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/hints_textq.xml rename to test-forms/src/main/resources/forms/hints_textq.xml diff --git a/collect_app/src/androidTest/assets/forms/identify-user-audit-false.xml b/test-forms/src/main/resources/forms/identify-user-audit-false.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/identify-user-audit-false.xml rename to test-forms/src/main/resources/forms/identify-user-audit-false.xml diff --git a/collect_app/src/androidTest/assets/forms/identify-user-audit.xml b/test-forms/src/main/resources/forms/identify-user-audit.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/identify-user-audit.xml rename to test-forms/src/main/resources/forms/identify-user-audit.xml diff --git a/collect_app/src/androidTest/assets/forms/image_widget.xml b/test-forms/src/main/resources/forms/image_widget.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/image_widget.xml rename to test-forms/src/main/resources/forms/image_widget.xml diff --git a/collect_app/src/androidTest/assets/forms/intent-group.xml b/test-forms/src/main/resources/forms/intent-group.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/intent-group.xml rename to test-forms/src/main/resources/forms/intent-group.xml diff --git a/collect_app/src/androidTest/assets/forms/internal-audio-question.xml b/test-forms/src/main/resources/forms/internal-audio-question.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/internal-audio-question.xml rename to test-forms/src/main/resources/forms/internal-audio-question.xml diff --git a/collect_app/src/androidTest/assets/forms/internal_select_10.xml b/test-forms/src/main/resources/forms/internal_select_10.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/internal_select_10.xml rename to test-forms/src/main/resources/forms/internal_select_10.xml diff --git a/collect_app/src/androidTest/assets/forms/invalid-events.xml b/test-forms/src/main/resources/forms/invalid-events.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/invalid-events.xml rename to test-forms/src/main/resources/forms/invalid-events.xml diff --git a/collect_app/src/androidTest/assets/forms/invalid-form.xml b/test-forms/src/main/resources/forms/invalid-form.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/invalid-form.xml rename to test-forms/src/main/resources/forms/invalid-form.xml diff --git a/collect_app/src/androidTest/assets/forms/likert_test.xml b/test-forms/src/main/resources/forms/likert_test.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/likert_test.xml rename to test-forms/src/main/resources/forms/likert_test.xml diff --git a/collect_app/src/androidTest/assets/forms/location-audit.xml b/test-forms/src/main/resources/forms/location-audit.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/location-audit.xml rename to test-forms/src/main/resources/forms/location-audit.xml diff --git a/collect_app/src/androidTest/assets/forms/manyQ.xml b/test-forms/src/main/resources/forms/manyQ.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/manyQ.xml rename to test-forms/src/main/resources/forms/manyQ.xml diff --git a/collect_app/src/androidTest/assets/forms/metadata.xml b/test-forms/src/main/resources/forms/metadata.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/metadata.xml rename to test-forms/src/main/resources/forms/metadata.xml diff --git a/collect_app/src/androidTest/assets/forms/metadata2.xml b/test-forms/src/main/resources/forms/metadata2.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/metadata2.xml rename to test-forms/src/main/resources/forms/metadata2.xml diff --git a/collect_app/src/androidTest/assets/forms/multiple-events.xml b/test-forms/src/main/resources/forms/multiple-events.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/multiple-events.xml rename to test-forms/src/main/resources/forms/multiple-events.xml diff --git a/collect_app/src/androidTest/assets/forms/nested-repeats-complex.xml b/test-forms/src/main/resources/forms/nested-repeats-complex.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/nested-repeats-complex.xml rename to test-forms/src/main/resources/forms/nested-repeats-complex.xml diff --git a/collect_app/src/androidTest/assets/forms/nigeria-wards.xml b/test-forms/src/main/resources/forms/nigeria-wards.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/nigeria-wards.xml rename to test-forms/src/main/resources/forms/nigeria-wards.xml diff --git a/collect_app/src/androidTest/assets/forms/no-track-changes-reason.xml b/test-forms/src/main/resources/forms/no-track-changes-reason.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/no-track-changes-reason.xml rename to test-forms/src/main/resources/forms/no-track-changes-reason.xml diff --git a/collect_app/src/androidTest/assets/forms/numberInCSV.xml b/test-forms/src/main/resources/forms/numberInCSV.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/numberInCSV.xml rename to test-forms/src/main/resources/forms/numberInCSV.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-audit-constraint.xml b/test-forms/src/main/resources/forms/one-question-audit-constraint.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-audit-constraint.xml rename to test-forms/src/main/resources/forms/one-question-audit-constraint.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-audit.xml b/test-forms/src/main/resources/forms/one-question-audit.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-audit.xml rename to test-forms/src/main/resources/forms/one-question-audit.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-autosend.xml b/test-forms/src/main/resources/forms/one-question-autosend.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-autosend.xml rename to test-forms/src/main/resources/forms/one-question-autosend.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-background-audio-multiple.xml b/test-forms/src/main/resources/forms/one-question-background-audio-multiple.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-background-audio-multiple.xml rename to test-forms/src/main/resources/forms/one-question-background-audio-multiple.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-background-audio.xml b/test-forms/src/main/resources/forms/one-question-background-audio.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-background-audio.xml rename to test-forms/src/main/resources/forms/one-question-background-audio.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-entity.xml b/test-forms/src/main/resources/forms/one-question-entity.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-entity.xml rename to test-forms/src/main/resources/forms/one-question-entity.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-google.xml b/test-forms/src/main/resources/forms/one-question-google.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-google.xml rename to test-forms/src/main/resources/forms/one-question-google.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-repeat.xml b/test-forms/src/main/resources/forms/one-question-repeat.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-repeat.xml rename to test-forms/src/main/resources/forms/one-question-repeat.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-translation.xml b/test-forms/src/main/resources/forms/one-question-translation.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-translation.xml rename to test-forms/src/main/resources/forms/one-question-translation.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question-updated.xml b/test-forms/src/main/resources/forms/one-question-updated.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question-updated.xml rename to test-forms/src/main/resources/forms/one-question-updated.xml diff --git a/collect_app/src/androidTest/assets/forms/one-question.xml b/test-forms/src/main/resources/forms/one-question.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/one-question.xml rename to test-forms/src/main/resources/forms/one-question.xml diff --git a/collect_app/src/androidTest/assets/forms/predicate-warning.xml b/test-forms/src/main/resources/forms/predicate-warning.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/predicate-warning.xml rename to test-forms/src/main/resources/forms/predicate-warning.xml diff --git a/collect_app/src/androidTest/assets/forms/random.xml b/test-forms/src/main/resources/forms/random.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/random.xml rename to test-forms/src/main/resources/forms/random.xml diff --git a/collect_app/src/androidTest/assets/forms/randomTest_broken.xml b/test-forms/src/main/resources/forms/randomTest_broken.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/randomTest_broken.xml rename to test-forms/src/main/resources/forms/randomTest_broken.xml diff --git a/collect_app/src/androidTest/assets/forms/ranking_widget.xml b/test-forms/src/main/resources/forms/ranking_widget.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/ranking_widget.xml rename to test-forms/src/main/resources/forms/ranking_widget.xml diff --git a/collect_app/src/androidTest/assets/forms/regularGroupWithFieldListGroupInside.xml b/test-forms/src/main/resources/forms/regularGroupWithFieldListGroupInside.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/regularGroupWithFieldListGroupInside.xml rename to test-forms/src/main/resources/forms/regularGroupWithFieldListGroupInside.xml diff --git a/collect_app/src/androidTest/assets/forms/regularGroupWithQuestionAndRegularGroupInside.xml b/test-forms/src/main/resources/forms/regularGroupWithQuestionAndRegularGroupInside.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/regularGroupWithQuestionAndRegularGroupInside.xml rename to test-forms/src/main/resources/forms/regularGroupWithQuestionAndRegularGroupInside.xml diff --git a/collect_app/src/androidTest/assets/forms/regularGroupWithQuestionsAndRegularGroupInside.xml b/test-forms/src/main/resources/forms/regularGroupWithQuestionsAndRegularGroupInside.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/regularGroupWithQuestionsAndRegularGroupInside.xml rename to test-forms/src/main/resources/forms/regularGroupWithQuestionsAndRegularGroupInside.xml diff --git a/collect_app/src/androidTest/assets/forms/repeat_group_form.xml b/test-forms/src/main/resources/forms/repeat_group_form.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/repeat_group_form.xml rename to test-forms/src/main/resources/forms/repeat_group_form.xml diff --git a/collect_app/src/androidTest/assets/forms/repeat_group_new.xml b/test-forms/src/main/resources/forms/repeat_group_new.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/repeat_group_new.xml rename to test-forms/src/main/resources/forms/repeat_group_new.xml diff --git a/collect_app/src/androidTest/assets/forms/repeat_groups.xml b/test-forms/src/main/resources/forms/repeat_groups.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/repeat_groups.xml rename to test-forms/src/main/resources/forms/repeat_groups.xml diff --git a/collect_app/src/androidTest/assets/forms/requiredJR275.xml b/test-forms/src/main/resources/forms/requiredJR275.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/requiredJR275.xml rename to test-forms/src/main/resources/forms/requiredJR275.xml diff --git a/collect_app/src/androidTest/assets/forms/requiredQuestionInFieldList.xml b/test-forms/src/main/resources/forms/requiredQuestionInFieldList.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/requiredQuestionInFieldList.xml rename to test-forms/src/main/resources/forms/requiredQuestionInFieldList.xml diff --git a/collect_app/src/androidTest/assets/forms/search_and_select.xml b/test-forms/src/main/resources/forms/search_and_select.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/search_and_select.xml rename to test-forms/src/main/resources/forms/search_and_select.xml diff --git a/collect_app/src/androidTest/assets/forms/selectOneExternal.xml b/test-forms/src/main/resources/forms/selectOneExternal.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/selectOneExternal.xml rename to test-forms/src/main/resources/forms/selectOneExternal.xml diff --git a/collect_app/src/androidTest/assets/forms/select_one_external.xml b/test-forms/src/main/resources/forms/select_one_external.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/select_one_external.xml rename to test-forms/src/main/resources/forms/select_one_external.xml diff --git a/collect_app/src/androidTest/assets/forms/setgeopoint-action.xml b/test-forms/src/main/resources/forms/setgeopoint-action.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/setgeopoint-action.xml rename to test-forms/src/main/resources/forms/setgeopoint-action.xml diff --git a/collect_app/src/androidTest/assets/forms/setlocation-action-instance-load.xml b/test-forms/src/main/resources/forms/setlocation-action-instance-load.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/setlocation-action-instance-load.xml rename to test-forms/src/main/resources/forms/setlocation-action-instance-load.xml diff --git a/collect_app/src/androidTest/assets/forms/setlocation-and-audit-location.xml b/test-forms/src/main/resources/forms/setlocation-and-audit-location.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/setlocation-and-audit-location.xml rename to test-forms/src/main/resources/forms/setlocation-and-audit-location.xml diff --git a/collect_app/src/androidTest/assets/forms/simple-search-external-csv.xml b/test-forms/src/main/resources/forms/simple-search-external-csv.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/simple-search-external-csv.xml rename to test-forms/src/main/resources/forms/simple-search-external-csv.xml diff --git a/collect_app/src/androidTest/assets/forms/simpleFieldList.xml b/test-forms/src/main/resources/forms/simpleFieldList.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/simpleFieldList.xml rename to test-forms/src/main/resources/forms/simpleFieldList.xml diff --git a/collect_app/src/androidTest/assets/forms/single-geopoint.xml b/test-forms/src/main/resources/forms/single-geopoint.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/single-geopoint.xml rename to test-forms/src/main/resources/forms/single-geopoint.xml diff --git a/collect_app/src/androidTest/assets/forms/start-geopoint.xml b/test-forms/src/main/resources/forms/start-geopoint.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/start-geopoint.xml rename to test-forms/src/main/resources/forms/start-geopoint.xml diff --git a/collect_app/src/androidTest/assets/forms/string_widgets_in_field_list.xml b/test-forms/src/main/resources/forms/string_widgets_in_field_list.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/string_widgets_in_field_list.xml rename to test-forms/src/main/resources/forms/string_widgets_in_field_list.xml diff --git a/collect_app/src/androidTest/assets/forms/t21257.xml b/test-forms/src/main/resources/forms/t21257.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/t21257.xml rename to test-forms/src/main/resources/forms/t21257.xml diff --git a/collect_app/src/androidTest/assets/forms/test_multiselect_cleared.xml b/test-forms/src/main/resources/forms/test_multiselect_cleared.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/test_multiselect_cleared.xml rename to test-forms/src/main/resources/forms/test_multiselect_cleared.xml diff --git a/collect_app/src/androidTest/assets/forms/threeNestedFieldListGroups.xml b/test-forms/src/main/resources/forms/threeNestedFieldListGroups.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/threeNestedFieldListGroups.xml rename to test-forms/src/main/resources/forms/threeNestedFieldListGroups.xml diff --git a/collect_app/src/androidTest/assets/forms/track-changes-reason-on-edit.xml b/test-forms/src/main/resources/forms/track-changes-reason-on-edit.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/track-changes-reason-on-edit.xml rename to test-forms/src/main/resources/forms/track-changes-reason-on-edit.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question-audit.xml b/test-forms/src/main/resources/forms/two-question-audit.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question-audit.xml rename to test-forms/src/main/resources/forms/two-question-audit.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question-required.xml b/test-forms/src/main/resources/forms/two-question-required.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question-required.xml rename to test-forms/src/main/resources/forms/two-question-required.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question-save-incomplete-required.xml b/test-forms/src/main/resources/forms/two-question-save-incomplete-required.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question-save-incomplete-required.xml rename to test-forms/src/main/resources/forms/two-question-save-incomplete-required.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question-save-incomplete.xml b/test-forms/src/main/resources/forms/two-question-save-incomplete.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question-save-incomplete.xml rename to test-forms/src/main/resources/forms/two-question-save-incomplete.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question-updated.xml b/test-forms/src/main/resources/forms/two-question-updated.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question-updated.xml rename to test-forms/src/main/resources/forms/two-question-updated.xml diff --git a/collect_app/src/androidTest/assets/forms/two-question.xml b/test-forms/src/main/resources/forms/two-question.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/two-question.xml rename to test-forms/src/main/resources/forms/two-question.xml diff --git a/collect_app/src/androidTest/assets/forms/twoNestedRegularGroups.xml b/test-forms/src/main/resources/forms/twoNestedRegularGroups.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/twoNestedRegularGroups.xml rename to test-forms/src/main/resources/forms/twoNestedRegularGroups.xml diff --git a/collect_app/src/androidTest/assets/forms/validate.xml b/test-forms/src/main/resources/forms/validate.xml similarity index 100% rename from collect_app/src/androidTest/assets/forms/validate.xml rename to test-forms/src/main/resources/forms/validate.xml From 9d66c99569b8709b9fe48d7ed5cc4a289bdf460c Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 10 Aug 2023 11:32:26 +0100 Subject: [PATCH 08/35] Integrate test file utils into main set --- .../feature/formentry/AudioRecordingTest.java | 5 ++- .../BackgroundAudioRecordingTest.java | 4 +- .../formentry/ExternalAudioRecordingTest.java | 5 ++- .../feature/formentry/IntentGroupTest.java | 5 ++- .../collect/android/support/FileUtils.java | 43 ------------------- .../collect/android/support/StorageUtils.kt | 19 ++++++-- .../android/support/StubOpenRosaServer.java | 2 +- .../collect/android/utilities/FileUtils.java | 28 ++++++++++++ 8 files changed, 56 insertions(+), 55 deletions(-) delete mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java index da276a15bac..1d341b74b7f 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java @@ -1,10 +1,11 @@ package org.odk.collect.android.feature.formentry; -import static org.odk.collect.android.support.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; import android.app.Application; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; @@ -35,7 +36,7 @@ public AudioRecorder providesAudioRecorder(Application application) { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets("media/test.m4a", stubRecording.getAbsolutePath()); + copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); stubAudioRecorderViewModel = new StubAudioRecorder(stubRecording.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java index 05b1dc84ef7..62dff195a2b 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java @@ -4,7 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.odk.collect.android.support.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; import android.Manifest; import android.app.Activity; @@ -56,7 +56,7 @@ public AudioRecorder providesAudioRecorder(Application application) { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets("media/test.m4a", stubRecording.getAbsolutePath()); + copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); stubAudioRecorderViewModel = new StubAudioRecorder(stubRecording.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java index 0171286395f..f7c3ce63807 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java @@ -2,7 +2,7 @@ import static androidx.test.espresso.intent.Intents.intending; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static org.odk.collect.android.support.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; import android.app.Activity; import android.app.Instrumentation; @@ -11,6 +11,7 @@ import android.provider.MediaStore; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; @@ -39,7 +40,7 @@ public class ExternalAudioRecordingTest { try { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets("media/test.m4a", stubRecording.getAbsolutePath()); + copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); Intent intent = new Intent(); intent.setData(Uri.fromFile(stubRecording)); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java index adcc85adc09..f6f384bd061 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.odk.collect.android.support.matchers.CustomMatchers.withIndex; -import static org.odk.collect.android.support.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; import android.app.Activity; import android.app.Instrumentation; @@ -50,6 +50,7 @@ import androidx.core.content.FileProvider; import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; @@ -251,7 +252,7 @@ private Uri createTempFile(String name, String extension) throws IOException { .getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); File file = File.createTempFile(name, extension, downloadsDir); - copyFileFromAssets("media" + File.separator + name + "." + extension, file.getPath()); + copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), file.getPath(), "media" + File.separator + name + "." + extension); return getUriForFile(file); } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java deleted file mode 100644 index e1ce3bad76f..00000000000 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/FileUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.odk.collect.android.support; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.apache.commons.io.IOUtils; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public final class FileUtils { - - private FileUtils() { - } - - public static void copyFileFromAssets(String fileSourcePath, String fileDestPath) throws IOException { - copyStreamToPath(getAssetAsStream(fileSourcePath), fileDestPath); - } - - public static void copyFileFromResources(String fileSourcePath, String fileDestPath) throws IOException { - copyStreamToPath(getResourceAsStream(fileSourcePath), fileDestPath); - } - - @NonNull - public static InputStream getAssetAsStream(String fileSourcePath) throws IOException { - return InstrumentationRegistry.getInstrumentation().getContext().getAssets().open(fileSourcePath); - } - - @Nullable - public static InputStream getResourceAsStream(String fileSourcePath) { - return FileUtils.class.getResourceAsStream("/" + fileSourcePath); - } - - private static void copyStreamToPath(InputStream inputStream, String fileDestPath) throws IOException { - try (InputStream input = inputStream; - OutputStream output = new FileOutputStream(fileDestPath)) { - IOUtils.copy(input, output); - } - } -} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt index 9542778da80..602806e5cf2 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt @@ -17,11 +17,13 @@ package org.odk.collect.android.support import android.app.Application import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry import org.apache.commons.csv.CSVFormat import org.apache.commons.csv.CSVRecord import org.odk.collect.android.formmanagement.LocalFormUseCases import org.odk.collect.android.injection.DaggerUtils import org.odk.collect.android.storage.StorageSubdirectory +import org.odk.collect.android.utilities.FileUtils import java.io.File import java.io.FileReader import java.io.IOException @@ -83,13 +85,20 @@ object StorageUtils { fun copyInstance(instanceFileName: String, projectName: String) { val instanceDirPath = getInstancesDirPath(projectName) + instanceFileName.split("\\.".toRegex()).toTypedArray()[0] File(instanceDirPath).mkdir() - FileUtils.copyFileFromAssets("instances/$instanceFileName", "$instanceDirPath/$instanceFileName") + FileUtils.copyFileFromAssets( + InstrumentationRegistry.getInstrumentation().getContext(), + "$instanceDirPath/$instanceFileName", + "instances/$instanceFileName" + ) } @Throws(IOException::class) private fun copyForm(formFilename: String, copyTo: String, projectName: String): String { val pathname = getFormsDirPath(projectName) + copyTo - FileUtils.copyFileFromResources("forms/$formFilename", pathname) + FileUtils.copyFileFromResources( + "forms/$formFilename", + pathname + ) return pathname } @@ -98,7 +107,11 @@ object StorageUtils { val mediaPathName = getFormsDirPath(projectName) + formFilename.replace(".xml", "") + org.odk.collect.android.utilities.FileUtils.MEDIA_SUFFIX + "/" org.odk.collect.android.utilities.FileUtils.checkMediaPath(File(mediaPathName)) for (mediaFilePath in mediaFilePaths) { - FileUtils.copyFileFromAssets("media/$mediaFilePath", mediaPathName + getMediaFileName(mediaFilePath)) + FileUtils.copyFileFromAssets( + InstrumentationRegistry.getInstrumentation().getContext(), + mediaPathName + getMediaFileName(mediaFilePath), + "media/$mediaFilePath" + ) } } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java index 2e76cac8876..4355d105128 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java @@ -1,6 +1,6 @@ package org.odk.collect.android.support; -import static org.odk.collect.android.support.FileUtils.getResourceAsStream; +import static org.odk.collect.android.utilities.FileUtils.getResourceAsStream; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java b/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java index 47b0df12cff..bad861e42fa 100644 --- a/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java +++ b/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java @@ -22,6 +22,9 @@ import android.net.Uri; import android.webkit.MimeTypeMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.google.common.base.CharMatcher; import org.apache.commons.io.IOUtils; @@ -563,4 +566,29 @@ public static void interuptablyWriteFile(InputStream inputStream, File destinati throw new RuntimeException(msg); } } + + public static void copyFileFromAssets(Context context, String fileDestPath, String fileSourcePath) throws IOException { + copyStreamToPath(getAssetAsStream(context, fileSourcePath), fileDestPath); + } + + public static void copyFileFromResources(String fileSourcePath, String fileDestPath) throws IOException { + copyStreamToPath(getResourceAsStream(fileSourcePath), fileDestPath); + } + + @NonNull + public static InputStream getAssetAsStream(Context context, String fileSourcePath) throws IOException { + return context.getAssets().open(fileSourcePath); + } + + @Nullable + public static InputStream getResourceAsStream(String fileSourcePath) { + return FileUtils.class.getResourceAsStream("/" + fileSourcePath); + } + + private static void copyStreamToPath(InputStream inputStream, String fileDestPath) throws IOException { + try (InputStream input = inputStream; + OutputStream output = new FileOutputStream(fileDestPath)) { + IOUtils.copy(input, output); + } + } } From c0211ce37ac6366d5162686065b2ab2b9f063044 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 10 Aug 2023 12:06:58 +0100 Subject: [PATCH 09/35] Use real form for local process restore test --- collect_app/build.gradle | 4 +-- .../collect/android/utilities/FileUtils.java | 5 ++++ .../activities/FormFillingActivityTest.kt | 28 +++++++++++-------- .../org/odk/collect/formstest/FormFixtures.kt | 17 +++++++---- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/collect_app/build.gradle b/collect_app/build.gradle index 575bd9a4a0c..34cc142d402 100644 --- a/collect_app/build.gradle +++ b/collect_app/build.gradle @@ -364,6 +364,7 @@ dependencies { exclude group: 'org.robolectric' // Some tests in `collect_app` don't work with newer Robolectric } testImplementation(project(":shadows")) + testImplementation(project(":test-forms")) testImplementation Dependencies.robolectric @@ -380,6 +381,7 @@ dependencies { testImplementation Dependencies.androidx_test_core_ktx androidTestImplementation project(':androidtest') + androidTestImplementation project(':test-forms') androidTestImplementation Dependencies.mockito_android androidTestImplementation Dependencies.androidx_test_ext_junit @@ -394,8 +396,6 @@ dependencies { androidTestImplementation Dependencies.androidx_work_testing androidTestImplementation Dependencies.uiautomator - androidTestImplementation project(':test-forms') - debugImplementation project(':fragmentstest') debugImplementation(project(':testshared')) { exclude group: 'org.robolectric' // Some tests in `collect_app` don't work with newer Robolectric diff --git a/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java b/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java index bad861e42fa..f49491573a7 100644 --- a/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java +++ b/collect_app/src/main/java/org/odk/collect/android/utilities/FileUtils.java @@ -571,6 +571,11 @@ public static void copyFileFromAssets(Context context, String fileDestPath, Stri copyStreamToPath(getAssetAsStream(context, fileSourcePath), fileDestPath); } + public static File copyFileFromResources(String fileSourcePath, File fileDest) throws IOException { + copyStreamToPath(getResourceAsStream(fileSourcePath), fileDest.getAbsolutePath()); + return fileDest; + } + public static void copyFileFromResources(String fileSourcePath, String fileDestPath) throws IOException { copyStreamToPath(getResourceAsStream(fileSourcePath), fileDestPath); } diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index b9d9e8fa9eb..b3a4d359524 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -11,6 +11,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.equalTo import org.junit.Before import org.junit.Test @@ -23,14 +24,17 @@ import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes -import org.odk.collect.formstest.FormUtils.buildForm +import org.odk.collect.android.utilities.FileUtils +import org.odk.collect.formstest.FormFixtures.form import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf +import java.io.File @RunWith(AndroidJUnit4::class) class FormFillingActivityTest { private val application = ApplicationProvider.getApplicationContext() + private val component = DaggerUtils.getComponent(application) private val dependencies = object : AppDependencyModule() {} @Before @@ -42,12 +46,14 @@ class FormFillingActivityTest { fun whenProcessIsKilledAndRestoredDuringFormEntry_returnsToHierarchy() { val projectId = CollectHelpers.setupDemoProject() - val formsRepository = DaggerUtils.getComponent(application).formsRepositoryProvider().get() - val storagePathProvider = DaggerUtils.getComponent(application).storagePathProvider() - val formsDir = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) - val newForm = - buildForm(formId = "id", version = "version", formFilesPath = formsDir).build() - val form = formsRepository.save(newForm) + val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) + val formFile = FileUtils.copyFileFromResources( + "forms/one-question.xml", + File(formsDir, "one-question.xml") + ) + + val formsRepository = component.formsRepositoryProvider().get() + val form = formsRepository.save(form(formFile = formFile)) val intent = FormFillingIntentFactory.newInstanceIntent( application, @@ -57,8 +63,8 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() - onView(withText("Test Form")).check(matches(isDisplayed())) - onView(withText("question label")).check(matches(isDisplayed())) + onView(withText("One Question")).check(matches(isDisplayed())) + onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) // Destroy activity with saved instance state val outState = Bundle() @@ -79,7 +85,7 @@ class FormFillingActivityTest { // Return to FormFillingActivity from FormHierarchyActivity recreated.recreate().get() .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) - onView(withText("Test Form")).check(matches(isDisplayed())) - onView(withText("question label")).check(matches(isDisplayed())) + onView(withText("One Question")).check(matches(isDisplayed())) + onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) } } diff --git a/formstest/src/main/java/org/odk/collect/formstest/FormFixtures.kt b/formstest/src/main/java/org/odk/collect/formstest/FormFixtures.kt index 0eeca118716..4d291cc228c 100644 --- a/formstest/src/main/java/org/odk/collect/formstest/FormFixtures.kt +++ b/formstest/src/main/java/org/odk/collect/formstest/FormFixtures.kt @@ -10,21 +10,28 @@ object FormFixtures { formId: String = "formId", version: String = "1", mediaFiles: List> = emptyList(), - autoSend: String? = null + autoSend: String? = null, + formFile: File? = null ): Form { val formFilesPath = TempFiles.createTempDir().absolutePath - val mediaFilePath = TempFiles.createTempDir().absolutePath + val mediaFilesPath = TempFiles.createTempDir().absolutePath mediaFiles.forEach { (name, contents) -> - File(mediaFilePath, name).also { it.writeBytes(contents.toByteArray()) } + File(mediaFilesPath, name).also { it.writeBytes(contents.toByteArray()) } } return Form.Builder() .displayName("Test Form") .formId(formId) .version(version) - .formFilePath(FormUtils.createFormFixtureFile(formId, version, formFilesPath)) - .formMediaPath(mediaFilePath) + .formFilePath( + formFile?.absolutePath ?: FormUtils.createFormFixtureFile( + formId, + version, + formFilesPath + ) + ) + .formMediaPath(mediaFilesPath) .autoSend(autoSend) .build() } From 8d6f934c047fbe0d4a52cb927464947f806b23fe Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 10 Aug 2023 12:17:26 +0100 Subject: [PATCH 10/35] Correct recreating simulation --- .../odk/collect/android/activities/FormFillingActivityTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index b3a4d359524..03cbff2652d 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -76,7 +76,8 @@ class FormFillingActivityTest { newComponent.applicationInitializer().initialize() // Recreate and assert we start FormHierarchyActivity - val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + val recreated = + Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) From f03335f8f06cd1fc78de30438b85f8dd63e344b8 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 10 Aug 2023 12:25:50 +0100 Subject: [PATCH 11/35] Replace process restore with DialogFragment instrumentation test with local one --- .../feature/formentry/ProcessRestoreTest.kt | 49 ---------------- .../activities/FormFillingActivityTest.kt | 58 +++++++++++++++++++ 2 files changed, 58 insertions(+), 49 deletions(-) delete mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt deleted file mode 100644 index 393e8b40f2f..00000000000 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ProcessRestoreTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.odk.collect.android.feature.formentry - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.runner.RunWith -import org.odk.collect.android.support.pages.FormEntryPage -import org.odk.collect.android.support.pages.FormHierarchyPage -import org.odk.collect.android.support.pages.Page -import org.odk.collect.android.support.rules.FormEntryActivityTestRule -import org.odk.collect.android.support.rules.TestRuleChain - -@RunWith(AndroidJUnit4::class) -class ProcessRestoreTest { - - private val rule = FormEntryActivityTestRule() - - @get:Rule - val ruleChain: RuleChain = TestRuleChain.chain().around(rule) - - @Test - fun whenProcessIsKilledAndRestoredDuringFormEntry_andThereADialogFragmentOpen_returnsToHierarchy() { - rule.setUpProjectAndCopyForm("all-widgets.xml") - .fillNewForm("all-widgets.xml", "All widgets") - .clickGoToArrow() - .clickOnGroup("Select one widgets") - .clickOnQuestion("Select one from map widget") - .clickOnString(org.odk.collect.strings.R.string.select_place) - .let { simulateProcessRestore(FormHierarchyPage("All widgets")) } - - .pressBack(FormEntryPage("All widgets")) - .assertQuestion("Select one from map widget") - } - - /** - * Simulate a "process restore" case where an app in the background is killed by Android - * to reclaim memory, change permissions etc and then the process is recreated (backstack etc) - * when navigated back to - */ - private fun > simulateProcessRestore(destination: Page): Page { - rule.navigateAwayFromActivity() - .destroyActivity() - .simulateProcessRestart() - .restoreActivity() - - return destination.assertOnPage() - } -} diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 03cbff2652d..004f8dc6268 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -6,6 +6,8 @@ import android.content.ComponentName import android.os.Bundle import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText @@ -89,4 +91,60 @@ class FormFillingActivityTest { onView(withText("One Question")).check(matches(isDisplayed())) onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) } + + @Test + fun whenProcessIsKilledAndRestoredDuringFormEntry_andThereADialogFragmentOpen_returnsToHierarchy() { + val projectId = CollectHelpers.setupDemoProject() + + val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) + val formFile = FileUtils.copyFileFromResources( + "forms/all-widgets.xml", + File(formsDir, "all-widgets.xml") + ) + + val formsRepository = component.formsRepositoryProvider().get() + val form = formsRepository.save(form(formFile = formFile)) + + val intent = FormFillingIntentFactory.newInstanceIntent( + application, + FormsContract.getUri(projectId, form!!.dbId), + FormFillingActivity::class + ) + + // Start activity + val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + onView(withText("All widgets")).check(matches(isDisplayed())) + while (true) { + try { + onView(withText("Select one from map widget")).check(matches(isDisplayed())) + onView(withText("Select place")).perform(click()) + break + } catch (e: NoMatchingViewException) { + onView(withText("Next")).perform(click()) + } + } + + // Destroy activity with saved instance state + val outState = Bundle() + initial.saveInstanceState(outState).pause().stop().destroy() + + // Reset process + ApplicationProvider.getApplicationContext().getState().clear() + val newComponent = CollectHelpers.overrideAppDependencyModule(dependencies) + newComponent.applicationInitializer().initialize() + + // Recreate and assert we start FormHierarchyActivity + val recreated = + Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) + assertThat( + shadowOf(initial.get()).nextStartedActivity.component, + equalTo(ComponentName(application, FormHierarchyActivity::class.java)) + ) + + // Return to FormFillingActivity from FormHierarchyActivity + recreated.recreate().get() + .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + onView(withText("All widgets")).check(matches(isDisplayed())) + onView(withText(containsString("Select one from map widget"))).check(matches(isDisplayed())) + } } From 873b650c6c430f090149f0d542f590f6307f579f Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 10 Aug 2023 17:27:36 +0100 Subject: [PATCH 12/35] Migrate FormLoaderTask to use Scheduler --- .../collect/async/SchedulerAsyncTaskMimic.kt | 76 +++++++++++++++++++ .../forms/FormNavigationTest.java | 6 +- .../instrumented/forms/FormUtilsTest.java | 5 +- .../tasks/FormLoaderTaskTest.java | 21 ++--- .../activities/FormFillingActivity.java | 12 +-- .../collect/android/tasks/FormLoaderTask.java | 15 ++-- 6 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 async/src/main/java/org/odk/collect/async/SchedulerAsyncTaskMimic.kt diff --git a/async/src/main/java/org/odk/collect/async/SchedulerAsyncTaskMimic.kt b/async/src/main/java/org/odk/collect/async/SchedulerAsyncTaskMimic.kt new file mode 100644 index 00000000000..8f1f2d680f3 --- /dev/null +++ b/async/src/main/java/org/odk/collect/async/SchedulerAsyncTaskMimic.kt @@ -0,0 +1,76 @@ +package org.odk.collect.async + +import android.os.AsyncTask + +/** + * Basic reimplementation of the [AsyncTask] API that allows an [AsyncTask] implementation to + * use [Scheduler] with minimal internal and external changes. + */ +abstract class SchedulerAsyncTaskMimic(private val scheduler: Scheduler) { + + @Volatile + private var status: AsyncTask.Status = AsyncTask.Status.PENDING + + @Volatile + private var cancelled = false + + protected abstract fun onPreExecute() + protected abstract fun doInBackground(vararg params: Params): Result + protected abstract fun onProgressUpdate(vararg values: Progress) + protected abstract fun onPostExecute(result: Result) + protected abstract fun onCancelled() + + /** + * Execute [doInBackground] on calling thread and return the [Result] value. Should probably + * not be used as a replacement for [AsyncTask.get] (unless it's for testing purposes). + */ + fun executeSynchronously(vararg params: Params): Result { + return doInBackground(*params) + } + + fun execute(vararg params: Params): SchedulerAsyncTaskMimic { + status = AsyncTask.Status.RUNNING + onPreExecute() + + scheduler.immediate( + background = { + doInBackground(*params) + }, + foreground = { result -> + if (cancelled) { + onCancelled() + } else { + onPostExecute(result) + } + + status = AsyncTask.Status.FINISHED + } + ) + + return this + } + + fun getStatus(): AsyncTask.Status { + return status + } + + /** + * Unlike [AsyncTask.cancel], this does not offer the option to attempt to interrupt the + * background thread running [doInBackground]. Calling [cancel] will allow [doInBackground] + * to finish, but will prevent [onPostExecute] from running ([onCancelled] will be run + * instead). + */ + fun cancel() { + cancelled = true + } + + fun isCancelled(): Boolean { + return cancelled + } + + protected fun publishProgress(vararg values: Progress) { + scheduler.immediate( + foreground = { onProgressUpdate(*values) } + ) + } +} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java index 1ddcdf91dd8..07cc0d5fd9d 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java @@ -18,6 +18,8 @@ import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.mock; + import android.app.Application; import androidx.test.core.app.ApplicationProvider; @@ -129,7 +131,7 @@ private void testIndices(String formName, String[] expectedIndices) throws Execu Timber.i(e); } - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath(formName), null, null, formEntryControllerFactory); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath(formName), null, null, formEntryControllerFactory, mock()); formLoaderTask.setFormLoaderListener(new FormLoaderListener() { @Override public void loadingComplete(FormLoaderTask task, FormDef fd, String warningMsg) { @@ -162,7 +164,7 @@ public void onProgressStep(String stepMessage) { } }); - formLoaderTask.execute(formPath(formName)).get(); + formLoaderTask.executeSynchronously(formPath(formName)); } /** diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java index 9c725ae2825..80176e90d1a 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java @@ -1,5 +1,6 @@ package org.odk.collect.android.instrumented.forms; +import static org.mockito.Mockito.mock; import static org.odk.collect.android.support.StorageUtils.copyFormToStorage; import org.javarosa.core.model.FormDef; @@ -48,8 +49,8 @@ public void setUp() throws IOException { public void sessionRootTranslatorOrderDoesNotMatter() throws Exception { final String formPath = new StoragePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + BASIC_FORM; // Load the form in order to populate the ReferenceManager - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - formLoaderTask.execute(formPath).get(); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + formLoaderTask.executeSynchronously(formPath); final File formXml = new File(formPath); final File formMediaDir = FileUtils.getFormMediaDir(formXml); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java index af2476aef5b..1a782c9c131 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.mock; import android.app.Application; @@ -66,8 +67,8 @@ public FormEntryController create(FormDef formDef) { @Test public void loadFormWithSecondaryCSV() throws Exception { final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SECONDARY_INSTANCE_EXTERNAL_CSV_FORM; - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get(); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + FormLoaderTask.FECWrapper wrapper = formLoaderTask.executeSynchronously(formPath); Assert.assertNotNull(wrapper); } @@ -75,16 +76,16 @@ public void loadFormWithSecondaryCSV() throws Exception { @Test public void loadSearchFromExternalCSV() throws Exception { final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM; - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get(); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + FormLoaderTask.FECWrapper wrapper = formLoaderTask.executeSynchronously(formPath); assertThat(wrapper, notNullValue()); } @Test public void loadSearchFromexternalCsvLeavesFileUnchanged() throws Exception { final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM; - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get(); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + FormLoaderTask.FECWrapper wrapper = formLoaderTask.executeSynchronously(formPath); Assert.assertNotNull(wrapper); Assert.assertNotNull(wrapper.getController()); @@ -98,8 +99,8 @@ public void loadSearchFromexternalCsvLeavesFileUnchanged() throws Exception { public void loadSearchFromExternalCSVmultipleTimes() throws Exception { final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM; // initial load with side effects - FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get(); + FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + FormLoaderTask.FECWrapper wrapper = formLoaderTask.executeSynchronously(formPath); Assert.assertNotNull(wrapper); Assert.assertNotNull(wrapper.getController()); @@ -109,8 +110,8 @@ public void loadSearchFromExternalCSVmultipleTimes() throws Exception { long dbLastModified = dbFile.lastModified(); // subsequent load should succeed despite side effects from import - formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory); - wrapper = formLoaderTask.execute(formPath).get(); + formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory, mock()); + wrapper = formLoaderTask.executeSynchronously(formPath); Assert.assertNotNull(wrapper); Assert.assertNotNull(wrapper.getController()); Assert.assertEquals("expected file modification timestamp to be unchanged", dbLastModified, dbFile.lastModified()); diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java index e33b4ae94b1..24c11cb6839 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java @@ -685,7 +685,7 @@ private void loadForm() { formEntryViewModel.refresh(); } else { Timber.w("Reloading form and restoring state."); - formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory); + formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory, scheduler); showIfNotShowing(FormLoadingDialogFragment.class, getSupportFragmentManager()); formLoaderTask.execute(formPath); } @@ -729,7 +729,7 @@ private void loadFromIntent(Intent intent) { instancePath = loadSavePoint(); } - formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory); + formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory, scheduler); formLoaderTask.setFormLoaderListener(this); showIfNotShowing(FormLoadingDialogFragment.class, getSupportFragmentManager()); formLoaderTask.execute(formPath); @@ -1929,7 +1929,7 @@ protected void onResume() { DialogFragmentUtils.dismissDialog(FormLoadingDialogFragment.class, getSupportFragmentManager()); FormLoaderTask t = formLoaderTask; formLoaderTask = null; - t.cancel(true); + t.cancel(); t.destroy(); // there is no formController -- fire MainMenu activity? Timber.w("Starting MainMenuActivity because formController is null/formLoaderTask not null"); @@ -2005,7 +2005,7 @@ protected void onDestroy() { if (formLoaderTask.getStatus() == AsyncTask.Status.FINISHED) { FormLoaderTask t = formLoaderTask; formLoaderTask = null; - t.cancel(true); + t.cancel(); t.destroy(); } } @@ -2080,7 +2080,7 @@ public void loadingComplete(FormLoaderTask task, FormDef formDef, String warning formLoaderTask.setFormLoaderListener(null); FormLoaderTask t = formLoaderTask; formLoaderTask = null; - t.cancel(true); + t.cancel(); t.destroy(); Collect.getInstance().setExternalDataManager(task.getExternalDataManager()); @@ -2349,7 +2349,7 @@ public void onCancelFormLoading() { formLoaderTask.setFormLoaderListener(null); FormLoaderTask t = formLoaderTask; formLoaderTask = null; - t.cancel(true); + t.cancel(); t.destroy(); } exit(); diff --git a/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java b/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java index 637b1dca94d..ee195f64e94 100644 --- a/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java +++ b/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java @@ -20,7 +20,6 @@ import android.content.Intent; import android.database.Cursor; import android.database.SQLException; -import android.os.AsyncTask; import androidx.annotation.NonNull; @@ -53,6 +52,8 @@ import org.odk.collect.android.utilities.FileUtils; import org.odk.collect.android.utilities.FormDefCache; import org.odk.collect.android.utilities.ZipUtils; +import org.odk.collect.async.Scheduler; +import org.odk.collect.async.SchedulerAsyncTaskMimic; import org.odk.collect.shared.strings.Md5; import java.io.File; @@ -71,7 +72,7 @@ * @author Carl Hartung (carlhartung@gmail.com) * @author Yaw Anokwa (yanokwa@gmail.com) */ -public class FormLoaderTask extends AsyncTask { +public class FormLoaderTask extends SchedulerAsyncTaskMimic { private static final String ITEMSETS_CSV = "itemsets.csv"; private FormLoaderListener stateListener; @@ -88,6 +89,11 @@ public class FormLoaderTask extends AsyncTask Date: Thu, 10 Aug 2023 17:46:18 +0100 Subject: [PATCH 13/35] Use paused scheduler for FormFillingActiivtyTest --- .../activities/FormFillingActivityTest.kt | 37 +++++++++++++++++-- .../odk/collect/testshared/FakeScheduler.kt | 5 +++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 004f8dc6268..203cfc94800 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.app.Application import android.content.ComponentName import android.os.Bundle +import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.NoMatchingViewException @@ -12,22 +13,26 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.work.WorkManager import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.equalTo import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.odk.collect.android.application.Collect import org.odk.collect.android.external.FormsContract import org.odk.collect.android.formmanagement.FormFillingIntentFactory -import org.odk.collect.android.injection.DaggerUtils +import org.odk.collect.android.injection.config.AppDependencyComponent import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.android.utilities.FileUtils +import org.odk.collect.async.Scheduler import org.odk.collect.formstest.FormFixtures.form +import org.odk.collect.testshared.FakeScheduler import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf import java.io.File @@ -35,13 +40,22 @@ import java.io.File @RunWith(AndroidJUnit4::class) class FormFillingActivityTest { + @get:Rule + val instantTaskExecutorRule = InstantTaskExecutorRule() + + private val scheduler = FakeScheduler() + private val dependencies = object : AppDependencyModule() { + override fun providesScheduler(workManager: WorkManager): Scheduler { + return scheduler + } + } + private val application = ApplicationProvider.getApplicationContext() - private val component = DaggerUtils.getComponent(application) - private val dependencies = object : AppDependencyModule() {} + private lateinit var component: AppDependencyComponent @Before fun setup() { - CollectHelpers.overrideAppDependencyModule(dependencies) + component = CollectHelpers.overrideAppDependencyModule(dependencies) } @Test @@ -65,6 +79,8 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + scheduler.flush() + onView(withText("One Question")).check(matches(isDisplayed())) onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) @@ -80,6 +96,8 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) + scheduler.flush() + assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) @@ -88,6 +106,8 @@ class FormFillingActivityTest { // Return to FormFillingActivity from FormHierarchyActivity recreated.recreate().get() .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + scheduler.flush() + onView(withText("One Question")).check(matches(isDisplayed())) onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) } @@ -113,9 +133,12 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + scheduler.flush() + onView(withText("All widgets")).check(matches(isDisplayed())) while (true) { try { + scheduler.flush() onView(withText("Select one from map widget")).check(matches(isDisplayed())) onView(withText("Select place")).perform(click()) break @@ -124,6 +147,8 @@ class FormFillingActivityTest { } } + scheduler.flush() + // Destroy activity with saved instance state val outState = Bundle() initial.saveInstanceState(outState).pause().stop().destroy() @@ -136,6 +161,8 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) + scheduler.flush() + assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) @@ -144,6 +171,8 @@ class FormFillingActivityTest { // Return to FormFillingActivity from FormHierarchyActivity recreated.recreate().get() .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + scheduler.flush() + onView(withText("All widgets")).check(matches(isDisplayed())) onView(withText(containsString("Select one from map widget"))).check(matches(isDisplayed())) } diff --git a/testshared/src/main/java/org/odk/collect/testshared/FakeScheduler.kt b/testshared/src/main/java/org/odk/collect/testshared/FakeScheduler.kt index 1d4ead081bb..c5fdc991a7e 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/FakeScheduler.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/FakeScheduler.kt @@ -78,6 +78,11 @@ class FakeScheduler : Scheduler { } } + fun flush() { + runBackground() + runForeground() + } + fun isRepeatRunning(): Boolean { return repeatTasks.isNotEmpty() } From 6454364759f280c3e2c1f40b928416d6001d96e9 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:03:43 +0100 Subject: [PATCH 14/35] Make tests more specific --- .../activities/FormFillingActivityTest.kt | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 203cfc94800..27b853ae586 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -5,9 +5,9 @@ import android.app.Application import android.content.ComponentName import android.os.Bundle import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed @@ -15,7 +15,6 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.work.WorkManager import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.equalTo import org.junit.Before import org.junit.Rule @@ -30,6 +29,7 @@ import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.android.utilities.FileUtils +import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.async.Scheduler import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.testshared.FakeScheduler @@ -59,13 +59,13 @@ class FormFillingActivityTest { } @Test - fun whenProcessIsKilledAndRestoredDuringFormEntry_returnsToHierarchy() { + fun whenProcessIsKilledAndRestored_returnsToHierarchyAtQuestion() { val projectId = CollectHelpers.setupDemoProject() val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) val formFile = FileUtils.copyFileFromResources( - "forms/one-question.xml", - File(formsDir, "one-question.xml") + "forms/two-question.xml", + File(formsDir, "two-question.xml") ) val formsRepository = component.formsRepositoryProvider().get() @@ -81,8 +81,12 @@ class FormFillingActivityTest { val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("One Question")).check(matches(isDisplayed())) - onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your name?")).check(matches(isDisplayed())) + + onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) + scheduler.flush() + onView(withText("What is your age?")).check(matches(isDisplayed())) // Destroy activity with saved instance state val outState = Bundle() @@ -108,18 +112,18 @@ class FormFillingActivityTest { .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() - onView(withText("One Question")).check(matches(isDisplayed())) - onView(withText(containsString("what is your age"))).check(matches(isDisplayed())) + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your age?")).check(matches(isDisplayed())) } @Test - fun whenProcessIsKilledAndRestoredDuringFormEntry_andThereADialogFragmentOpen_returnsToHierarchy() { + fun whenProcessIsKilledAndRestored_andThereADialogFragmentOpen_doesNotRestoreDialogFragment() { val projectId = CollectHelpers.setupDemoProject() val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) val formFile = FileUtils.copyFileFromResources( - "forms/all-widgets.xml", - File(formsDir, "all-widgets.xml") + "forms/two-question.xml", + File(formsDir, "two-question.xml") ) val formsRepository = component.formsRepositoryProvider().get() @@ -135,19 +139,19 @@ class FormFillingActivityTest { val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("All widgets")).check(matches(isDisplayed())) - while (true) { - try { - scheduler.flush() - onView(withText("Select one from map widget")).check(matches(isDisplayed())) - onView(withText("Select place")).perform(click()) - break - } catch (e: NoMatchingViewException) { - onView(withText("Next")).perform(click()) - } - } + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your name?")).check(matches(isDisplayed())) + onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) scheduler.flush() + onView(withText("What is your age?")).check(matches(isDisplayed())) + + val initialFragmentManager = initial.get().supportFragmentManager + DialogFragmentUtils.showIfNotShowing(TestDialogFragment::class.java, initialFragmentManager) + assertThat( + initialFragmentManager.fragments.any { it::class == TestDialogFragment::class }, + equalTo(true) + ) // Destroy activity with saved instance state val outState = Bundle() @@ -173,7 +177,14 @@ class FormFillingActivityTest { .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() - onView(withText("All widgets")).check(matches(isDisplayed())) - onView(withText(containsString("Select one from map widget"))).check(matches(isDisplayed())) + assertThat( + recreated.get().supportFragmentManager.fragments.any { it::class == TestDialogFragment::class }, + equalTo(false) + ) + + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your age?")).check(matches(isDisplayed())) } } + +class TestDialogFragment : DialogFragment() From fc9629be89ae403e9ad16468e3f090e310e83bcf Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:23:46 +0100 Subject: [PATCH 15/35] Pull out common helpers --- .../activities/FormFillingActivityTest.kt | 76 ++++++------------- .../support/ActivityControllerExtensions.kt | 22 ++++++ .../android/support/CollectHelpers.java | 9 +++ 3 files changed, 55 insertions(+), 52 deletions(-) create mode 100644 collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 27b853ae586..5352a51a442 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -3,7 +3,6 @@ package org.odk.collect.android.activities import android.app.Activity import android.app.Application import android.content.ComponentName -import android.os.Bundle import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider @@ -20,17 +19,19 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.odk.collect.android.application.Collect import org.odk.collect.android.external.FormsContract import org.odk.collect.android.formmanagement.FormFillingIntentFactory import org.odk.collect.android.injection.config.AppDependencyComponent import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory +import org.odk.collect.android.support.ActivityControllerExtensions.recreateWithProcessRestore import org.odk.collect.android.support.CollectHelpers +import org.odk.collect.android.support.CollectHelpers.resetProcess import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.android.utilities.FileUtils import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.async.Scheduler +import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.testshared.FakeScheduler import org.robolectric.Robolectric @@ -62,15 +63,7 @@ class FormFillingActivityTest { fun whenProcessIsKilledAndRestored_returnsToHierarchyAtQuestion() { val projectId = CollectHelpers.setupDemoProject() - val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) - val formFile = FileUtils.copyFileFromResources( - "forms/two-question.xml", - File(formsDir, "two-question.xml") - ) - - val formsRepository = component.formsRepositoryProvider().get() - val form = formsRepository.save(form(formFile = formFile)) - + val form = setupForm("forms/two-question.xml") val intent = FormFillingIntentFactory.newInstanceIntent( application, FormsContract.getUri(projectId, form!!.dbId), @@ -80,7 +73,6 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) onView(withText("What is your name?")).check(matches(isDisplayed())) @@ -88,27 +80,16 @@ class FormFillingActivityTest { scheduler.flush() onView(withText("What is your age?")).check(matches(isDisplayed())) - // Destroy activity with saved instance state - val outState = Bundle() - initial.saveInstanceState(outState).pause().stop().destroy() - - // Reset process - ApplicationProvider.getApplicationContext().getState().clear() - val newComponent = CollectHelpers.overrideAppDependencyModule(dependencies) - newComponent.applicationInitializer().initialize() - // Recreate and assert we start FormHierarchyActivity - val recreated = - Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) + val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) // Return to FormFillingActivity from FormHierarchyActivity - recreated.recreate().get() + recreated.get() .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() @@ -120,15 +101,7 @@ class FormFillingActivityTest { fun whenProcessIsKilledAndRestored_andThereADialogFragmentOpen_doesNotRestoreDialogFragment() { val projectId = CollectHelpers.setupDemoProject() - val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) - val formFile = FileUtils.copyFileFromResources( - "forms/two-question.xml", - File(formsDir, "two-question.xml") - ) - - val formsRepository = component.formsRepositoryProvider().get() - val form = formsRepository.save(form(formFile = formFile)) - + val form = setupForm("forms/two-question.xml") val intent = FormFillingIntentFactory.newInstanceIntent( application, FormsContract.getUri(projectId, form!!.dbId), @@ -138,7 +111,6 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) onView(withText("What is your name?")).check(matches(isDisplayed())) @@ -153,38 +125,38 @@ class FormFillingActivityTest { equalTo(true) ) - // Destroy activity with saved instance state - val outState = Bundle() - initial.saveInstanceState(outState).pause().stop().destroy() - - // Reset process - ApplicationProvider.getApplicationContext().getState().clear() - val newComponent = CollectHelpers.overrideAppDependencyModule(dependencies) - newComponent.applicationInitializer().initialize() - // Recreate and assert we start FormHierarchyActivity - val recreated = - Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup(outState) + val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) // Return to FormFillingActivity from FormHierarchyActivity - recreated.recreate().get() + recreated.get() .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your age?")).check(matches(isDisplayed())) assertThat( recreated.get().supportFragmentManager.fragments.any { it::class == TestDialogFragment::class }, equalTo(false) ) + } - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your age?")).check(matches(isDisplayed())) + private fun setupForm(testFormPath: String): Form? { + val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) + val formFile = FileUtils.copyFileFromResources( + testFormPath, + File(formsDir, "two-question.xml") + ) + + val formsRepository = component.formsRepositoryProvider().get() + val form = formsRepository.save(form(formFile = formFile)) + return form } -} -class TestDialogFragment : DialogFragment() + class TestDialogFragment : DialogFragment() +} diff --git a/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt b/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt new file mode 100644 index 00000000000..6072dbcceb8 --- /dev/null +++ b/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt @@ -0,0 +1,22 @@ +package org.odk.collect.android.support + +import android.app.Activity +import android.os.Bundle +import org.robolectric.Robolectric +import org.robolectric.android.controller.ActivityController + +object ActivityControllerExtensions { + inline fun ActivityController.recreateWithProcessRestore( + resetProcess: () -> Unit + ): ActivityController { + // Destroy activity with saved instance state + val outState = Bundle() + this.saveInstanceState(outState).pause().stop().destroy() + + // Reset process + resetProcess() + + // Recreate with saved instance state + return Robolectric.buildActivity(A::class.java, this.intent).setup(outState) + } +} diff --git a/collect_app/src/test/java/org/odk/collect/android/support/CollectHelpers.java b/collect_app/src/test/java/org/odk/collect/android/support/CollectHelpers.java index d7419315ab3..e115aaeeae9 100644 --- a/collect_app/src/test/java/org/odk/collect/android/support/CollectHelpers.java +++ b/collect_app/src/test/java/org/odk/collect/android/support/CollectHelpers.java @@ -67,6 +67,15 @@ public static AppDependencyComponent overrideAppDependencyModule(AppDependencyMo return testComponent; } + public static void resetProcess(AppDependencyModule dependencies) { + Collect application = ApplicationProvider.getApplicationContext(); + + application.getState().clear(); + + AppDependencyComponent newComponent = CollectHelpers.overrideAppDependencyModule(dependencies); + newComponent.applicationInitializer().initialize(); + } + public static T createThemedActivity(Class clazz) { return RobolectricHelpers.createThemedActivity(clazz); } From 6f0aefe522057e6ccc2e04fcdcaf4786500ff59d Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:31:41 +0100 Subject: [PATCH 16/35] Add test for returning from hierarchy --- .../activities/FormFillingActivityTest.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 5352a51a442..8724e803578 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -10,6 +10,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.work.WorkManager @@ -97,6 +98,51 @@ class FormFillingActivityTest { onView(withText("What is your age?")).check(matches(isDisplayed())) } + @Test + fun whenProcessIsKilledAndRestored_andHierarchyIsOpen_returnsToHierarchyAtQuestion() { + val projectId = CollectHelpers.setupDemoProject() + + val form = setupForm("forms/two-question.xml") + val intent = FormFillingIntentFactory.newInstanceIntent( + application, + FormsContract.getUri(projectId, form!!.dbId), + FormFillingActivity::class + ) + + // Start activity + val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + scheduler.flush() + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your name?")).check(matches(isDisplayed())) + + onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) + scheduler.flush() + onView(withText("What is your age?")).check(matches(isDisplayed())) + + onView(withContentDescription(org.odk.collect.strings.R.string.view_hierarchy)) + .perform(click()) + assertThat( + shadowOf(initial.get()).nextStartedActivity.component, + equalTo(ComponentName(application, FormHierarchyActivity::class.java)) + ) + + // Recreate and assert we start FormHierarchyActivity + val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + scheduler.flush() + assertThat( + shadowOf(initial.get()).nextStartedActivity.component, + equalTo(ComponentName(application, FormHierarchyActivity::class.java)) + ) + + // Return to FormFillingActivity from FormHierarchyActivity + recreated.get() + .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + scheduler.flush() + + onView(withText("Two Question")).check(matches(isDisplayed())) + onView(withText("What is your age?")).check(matches(isDisplayed())) + } + @Test fun whenProcessIsKilledAndRestored_andThereADialogFragmentOpen_doesNotRestoreDialogFragment() { val projectId = CollectHelpers.setupDemoProject() From 33ef83a0f1f77fc9da19c6677e2663cb770af34b Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:38:43 +0100 Subject: [PATCH 17/35] Pull out helpers for Espresso --- .../activities/FormFillingActivityTest.kt | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 8724e803578..d274cc9f1a3 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -34,6 +34,7 @@ import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.async.Scheduler import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form +import org.odk.collect.strings.R import org.odk.collect.testshared.FakeScheduler import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf @@ -74,12 +75,12 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your name?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your name?") - onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) + clickOn(R.string.form_forward) scheduler.flush() - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("What is your age?") // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } @@ -94,8 +95,8 @@ class FormFillingActivityTest { .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your age?") } @Test @@ -112,15 +113,14 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your name?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your name?") - onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) + clickOn(R.string.form_forward) scheduler.flush() - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("What is your age?") - onView(withContentDescription(org.odk.collect.strings.R.string.view_hierarchy)) - .perform(click()) + clickOn(R.string.view_hierarchy) assertThat( shadowOf(initial.get()).nextStartedActivity.component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) @@ -139,8 +139,8 @@ class FormFillingActivityTest { .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your age?") } @Test @@ -157,12 +157,12 @@ class FormFillingActivityTest { // Start activity val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your name?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your name?") - onView(withText(org.odk.collect.strings.R.string.form_forward)).perform(click()) + clickOn(R.string.form_forward) scheduler.flush() - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("What is your age?") val initialFragmentManager = initial.get().supportFragmentManager DialogFragmentUtils.showIfNotShowing(TestDialogFragment::class.java, initialFragmentManager) @@ -184,14 +184,22 @@ class FormFillingActivityTest { .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) scheduler.flush() - onView(withText("Two Question")).check(matches(isDisplayed())) - onView(withText("What is your age?")).check(matches(isDisplayed())) + assertText("Two Question") + assertText("What is your age?") assertThat( recreated.get().supportFragmentManager.fragments.any { it::class == TestDialogFragment::class }, equalTo(false) ) } + private fun assertText(text: String) { + onView(withText(text)).check(matches(isDisplayed())) + } + + private fun clickOn(string: Int) { + onView(withContentDescription(string)).perform(click()) + } + private fun setupForm(testFormPath: String): Form? { val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) val formFile = FileUtils.copyFileFromResources( From 3e2b7733034c7b02e77e5b0ba2b45a39e15ea421 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:41:19 +0100 Subject: [PATCH 18/35] Remove shadows from FormFillingAcitivity --- .../android/activities/FormFillingActivityTest.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index d274cc9f1a3..06791c4ee45 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -9,6 +9,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withText @@ -31,13 +32,13 @@ import org.odk.collect.android.support.CollectHelpers.resetProcess import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.android.utilities.FileUtils import org.odk.collect.androidshared.ui.DialogFragmentUtils +import org.odk.collect.androidtest.RecordedIntentsRule import org.odk.collect.async.Scheduler import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R import org.odk.collect.testshared.FakeScheduler import org.robolectric.Robolectric -import org.robolectric.Shadows.shadowOf import java.io.File @RunWith(AndroidJUnit4::class) @@ -46,6 +47,9 @@ class FormFillingActivityTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() + @get:Rule + val recordedIntentsRule = RecordedIntentsRule() + private val scheduler = FakeScheduler() private val dependencies = object : AppDependencyModule() { override fun providesScheduler(workManager: WorkManager): Scheduler { @@ -86,7 +90,7 @@ class FormFillingActivityTest { val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() assertThat( - shadowOf(initial.get()).nextStartedActivity.component, + Intents.getIntents()[0].component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) @@ -122,7 +126,7 @@ class FormFillingActivityTest { clickOn(R.string.view_hierarchy) assertThat( - shadowOf(initial.get()).nextStartedActivity.component, + Intents.getIntents()[0].component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) @@ -130,7 +134,7 @@ class FormFillingActivityTest { val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() assertThat( - shadowOf(initial.get()).nextStartedActivity.component, + Intents.getIntents()[0].component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) @@ -175,7 +179,7 @@ class FormFillingActivityTest { val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() assertThat( - shadowOf(initial.get()).nextStartedActivity.component, + Intents.getIntents()[0].component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) ) From a5ddbf81da0ea066c8ace09d570d6ef9013057c6 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:45:17 +0100 Subject: [PATCH 19/35] Move extension to shared module --- .../activities/FormFillingActivityTest.kt | 2 +- .../support/ActivityControllerExtensions.kt | 22 ------------------- .../collect/testshared/RobolectricHelpers.kt | 17 ++++++++++++++ 3 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 06791c4ee45..ea34f50bb7d 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -26,7 +26,6 @@ import org.odk.collect.android.formmanagement.FormFillingIntentFactory import org.odk.collect.android.injection.config.AppDependencyComponent import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory -import org.odk.collect.android.support.ActivityControllerExtensions.recreateWithProcessRestore import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.CollectHelpers.resetProcess import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes @@ -38,6 +37,7 @@ import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R import org.odk.collect.testshared.FakeScheduler +import org.odk.collect.testshared.RobolectricHelpers.recreateWithProcessRestore import org.robolectric.Robolectric import java.io.File diff --git a/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt b/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt deleted file mode 100644 index 6072dbcceb8..00000000000 --- a/collect_app/src/test/java/org/odk/collect/android/support/ActivityControllerExtensions.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.odk.collect.android.support - -import android.app.Activity -import android.os.Bundle -import org.robolectric.Robolectric -import org.robolectric.android.controller.ActivityController - -object ActivityControllerExtensions { - inline fun ActivityController.recreateWithProcessRestore( - resetProcess: () -> Unit - ): ActivityController { - // Destroy activity with saved instance state - val outState = Bundle() - this.saveInstanceState(outState).pause().stop().destroy() - - // Reset process - resetProcess() - - // Recreate with saved instance state - return Robolectric.buildActivity(A::class.java, this.intent).setup(outState) - } -} diff --git a/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt index 1368e4bbc6f..e295d4a9817 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt @@ -1,9 +1,11 @@ package org.odk.collect.testshared +import android.app.Activity import android.app.Application import android.app.Service import android.graphics.drawable.Drawable import android.media.MediaMetadataRetriever +import android.os.Bundle import android.os.Environment import android.os.Looper import android.view.ViewGroup @@ -17,6 +19,7 @@ import org.odk.collect.servicetest.ServiceScenario import org.odk.collect.servicetest.ServiceScenario.Companion.launch import org.robolectric.Robolectric import org.robolectric.Shadows +import org.robolectric.android.controller.ActivityController import org.robolectric.shadows.ShadowEnvironment import org.robolectric.shadows.ShadowMediaMetadataRetriever import org.robolectric.shadows.ShadowMediaPlayer @@ -132,4 +135,18 @@ object RobolectricHelpers { } } } + + inline fun ActivityController.recreateWithProcessRestore( + resetProcess: () -> Unit + ): ActivityController { + // Destroy activity with saved instance state + val outState = Bundle() + this.saveInstanceState(outState).pause().stop().destroy() + + // Reset process + resetProcess() + + // Recreate with saved instance state + return Robolectric.buildActivity(A::class.java, this.intent).setup(outState) + } } From 3351c2c22b5dc214d6e13a9999d66f4a5c6c6313 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 14:54:34 +0100 Subject: [PATCH 20/35] Pull out shared Espresso helpers --- .../odk/collect/android/support/pages/Page.kt | 11 +++++---- .../activities/FormFillingActivityTest.kt | 24 +++++-------------- .../odk/collect/testshared/EspressoHelpers.kt | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt index 0b292629076..5cb1ac89e4a 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt @@ -44,6 +44,7 @@ import org.odk.collect.android.support.actions.RotateAction import org.odk.collect.android.support.matchers.CustomMatchers.withIndex import org.odk.collect.androidshared.ui.ToastUtils.popRecordedToasts import org.odk.collect.strings.localization.getLocalizedString +import org.odk.collect.testshared.EspressoHelpers import org.odk.collect.testshared.RecyclerViewMatcher import timber.log.Timber import java.io.File @@ -86,7 +87,7 @@ abstract class Page> { return destination.assertOnPage() } - fun assertTexts(vararg texts: String?): T { + fun assertTexts(vararg texts: String): T { closeSoftKeyboard() for (text in texts) { assertText(text) @@ -99,8 +100,8 @@ abstract class Page> { return this as T } - fun assertText(text: String?): T { - onView(allOf(withText(text), withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))).check(matches(not(doesNotExist()))) + fun assertText(text: String): T { + EspressoHelpers.assertText(text) return this as T } @@ -380,7 +381,7 @@ abstract class Page> { wait250ms() // https://github.com/android/android-test/issues/444 } - protected fun waitForText(text: String?) { + protected fun waitForText(text: String) { waitFor { assertText(text) } } @@ -403,7 +404,7 @@ abstract class Page> { } fun clickOnContentDescription(string: Int): T { - onView(withContentDescription(string)).perform(click()) + EspressoHelpers.clickOnContentDescription(string) return this as T } diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index ea34f50bb7d..966bcd17a91 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -6,13 +6,7 @@ import android.content.ComponentName import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withContentDescription -import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.work.WorkManager import org.hamcrest.MatcherAssert.assertThat @@ -36,6 +30,8 @@ import org.odk.collect.async.Scheduler import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R +import org.odk.collect.testshared.EspressoHelpers.assertText +import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler import org.odk.collect.testshared.RobolectricHelpers.recreateWithProcessRestore import org.robolectric.Robolectric @@ -82,7 +78,7 @@ class FormFillingActivityTest { assertText("Two Question") assertText("What is your name?") - clickOn(R.string.form_forward) + clickOnContentDescription(R.string.form_forward) scheduler.flush() assertText("What is your age?") @@ -120,11 +116,11 @@ class FormFillingActivityTest { assertText("Two Question") assertText("What is your name?") - clickOn(R.string.form_forward) + clickOnContentDescription(R.string.form_forward) scheduler.flush() assertText("What is your age?") - clickOn(R.string.view_hierarchy) + clickOnContentDescription(R.string.view_hierarchy) assertThat( Intents.getIntents()[0].component, equalTo(ComponentName(application, FormHierarchyActivity::class.java)) @@ -164,7 +160,7 @@ class FormFillingActivityTest { assertText("Two Question") assertText("What is your name?") - clickOn(R.string.form_forward) + clickOnContentDescription(R.string.form_forward) scheduler.flush() assertText("What is your age?") @@ -196,14 +192,6 @@ class FormFillingActivityTest { ) } - private fun assertText(text: String) { - onView(withText(text)).check(matches(isDisplayed())) - } - - private fun clickOn(string: Int) { - onView(withContentDescription(string)).perform(click()) - } - private fun setupForm(testFormPath: String): Form? { val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) val formFile = FileUtils.copyFileFromResources( diff --git a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt new file mode 100644 index 00000000000..977d50041e0 --- /dev/null +++ b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt @@ -0,0 +1,24 @@ +package org.odk.collect.testshared + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility +import androidx.test.espresso.matcher.ViewMatchers.withText +import org.hamcrest.CoreMatchers.not +import org.hamcrest.Matchers.allOf + +object EspressoHelpers { + + fun assertText(text: String) { + onView(allOf(withText(text), withEffectiveVisibility(VISIBLE))) + .check(matches(not(doesNotExist()))) + } + + fun clickOnContentDescription(string: Int) { + onView(withContentDescription(string)).perform(click()) + } +} From 116ef1cc900a337acfb2367a337e8a6a77be39ec Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 15:02:23 +0100 Subject: [PATCH 21/35] Add Espresso helper for asserting on intents --- .../activities/FormFillingActivityTest.kt | 28 ++++--------------- .../odk/collect/testshared/EspressoHelpers.kt | 8 ++++++ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 966bcd17a91..76158283741 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -2,11 +2,9 @@ package org.odk.collect.android.activities import android.app.Activity import android.app.Application -import android.content.ComponentName import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider -import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.work.WorkManager import org.hamcrest.MatcherAssert.assertThat @@ -30,6 +28,7 @@ import org.odk.collect.async.Scheduler import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R +import org.odk.collect.testshared.EspressoHelpers.assertLastIntent import org.odk.collect.testshared.EspressoHelpers.assertText import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler @@ -85,10 +84,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertThat( - Intents.getIntents()[0].component, - equalTo(ComponentName(application, FormHierarchyActivity::class.java)) - ) + assertLastIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -121,18 +117,12 @@ class FormFillingActivityTest { assertText("What is your age?") clickOnContentDescription(R.string.view_hierarchy) - assertThat( - Intents.getIntents()[0].component, - equalTo(ComponentName(application, FormHierarchyActivity::class.java)) - ) + assertLastIntent(FormHierarchyActivity::class) // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertThat( - Intents.getIntents()[0].component, - equalTo(ComponentName(application, FormHierarchyActivity::class.java)) - ) + assertLastIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -174,10 +164,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertThat( - Intents.getIntents()[0].component, - equalTo(ComponentName(application, FormHierarchyActivity::class.java)) - ) + assertLastIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -186,10 +173,7 @@ class FormFillingActivityTest { assertText("Two Question") assertText("What is your age?") - assertThat( - recreated.get().supportFragmentManager.fragments.any { it::class == TestDialogFragment::class }, - equalTo(false) - ) + assertLastIntent(FormHierarchyActivity::class) } private fun setupForm(testFormPath: String): Form? { diff --git a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt index 977d50041e0..a1b92e0930b 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt @@ -4,12 +4,16 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withText import org.hamcrest.CoreMatchers.not +import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf +import kotlin.reflect.KClass object EspressoHelpers { @@ -21,4 +25,8 @@ object EspressoHelpers { fun clickOnContentDescription(string: Int) { onView(withContentDescription(string)).perform(click()) } + + fun assertLastIntent(activityClass: KClass<*>) { + assertThat(Intents.getIntents()[0], hasComponent(activityClass.java.name)) + } } From 6f584b2a435ffe15792c24c9632c543bcfd00f77 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 15:05:05 +0100 Subject: [PATCH 22/35] Remove unused assertion --- .../odk/collect/android/activities/FormFillingActivityTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 76158283741..77b28425288 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -173,7 +173,6 @@ class FormFillingActivityTest { assertText("Two Question") assertText("What is your age?") - assertLastIntent(FormHierarchyActivity::class) } private fun setupForm(testFormPath: String): Form? { From 8b7f249a0ca761b230bcdb118781ef7436102a4a Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 15:16:50 +0100 Subject: [PATCH 23/35] Improve intent assertion --- .../android/activities/FormFillingActivityTest.kt | 10 +++++----- .../java/org/odk/collect/testshared/EspressoHelpers.kt | 10 ++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 77b28425288..2eca74c18fb 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -28,7 +28,7 @@ import org.odk.collect.async.Scheduler import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R -import org.odk.collect.testshared.EspressoHelpers.assertLastIntent +import org.odk.collect.testshared.EspressoHelpers.assertIntents import org.odk.collect.testshared.EspressoHelpers.assertText import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler @@ -84,7 +84,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertLastIntent(FormHierarchyActivity::class) + assertIntents(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -117,12 +117,12 @@ class FormFillingActivityTest { assertText("What is your age?") clickOnContentDescription(R.string.view_hierarchy) - assertLastIntent(FormHierarchyActivity::class) + assertIntents(FormHierarchyActivity::class) // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertLastIntent(FormHierarchyActivity::class) + assertIntents(FormHierarchyActivity::class, FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -164,7 +164,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertLastIntent(FormHierarchyActivity::class) + assertIntents(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() diff --git a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt index a1b92e0930b..8a123fb90ac 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt @@ -13,6 +13,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import org.hamcrest.CoreMatchers.not import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf +import org.hamcrest.Matchers.equalTo import kotlin.reflect.KClass object EspressoHelpers { @@ -26,7 +27,12 @@ object EspressoHelpers { onView(withContentDescription(string)).perform(click()) } - fun assertLastIntent(activityClass: KClass<*>) { - assertThat(Intents.getIntents()[0], hasComponent(activityClass.java.name)) + fun assertIntents(vararg activityClasses: KClass<*>) { + val intents = Intents.getIntents() + assertThat(activityClasses.size, equalTo(intents.size)) + + activityClasses.forEachIndexed { index, kClass: KClass<*> -> + assertThat(intents[index], hasComponent(kClass.java.name)) + } } } From 4444a92cb10b785b1073411dee1d2470a7189bd0 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 11 Aug 2023 15:42:42 +0100 Subject: [PATCH 24/35] Add test for restoring and returning data --- .../formentry/questions/WidgetViewUtils.java | 1 + .../activities/FormFillingActivityTest.kt | 40 +++++++++++++++++++ .../resources/forms/two-question-external.xml | 24 +++++++++++ .../odk/collect/testshared/EspressoHelpers.kt | 13 ++++-- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test-forms/src/main/resources/forms/two-question-external.xml diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/questions/WidgetViewUtils.java b/collect_app/src/main/java/org/odk/collect/android/formentry/questions/WidgetViewUtils.java index 36f2c9f2d11..d2f113e7ae7 100644 --- a/collect_app/src/main/java/org/odk/collect/android/formentry/questions/WidgetViewUtils.java +++ b/collect_app/src/main/java/org/odk/collect/android/formentry/questions/WidgetViewUtils.java @@ -81,6 +81,7 @@ public static Button createSimpleButton(Context context, @IdRes final int withId } else { button.setId(withId); button.setText(text); + button.setContentDescription(text); button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, answerFontSize); TableLayout.LayoutParams params = new TableLayout.LayoutParams(); diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 2eca74c18fb..8e08baaec4b 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -5,6 +5,7 @@ import android.app.Application import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.work.WorkManager import org.hamcrest.MatcherAssert.assertThat @@ -25,6 +26,7 @@ import org.odk.collect.android.utilities.FileUtils import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.androidtest.RecordedIntentsRule import org.odk.collect.async.Scheduler +import org.odk.collect.externalapp.ExternalAppUtils import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R @@ -175,6 +177,44 @@ class FormFillingActivityTest { assertText("What is your age?") } + @Test + fun whenProcessIsKilledAndRestored_andIsWaitingForExternalData_dataCanStillBeReturned() { + val projectId = CollectHelpers.setupDemoProject() + + val form = setupForm("forms/two-question-external.xml") + val intent = FormFillingIntentFactory.newInstanceIntent( + application, + FormsContract.getUri(projectId, form!!.dbId), + FormFillingActivity::class + ) + + // Start activity + val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + scheduler.flush() + assertText("Two Question") + assertText("What is your name?") + + clickOnContentDescription(R.string.form_forward) + scheduler.flush() + assertText("What is your age?") + + // Open external app + clickOnContentDescription(R.string.launch_app) + assertIntents(hasAction("com.example.EXAMPLE")) + + // Recreate with returned result and assert we don't start any other Activity + val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + val returnData = ExternalAppUtils.getReturnIntent("159") + recreated.get() + .onActivityResult(RequestCodes.EX_STRING_CAPTURE, Activity.RESULT_OK, returnData) + scheduler.flush() + assertIntents(hasAction("com.example.EXAMPLE")) + + assertText("Two Question") + assertText("What is your age?") + assertText("159") + } + private fun setupForm(testFormPath: String): Form? { val formsDir = component.storagePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) val formFile = FileUtils.copyFileFromResources( diff --git a/test-forms/src/main/resources/forms/two-question-external.xml b/test-forms/src/main/resources/forms/two-question-external.xml new file mode 100644 index 00000000000..9e57d3636a0 --- /dev/null +++ b/test-forms/src/main/resources/forms/two-question-external.xml @@ -0,0 +1,24 @@ + + + + Two Question + + + + + + + + + + + + + + + + + + + + diff --git a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt index 8a123fb90ac..9a927a7a29f 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt @@ -1,5 +1,6 @@ package org.odk.collect.testshared +import android.content.Intent import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist @@ -11,6 +12,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withText import org.hamcrest.CoreMatchers.not +import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.equalTo @@ -28,11 +30,16 @@ object EspressoHelpers { } fun assertIntents(vararg activityClasses: KClass<*>) { + val matchers = activityClasses.map { hasComponent(it.java.name) } + assertIntents(*matchers.toTypedArray()) + } + + fun assertIntents(vararg matchers: Matcher) { val intents = Intents.getIntents() - assertThat(activityClasses.size, equalTo(intents.size)) + assertThat(matchers.size, equalTo(intents.size)) - activityClasses.forEachIndexed { index, kClass: KClass<*> -> - assertThat(intents[index], hasComponent(kClass.java.name)) + matchers.forEachIndexed { index, matcher -> + assertThat(intents[index], matcher) } } } From 84da432125ff0da1f4f595be6b8cf2f8acc9cc9c Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 14 Aug 2023 10:08:07 +0100 Subject: [PATCH 25/35] Correct lifecycle for process restore from external app --- .../activities/FormFillingActivityTest.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 8e08baaec4b..5b51e396ef9 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -2,6 +2,7 @@ package org.odk.collect.android.activities import android.app.Activity import android.app.Application +import android.os.Bundle import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider @@ -202,14 +203,30 @@ class FormFillingActivityTest { clickOnContentDescription(R.string.launch_app) assertIntents(hasAction("com.example.EXAMPLE")) - // Recreate with returned result and assert we don't start any other Activity - val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + // Destroy activity with saved instance state + val outState = Bundle() + initial.saveInstanceState(outState).pause().stop().destroy() + + resetProcess(dependencies) + + // Recreate with saved instance state + val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, initial.intent).create(outState) + .start() + .restoreInstanceState(outState) + .postCreate(outState) + + // Return result (this happens before resume when restoring from an external app) val returnData = ExternalAppUtils.getReturnIntent("159") recreated.get() .onActivityResult(RequestCodes.EX_STRING_CAPTURE, Activity.RESULT_OK, returnData) + + // Resume activity + recreated.resume() + .visible() + .topActivityResumed(true) scheduler.flush() - assertIntents(hasAction("com.example.EXAMPLE")) + assertIntents(hasAction("com.example.EXAMPLE")) assertText("Two Question") assertText("What is your age?") assertText("159") From 97f2a68f39374e55ab2b9f965c6ff82db595dfa3 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 14 Aug 2023 10:19:57 +0100 Subject: [PATCH 26/35] Ad helper for asserting on new intents --- .../activities/FormFillingActivityTest.kt | 22 +++++++++------- .../collect/testshared/AssertIntentsHelper.kt | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 testshared/src/main/java/org/odk/collect/testshared/AssertIntentsHelper.kt diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 5b51e396ef9..2c0de36e56a 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -31,7 +31,7 @@ import org.odk.collect.externalapp.ExternalAppUtils import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R -import org.odk.collect.testshared.EspressoHelpers.assertIntents +import org.odk.collect.testshared.AssertIntentsHelper import org.odk.collect.testshared.EspressoHelpers.assertText import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler @@ -48,6 +48,8 @@ class FormFillingActivityTest { @get:Rule val recordedIntentsRule = RecordedIntentsRule() + private val assertIntentsHelper = AssertIntentsHelper() + private val scheduler = FakeScheduler() private val dependencies = object : AppDependencyModule() { override fun providesScheduler(workManager: WorkManager): Scheduler { @@ -87,7 +89,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertIntents(FormHierarchyActivity::class) + assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -120,12 +122,12 @@ class FormFillingActivityTest { assertText("What is your age?") clickOnContentDescription(R.string.view_hierarchy) - assertIntents(FormHierarchyActivity::class) + assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertIntents(FormHierarchyActivity::class, FormHierarchyActivity::class) + assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -167,7 +169,7 @@ class FormFillingActivityTest { // Recreate and assert we start FormHierarchyActivity val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } scheduler.flush() - assertIntents(FormHierarchyActivity::class) + assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity recreated.get() @@ -201,7 +203,7 @@ class FormFillingActivityTest { // Open external app clickOnContentDescription(R.string.launch_app) - assertIntents(hasAction("com.example.EXAMPLE")) + assertIntentsHelper.assertNewIntent(hasAction("com.example.EXAMPLE")) // Destroy activity with saved instance state val outState = Bundle() @@ -211,9 +213,9 @@ class FormFillingActivityTest { // Recreate with saved instance state val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, initial.intent).create(outState) - .start() - .restoreInstanceState(outState) - .postCreate(outState) + .start() + .restoreInstanceState(outState) + .postCreate(outState) // Return result (this happens before resume when restoring from an external app) val returnData = ExternalAppUtils.getReturnIntent("159") @@ -226,7 +228,7 @@ class FormFillingActivityTest { .topActivityResumed(true) scheduler.flush() - assertIntents(hasAction("com.example.EXAMPLE")) + assertIntentsHelper.assertNoNewIntent() assertText("Two Question") assertText("What is your age?") assertText("159") diff --git a/testshared/src/main/java/org/odk/collect/testshared/AssertIntentsHelper.kt b/testshared/src/main/java/org/odk/collect/testshared/AssertIntentsHelper.kt new file mode 100644 index 00000000000..69b2043c02d --- /dev/null +++ b/testshared/src/main/java/org/odk/collect/testshared/AssertIntentsHelper.kt @@ -0,0 +1,26 @@ +package org.odk.collect.testshared + +import android.content.Intent +import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent +import org.hamcrest.Matcher +import org.odk.collect.testshared.EspressoHelpers.assertIntents +import kotlin.reflect.KClass + +class AssertIntentsHelper { + + private val matchers = mutableListOf>() + + fun assertNewIntent(matcher: Matcher) { + matchers.add(matcher) + assertIntents(*matchers.toTypedArray()) + } + + fun assertNewIntent(activityClass: KClass<*>) { + matchers.add(hasComponent(activityClass.java.name)) + assertIntents(*matchers.toTypedArray()) + } + + fun assertNoNewIntent() { + assertIntents(*matchers.toTypedArray()) + } +} From 9d6da633473347b1cc1bd504f2c6524e78f3f874 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 14 Aug 2023 11:06:28 +0100 Subject: [PATCH 27/35] Add helper for restoring with result --- .../activities/FormFillingActivityTest.kt | 24 ++---------- .../collect/testshared/RobolectricHelpers.kt | 38 +++++++++++++++++-- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 2c0de36e56a..8b032c2edf0 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -1,8 +1,8 @@ package org.odk.collect.android.activities import android.app.Activity +import android.app.Activity.RESULT_OK import android.app.Application -import android.os.Bundle import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.fragment.app.DialogFragment import androidx.test.core.app.ApplicationProvider @@ -205,27 +205,9 @@ class FormFillingActivityTest { clickOnContentDescription(R.string.launch_app) assertIntentsHelper.assertNewIntent(hasAction("com.example.EXAMPLE")) - // Destroy activity with saved instance state - val outState = Bundle() - initial.saveInstanceState(outState).pause().stop().destroy() - - resetProcess(dependencies) - - // Recreate with saved instance state - val recreated = Robolectric.buildActivity(FormFillingActivity::class.java, initial.intent).create(outState) - .start() - .restoreInstanceState(outState) - .postCreate(outState) - - // Return result (this happens before resume when restoring from an external app) + // Recreate with result val returnData = ExternalAppUtils.getReturnIntent("159") - recreated.get() - .onActivityResult(RequestCodes.EX_STRING_CAPTURE, Activity.RESULT_OK, returnData) - - // Resume activity - recreated.resume() - .visible() - .topActivityResumed(true) + initial.recreateWithProcessRestore(RESULT_OK, returnData) { resetProcess(dependencies) } scheduler.flush() assertIntentsHelper.assertNoNewIntent() diff --git a/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt index e295d4a9817..37ab5650fcb 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/RobolectricHelpers.kt @@ -3,6 +3,7 @@ package org.odk.collect.testshared import android.app.Activity import android.app.Application import android.app.Service +import android.content.Intent import android.graphics.drawable.Drawable import android.media.MediaMetadataRetriever import android.os.Bundle @@ -19,6 +20,7 @@ import org.odk.collect.servicetest.ServiceScenario import org.odk.collect.servicetest.ServiceScenario.Companion.launch import org.robolectric.Robolectric import org.robolectric.Shadows +import org.robolectric.Shadows.shadowOf import org.robolectric.android.controller.ActivityController import org.robolectric.shadows.ShadowEnvironment import org.robolectric.shadows.ShadowMediaMetadataRetriever @@ -50,7 +52,11 @@ object RobolectricHelpers { @JvmOverloads fun setupMediaPlayerDataSource(testFile: String, duration: Int = 322450): DataSource { val dataSource = DataSource.toDataSource(testFile) - ShadowMediaMetadataRetriever.addMetadata(dataSource, MediaMetadataRetriever.METADATA_KEY_DURATION, duration.toString()) + ShadowMediaMetadataRetriever.addMetadata( + dataSource, + MediaMetadataRetriever.METADATA_KEY_DURATION, + duration.toString() + ) ShadowMediaPlayer.addMediaInfo(dataSource, MediaInfo(duration, 0)) return dataSource } @@ -76,7 +82,10 @@ object RobolectricHelpers { @JvmStatic @Suppress("UNCHECKED_CAST") - fun getFragmentByClass(fragmentManager: FragmentManager, fragmentClass: Class): F? { + fun getFragmentByClass( + fragmentManager: FragmentManager, + fragmentClass: Class + ): F? { val fragments = fragmentManager.fragments for (fragment in fragments) { if (fragment.javaClass.isAssignableFrom(fragmentClass)) { @@ -111,7 +120,8 @@ object RobolectricHelpers { if (services.containsKey(serviceClass)) { services[serviceClass]!!.startWithNewIntent(intent) } else { - val serviceController: ServiceScenario<*> = launch(serviceClass as Class, intent) + val serviceController: ServiceScenario<*> = + launch(serviceClass as Class, intent) services[serviceClass] = serviceController } } else { @@ -137,6 +147,8 @@ object RobolectricHelpers { } inline fun ActivityController.recreateWithProcessRestore( + resultCode: Int? = null, + result: Intent? = null, resetProcess: () -> Unit ): ActivityController { // Destroy activity with saved instance state @@ -147,6 +159,24 @@ object RobolectricHelpers { resetProcess() // Recreate with saved instance state - return Robolectric.buildActivity(A::class.java, this.intent).setup(outState) + val recreated = Robolectric.buildActivity(A::class.java, this.intent).create(outState) + .start() + .restoreInstanceState(outState) + .postCreate(outState) + + // Return result + if (resultCode != null) { + val startedActivityForResult = shadowOf(this.get()).nextStartedActivityForResult + shadowOf(recreated.get()).receiveResult( + startedActivityForResult.intent, + resultCode, + result + ) + } + + // Resume activity + return recreated.resume() + .visible() + .topActivityResumed(true) } } From 60060296411de0f81c5a2490fd0e7fb29e147511 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Mon, 14 Aug 2023 11:23:37 +0100 Subject: [PATCH 28/35] Use receiveResult instead of protected method --- .../android/activities/FormFillingActivityTest.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 8b032c2edf0..85b27bd8607 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -22,7 +22,6 @@ import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.CollectHelpers.resetProcess -import org.odk.collect.android.utilities.ApplicationConstants.RequestCodes import org.odk.collect.android.utilities.FileUtils import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.androidtest.RecordedIntentsRule @@ -37,6 +36,7 @@ import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler import org.odk.collect.testshared.RobolectricHelpers.recreateWithProcessRestore import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf import java.io.File @RunWith(AndroidJUnit4::class) @@ -92,8 +92,8 @@ class FormFillingActivityTest { assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity - recreated.get() - .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + val hierarchyIntent = shadowOf(recreated.get()).nextStartedActivityForResult.intent + shadowOf(recreated.get()).receiveResult(hierarchyIntent, Activity.RESULT_CANCELED, null) scheduler.flush() assertText("Two Question") @@ -130,8 +130,8 @@ class FormFillingActivityTest { assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity - recreated.get() - .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + val hierarchyIntent = shadowOf(recreated.get()).nextStartedActivityForResult.intent + shadowOf(recreated.get()).receiveResult(hierarchyIntent, Activity.RESULT_CANCELED, null) scheduler.flush() assertText("Two Question") @@ -172,8 +172,8 @@ class FormFillingActivityTest { assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Return to FormFillingActivity from FormHierarchyActivity - recreated.get() - .onActivityResult(RequestCodes.HIERARCHY_ACTIVITY, Activity.RESULT_CANCELED, null) + val hierarchyIntent = shadowOf(recreated.get()).nextStartedActivityForResult.intent + shadowOf(recreated.get()).receiveResult(hierarchyIntent, Activity.RESULT_CANCELED, null) scheduler.flush() assertText("Two Question") From 385ebafd378db62de97d8b0175f531c2b5bdbbec Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Tue, 15 Aug 2023 11:04:02 +0100 Subject: [PATCH 29/35] Avoid ActivityController mem leaks --- .../activities/FormFillingActivityTest.kt | 33 ++++++++++++++----- .../testshared/ActivityControllerRule.kt | 29 ++++++++++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 testshared/src/main/java/org/odk/collect/testshared/ActivityControllerRule.kt diff --git a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt index 85b27bd8607..20def43c8d4 100644 --- a/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/activities/FormFillingActivityTest.kt @@ -30,12 +30,12 @@ import org.odk.collect.externalapp.ExternalAppUtils import org.odk.collect.forms.Form import org.odk.collect.formstest.FormFixtures.form import org.odk.collect.strings.R +import org.odk.collect.testshared.ActivityControllerRule import org.odk.collect.testshared.AssertIntentsHelper import org.odk.collect.testshared.EspressoHelpers.assertText import org.odk.collect.testshared.EspressoHelpers.clickOnContentDescription import org.odk.collect.testshared.FakeScheduler import org.odk.collect.testshared.RobolectricHelpers.recreateWithProcessRestore -import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf import java.io.File @@ -48,6 +48,9 @@ class FormFillingActivityTest { @get:Rule val recordedIntentsRule = RecordedIntentsRule() + @get:Rule + val activityControllerRule = ActivityControllerRule() + private val assertIntentsHelper = AssertIntentsHelper() private val scheduler = FakeScheduler() @@ -77,7 +80,7 @@ class FormFillingActivityTest { ) // Start activity - val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + val initial = activityControllerRule.build(FormFillingActivity::class.java, intent).setup() scheduler.flush() assertText("Two Question") assertText("What is your name?") @@ -87,7 +90,10 @@ class FormFillingActivityTest { assertText("What is your age?") // Recreate and assert we start FormHierarchyActivity - val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + val recreated = activityControllerRule.add { + initial.recreateWithProcessRestore { resetProcess(dependencies) } + } + scheduler.flush() assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) @@ -112,7 +118,7 @@ class FormFillingActivityTest { ) // Start activity - val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + val initial = activityControllerRule.build(FormFillingActivity::class.java, intent).setup() scheduler.flush() assertText("Two Question") assertText("What is your name?") @@ -125,7 +131,10 @@ class FormFillingActivityTest { assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) // Recreate and assert we start FormHierarchyActivity - val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + val recreated = activityControllerRule.add { + initial.recreateWithProcessRestore { resetProcess(dependencies) } + } + scheduler.flush() assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) @@ -150,7 +159,7 @@ class FormFillingActivityTest { ) // Start activity - val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + val initial = activityControllerRule.build(FormFillingActivity::class.java, intent).setup() scheduler.flush() assertText("Two Question") assertText("What is your name?") @@ -167,7 +176,10 @@ class FormFillingActivityTest { ) // Recreate and assert we start FormHierarchyActivity - val recreated = initial.recreateWithProcessRestore { resetProcess(dependencies) } + val recreated = activityControllerRule.add { + initial.recreateWithProcessRestore { resetProcess(dependencies) } + } + scheduler.flush() assertIntentsHelper.assertNewIntent(FormHierarchyActivity::class) @@ -192,7 +204,7 @@ class FormFillingActivityTest { ) // Start activity - val initial = Robolectric.buildActivity(FormFillingActivity::class.java, intent).setup() + val initial = activityControllerRule.build(FormFillingActivity::class.java, intent).setup() scheduler.flush() assertText("Two Question") assertText("What is your name?") @@ -207,7 +219,10 @@ class FormFillingActivityTest { // Recreate with result val returnData = ExternalAppUtils.getReturnIntent("159") - initial.recreateWithProcessRestore(RESULT_OK, returnData) { resetProcess(dependencies) } + activityControllerRule.add { + initial.recreateWithProcessRestore(RESULT_OK, returnData) { resetProcess(dependencies) } + } + scheduler.flush() assertIntentsHelper.assertNoNewIntent() diff --git a/testshared/src/main/java/org/odk/collect/testshared/ActivityControllerRule.kt b/testshared/src/main/java/org/odk/collect/testshared/ActivityControllerRule.kt new file mode 100644 index 00000000000..9cd8b975a55 --- /dev/null +++ b/testshared/src/main/java/org/odk/collect/testshared/ActivityControllerRule.kt @@ -0,0 +1,29 @@ +package org.odk.collect.testshared + +import android.app.Activity +import android.content.Intent +import org.junit.rules.ExternalResource +import org.robolectric.Robolectric +import org.robolectric.android.controller.ActivityController + +class ActivityControllerRule : ExternalResource() { + + private val controllers = mutableListOf>() + + override fun after() { + controllers.forEach { it.close() } + controllers.clear() + } + + fun build(activityClass: Class, intent: Intent): ActivityController { + return Robolectric.buildActivity(activityClass, intent).also { + controllers.add(it) + } + } + + fun add(supplier: () -> ActivityController): ActivityController { + return supplier().also { + controllers.add(it) + } + } +} From b8b6808ff1c6cc8465d3580feb6942a29d048e8a Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 16:37:36 +0100 Subject: [PATCH 30/35] Fix java version --- test-forms/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-forms/build.gradle.kts b/test-forms/build.gradle.kts index aaf27444adb..a22cf91aa52 100644 --- a/test-forms/build.gradle.kts +++ b/test-forms/build.gradle.kts @@ -4,6 +4,6 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } From b733eada7bdd75dedf01f96e4ee7bfac58b0193b Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 16:38:19 +0100 Subject: [PATCH 31/35] Remove unused method --- .../android/support/rules/FormEntryActivityTestRule.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt index 0377ba1a169..96d148023fa 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt @@ -90,12 +90,6 @@ class FormEntryActivityTestRule : ExternalResource() { return this } - fun restoreActivity(): FormEntryActivityTestRule { - savedInstanceStateProvider.setState(outState) - scenario = ActivityScenario.launch(intent) - return this - } - fun simulateProcessRestart(): FormEntryActivityTestRule { CollectHelpers.simulateProcessRestart(object : TestDependencies() { override fun providesSavedInstanceStateProvider(): SavedInstanceStateProvider { From 4181ce841249db3c5df1ba40227271e36fdf80c1 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 17:52:41 +0100 Subject: [PATCH 32/35] Remove no longer needed test dummy --- .../system/SavedInstanceStateProvider.kt | 7 --- .../rules/FormEntryActivityTestRule.kt | 45 +------------------ .../activities/FormFillingActivity.java | 6 --- .../injection/config/AppDependencyModule.java | 6 --- 4 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 androidshared/src/main/java/org/odk/collect/androidshared/system/SavedInstanceStateProvider.kt diff --git a/androidshared/src/main/java/org/odk/collect/androidshared/system/SavedInstanceStateProvider.kt b/androidshared/src/main/java/org/odk/collect/androidshared/system/SavedInstanceStateProvider.kt deleted file mode 100644 index 660997fe230..00000000000 --- a/androidshared/src/main/java/org/odk/collect/androidshared/system/SavedInstanceStateProvider.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.odk.collect.androidshared.system - -import android.os.Bundle - -interface SavedInstanceStateProvider { - fun getState(savedInstanceState: Bundle?): Bundle? -} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt index 96d148023fa..4ddab4eb577 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/rules/FormEntryActivityTestRule.kt @@ -3,7 +3,6 @@ package org.odk.collect.android.support.rules import android.app.Activity import android.app.Application import android.content.Intent -import android.os.Bundle import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider @@ -15,11 +14,9 @@ import org.odk.collect.android.injection.DaggerUtils import org.odk.collect.android.storage.StorageSubdirectory import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.StorageUtils -import org.odk.collect.android.support.TestDependencies import org.odk.collect.android.support.pages.FormEntryPage import org.odk.collect.android.support.pages.FormHierarchyPage import org.odk.collect.android.support.pages.Page -import org.odk.collect.androidshared.system.SavedInstanceStateProvider import org.odk.collect.androidtest.ActivityScenarioExtensions.saveInstanceState import timber.log.Timber import java.io.IOException @@ -29,20 +26,6 @@ class FormEntryActivityTestRule : ExternalResource() { private lateinit var intent: Intent private lateinit var scenario: ActivityScenario - private var outState: Bundle? = null - - private val savedInstanceStateProvider = InMemSavedInstanceStateProvider() - - override fun before() { - super.before() - - CollectHelpers.overrideAppDependencyModule(object : TestDependencies() { - override fun providesSavedInstanceStateProvider(): SavedInstanceStateProvider { - return savedInstanceStateProvider - } - }) - } - override fun after() { try { scenario.close() @@ -81,7 +64,7 @@ class FormEntryActivityTestRule : ExternalResource() { fun navigateAwayFromActivity(): FormEntryActivityTestRule { scenario.moveToState(Lifecycle.State.STARTED) - outState = scenario.saveInstanceState() + scenario.saveInstanceState() return this } @@ -91,12 +74,7 @@ class FormEntryActivityTestRule : ExternalResource() { } fun simulateProcessRestart(): FormEntryActivityTestRule { - CollectHelpers.simulateProcessRestart(object : TestDependencies() { - override fun providesSavedInstanceStateProvider(): SavedInstanceStateProvider { - return savedInstanceStateProvider - } - }) - + CollectHelpers.simulateProcessRestart() return this } @@ -135,22 +113,3 @@ class FormEntryActivityTestRule : ExternalResource() { ) } } - -private class InMemSavedInstanceStateProvider : SavedInstanceStateProvider { - - private var bundle: Bundle? = null - - fun setState(savedInstanceState: Bundle?) { - bundle = savedInstanceState - } - - override fun getState(savedInstanceState: Bundle?): Bundle? { - return if (bundle != null) { - bundle.also { - bundle = null - } - } else { - savedInstanceState - } - } -} diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java index 24c11cb6839..fd571c3ea4e 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java @@ -176,7 +176,6 @@ import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; import org.odk.collect.androidshared.system.IntentLauncher; import org.odk.collect.androidshared.system.ProcessRestoreDetector; -import org.odk.collect.androidshared.system.SavedInstanceStateProvider; import org.odk.collect.androidshared.ui.DialogFragmentUtils; import org.odk.collect.androidshared.ui.FragmentFactoryBuilder; import org.odk.collect.androidshared.ui.SnackbarUtils; @@ -371,9 +370,6 @@ public void allowSwiping(boolean doSwipe) { @Inject public InstancesRepositoryProvider instancesRepositoryProvider; - @Inject - public SavedInstanceStateProvider savedInstanceStateProvider; - private final LocationProvidersReceiver locationProvidersReceiver = new LocationProvidersReceiver(); private SwipeHandler swipeHandler; @@ -442,8 +438,6 @@ public void onCreate(Bundle savedInstanceState) { .forClass(SelectOneFromMapDialogFragment.class, () -> new SelectOneFromMapDialogFragment(viewModelFactory)) .build()); - savedInstanceState = savedInstanceStateProvider.getState(savedInstanceState); - if (ProcessRestoreDetector.isProcessRestoring(this, savedInstanceState)) { if (savedInstanceState.containsKey(KEY_XPATH)) { startingXPath = savedInstanceState.getString(KEY_XPATH); diff --git a/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java b/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java index 30ddac7a060..6ac96235dc4 100644 --- a/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java +++ b/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java @@ -107,7 +107,6 @@ import org.odk.collect.androidshared.network.NetworkStateProvider; import org.odk.collect.androidshared.system.IntentLauncher; import org.odk.collect.androidshared.system.IntentLauncherImpl; -import org.odk.collect.androidshared.system.SavedInstanceStateProvider; import org.odk.collect.androidshared.utils.ScreenUtils; import org.odk.collect.async.CoroutineAndWorkManagerScheduler; import org.odk.collect.async.Scheduler; @@ -643,9 +642,4 @@ public ImageCompressionController providesImageCompressorManager() { public FormLoaderTask.FormEntryControllerFactory formEntryControllerFactory(SettingsProvider settingsProvider) { return new CollectFormEntryControllerFactory(settingsProvider.getUnprotectedSettings()); } - - @Provides - public SavedInstanceStateProvider providesSavedInstanceStateProvider() { - return savedInstanceState -> savedInstanceState; - } } From 174a2906e67b919fa359c77a6caac0c6c1d252d1 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 18:09:05 +0100 Subject: [PATCH 33/35] Inline settings setup in androidTest --- .../collect/android/TestSettingsProvider.kt | 21 ------------------- .../formentry/FieldListUpdateTest.java | 9 ++++++-- .../formentry/GuidanceHintFormTest.java | 17 ++++++++++++--- 3 files changed, 21 insertions(+), 26 deletions(-) delete mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt b/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt deleted file mode 100644 index 69317ff774a..00000000000 --- a/collect_app/src/androidTest/java/org/odk/collect/android/TestSettingsProvider.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.odk.collect.android - -import androidx.test.core.app.ApplicationProvider -import org.odk.collect.android.application.Collect -import org.odk.collect.android.injection.DaggerUtils -import org.odk.collect.settings.SettingsProvider -import org.odk.collect.shared.settings.Settings - -// Use just for testing -object TestSettingsProvider { - @JvmStatic - fun getSettingsProvider(): SettingsProvider { - return DaggerUtils.getComponent(ApplicationProvider.getApplicationContext()).settingsProvider() - } - - @JvmStatic - @JvmOverloads - fun getUnprotectedSettings(uuid: String? = null): Settings { - return getSettingsProvider().getUnprotectedSettings(uuid) - } -} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FieldListUpdateTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FieldListUpdateTest.java index 63c4d150771..d45faf77daa 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FieldListUpdateTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FieldListUpdateTest.java @@ -43,6 +43,7 @@ import static org.odk.collect.android.support.matchers.CustomMatchers.withIndex; import android.app.Activity; +import android.app.Application; import android.app.Instrumentation; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -60,7 +61,7 @@ import org.junit.Test; import org.junit.rules.RuleChain; import org.odk.collect.android.R; -import org.odk.collect.android.TestSettingsProvider; +import org.odk.collect.android.injection.DaggerUtils; import org.odk.collect.android.preferences.GuidanceHint; import org.odk.collect.android.storage.StoragePathProvider; import org.odk.collect.android.support.pages.FormEntryPage; @@ -323,7 +324,11 @@ public void questionsAppearingBeforeCurrentBinaryQuestion_ShouldNotChangeFocus() @Test public void changeInValueUsedInGuidanceHint_ShouldChangeGuidanceHintText() { - TestSettingsProvider.getUnprotectedSettings().save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES.toString()); + DaggerUtils.getComponent(ApplicationProvider.getApplicationContext()) + .settingsProvider() + .getUnprotectedSettings() + .save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES.toString()); + jumpToGroupWithText("Guidance hint"); onView(withText(startsWith("Source11"))).perform(click()); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/GuidanceHintFormTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/GuidanceHintFormTest.java index ae3ec0b5820..2efdb942eb1 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/GuidanceHintFormTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/GuidanceHintFormTest.java @@ -8,13 +8,16 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.CoreMatchers.not; +import android.app.Application; + +import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.matcher.ViewMatchers; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.odk.collect.android.R; -import org.odk.collect.android.TestSettingsProvider; +import org.odk.collect.android.injection.DaggerUtils; import org.odk.collect.android.preferences.GuidanceHint; import org.odk.collect.android.support.rules.BlankFormTestRule; import org.odk.collect.android.support.rules.TestRuleChain; @@ -36,7 +39,11 @@ public void guidanceHint_ShouldBeHiddenByDefault() { @Test public void guidanceHint_ShouldBeDisplayedWhenSettingSetToYes() { - TestSettingsProvider.getUnprotectedSettings().save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES.toString()); + DaggerUtils.getComponent(ApplicationProvider.getApplicationContext()) + .settingsProvider() + .getUnprotectedSettings() + .save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES.toString()); + // jump to force recreation of the view after the settings change onView(withId(R.id.menu_goto)).perform(click()); onView(withId(R.id.jumpBeginningButton)).perform(click()); @@ -46,7 +53,11 @@ public void guidanceHint_ShouldBeDisplayedWhenSettingSetToYes() { @Test public void guidanceHint_ShouldBeDisplayedAfterClickWhenSettingSetToYesCollapsed() { - TestSettingsProvider.getUnprotectedSettings().save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES_COLLAPSED.toString()); + DaggerUtils.getComponent(ApplicationProvider.getApplicationContext()) + .settingsProvider() + .getUnprotectedSettings() + .save(ProjectKeys.KEY_GUIDANCE_HINT, GuidanceHint.YES_COLLAPSED.toString()); + // jump to force recreation of the view after the settings change onView(withId(R.id.menu_goto)).perform(click()); onView(withId(R.id.jumpBeginningButton)).perform(click()); From dee2561d9288e3471c446cd65186c64c153c99b0 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 18:10:53 +0100 Subject: [PATCH 34/35] Remove unused method --- .../java/org/odk/collect/testshared/EspressoHelpers.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt index 9a927a7a29f..a787828af2c 100644 --- a/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt +++ b/testshared/src/main/java/org/odk/collect/testshared/EspressoHelpers.kt @@ -6,7 +6,6 @@ import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents -import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility @@ -16,7 +15,6 @@ import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.equalTo -import kotlin.reflect.KClass object EspressoHelpers { @@ -29,11 +27,6 @@ object EspressoHelpers { onView(withContentDescription(string)).perform(click()) } - fun assertIntents(vararg activityClasses: KClass<*>) { - val matchers = activityClasses.map { hasComponent(it.java.name) } - assertIntents(*matchers.toTypedArray()) - } - fun assertIntents(vararg matchers: Matcher) { val intents = Intents.getIntents() assertThat(matchers.size, equalTo(intents.size)) From 8985e19b6ebc51a98f4c47632b8ac931197412c2 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Fri, 25 Aug 2023 19:53:16 +0100 Subject: [PATCH 35/35] Move form media files to test-forms module --- .../feature/formentry/AudioRecordingTest.java | 9 ++++----- .../formentry/BackgroundAudioRecordingTest.java | 4 ++-- .../formentry/ExternalAudioRecordingTest.java | 5 ++--- .../android/feature/formentry/IntentGroupTest.java | 5 ++--- .../org/odk/collect/android/support/StorageUtils.kt | 7 +++---- .../collect/android/support/StubOpenRosaServer.java | 10 ++-------- .../src/main/resources}/media/TrapLists.csv | 0 .../src/main/resources}/media/doc.png | Bin .../src/main/resources}/media/espece.csv | 0 .../media/external-csv-search-produce.csv | 0 .../main/resources}/media/external_csv_cities.csv | 0 .../resources}/media/external_csv_countries.csv | 0 .../media/external_csv_neighbourhoods.csv | 0 .../src/main/resources}/media/external_data_10.xml | 0 .../src/main/resources}/media/famous.jpg | Bin .../src/main/resources}/media/file.gif | Bin .../media/formWithExternalFiles-media/fruits.csv | 0 .../media/formWithExternalFiles-media/fruits.xml | 0 .../media/formWithExternalFiles-media/itemsets.csv | 0 .../formWithExternalFiles-media/last-saved.xml | 0 .../src/main/resources}/media/fruits.csv | 0 .../src/main/resources}/media/itemSets.csv | 0 .../src/main/resources}/media/sampleAudio.wav | Bin .../src/main/resources}/media/sampleVideo.mp4 | Bin .../media/selectOneExternal-media/itemsets.csv | 0 .../media/simple-search-external-csv-fruits.csv | 0 .../src/main/resources}/media/staff_list.csv | 0 .../src/main/resources}/media/staff_rights.csv | 0 .../src/main/resources}/media/test.m4a | Bin 29 files changed, 15 insertions(+), 25 deletions(-) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/TrapLists.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/doc.png (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/espece.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/external-csv-search-produce.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/external_csv_cities.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/external_csv_countries.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/external_csv_neighbourhoods.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/external_data_10.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/famous.jpg (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/file.gif (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/formWithExternalFiles-media/fruits.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/formWithExternalFiles-media/fruits.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/formWithExternalFiles-media/itemsets.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/formWithExternalFiles-media/last-saved.xml (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/fruits.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/itemSets.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/sampleAudio.wav (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/sampleVideo.mp4 (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/selectOneExternal-media/itemsets.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/simple-search-external-csv-fruits.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/staff_list.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/staff_rights.csv (100%) rename {collect_app/src/androidTest/assets => test-forms/src/main/resources}/media/test.m4a (100%) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java index 1d341b74b7f..4b3d2550ada 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java @@ -1,22 +1,21 @@ package org.odk.collect.android.feature.formentry; -import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromResources; import android.app.Application; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.runner.RunWith; -import org.odk.collect.android.support.rules.CollectTestRule; import org.odk.collect.android.support.TestDependencies; -import org.odk.collect.android.support.rules.TestRuleChain; import org.odk.collect.android.support.pages.FormEntryPage; import org.odk.collect.android.support.pages.MainMenuPage; import org.odk.collect.android.support.pages.OkDialog; +import org.odk.collect.android.support.rules.CollectTestRule; +import org.odk.collect.android.support.rules.TestRuleChain; import org.odk.collect.audiorecorder.recording.AudioRecorder; import org.odk.collect.audiorecorder.testsupport.StubAudioRecorder; @@ -36,7 +35,7 @@ public AudioRecorder providesAudioRecorder(Application application) { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); + copyFileFromResources("media/test.m4a", stubRecording.getAbsolutePath()); stubAudioRecorderViewModel = new StubAudioRecorder(stubRecording.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java index 62dff195a2b..7d651158843 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java @@ -4,7 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromResources; import android.Manifest; import android.app.Activity; @@ -56,7 +56,7 @@ public AudioRecorder providesAudioRecorder(Application application) { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); + copyFileFromResources("media/test.m4a", stubRecording.getAbsolutePath()); stubAudioRecorderViewModel = new StubAudioRecorder(stubRecording.getAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java index f7c3ce63807..a4fab7ad98f 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java @@ -2,7 +2,7 @@ import static androidx.test.espresso.intent.Intents.intending; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromResources; import android.app.Activity; import android.app.Instrumentation; @@ -11,7 +11,6 @@ import android.provider.MediaStore; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; @@ -40,7 +39,7 @@ public class ExternalAudioRecordingTest { try { File stubRecording = File.createTempFile("test", ".m4a"); stubRecording.deleteOnExit(); - copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), stubRecording.getAbsolutePath(), "media/test.m4a"); + copyFileFromResources("media/test.m4a", stubRecording.getAbsolutePath()); Intent intent = new Intent(); intent.setData(Uri.fromFile(stubRecording)); diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java index f6f384bd061..5147b0655af 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/IntentGroupTest.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.odk.collect.android.support.matchers.CustomMatchers.withIndex; -import static org.odk.collect.android.utilities.FileUtils.copyFileFromAssets; +import static org.odk.collect.android.utilities.FileUtils.copyFileFromResources; import android.app.Activity; import android.app.Instrumentation; @@ -50,7 +50,6 @@ import androidx.core.content.FileProvider; import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Rule; import org.junit.Test; @@ -252,7 +251,7 @@ private Uri createTempFile(String name, String extension) throws IOException { .getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); File file = File.createTempFile(name, extension, downloadsDir); - copyFileFromAssets(InstrumentationRegistry.getInstrumentation().getContext(), file.getPath(), "media" + File.separator + name + "." + extension); + copyFileFromResources("media" + File.separator + name + "." + extension, file.getPath()); return getUriForFile(file); } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt index 602806e5cf2..5f59d13fd26 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StorageUtils.kt @@ -107,10 +107,9 @@ object StorageUtils { val mediaPathName = getFormsDirPath(projectName) + formFilename.replace(".xml", "") + org.odk.collect.android.utilities.FileUtils.MEDIA_SUFFIX + "/" org.odk.collect.android.utilities.FileUtils.checkMediaPath(File(mediaPathName)) for (mediaFilePath in mediaFilePaths) { - FileUtils.copyFileFromAssets( - InstrumentationRegistry.getInstrumentation().getContext(), - mediaPathName + getMediaFileName(mediaFilePath), - "media/$mediaFilePath" + FileUtils.copyFileFromResources( + "media/$mediaFilePath", + mediaPathName + getMediaFileName(mediaFilePath) ) } } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java index 4355d105128..9294de82d69 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/StubOpenRosaServer.java @@ -4,11 +4,8 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import android.content.res.AssetManager; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.test.platform.app.InstrumentationRegistry; import org.jetbrains.annotations.NotNull; import org.odk.collect.android.openrosa.CaseInsensitiveEmptyHeaders; @@ -239,13 +236,12 @@ private InputStream getManifestResponse(@NonNull URI uri) throws IOException { .append("\n"); for (String mediaFile : xformItem.getMediaFiles()) { - AssetManager assetManager = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); String mediaFileHash; if (randomHash) { mediaFileHash = RandomString.randomString(8); } else { - mediaFileHash = Md5.getMd5Hash(assetManager.open("media/" + mediaFile)); + mediaFileHash = Md5.getMd5Hash(getResourceAsStream("media/" + mediaFile)); } stringBuilder @@ -277,9 +273,7 @@ private InputStream getFormXML(String formID) throws IOException { @NotNull private InputStream getMediaFile(URI uri) throws IOException { String mediaFileName = uri.getQuery().split("=")[1]; - - AssetManager assetManager = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); - return assetManager.open("media/" + mediaFileName); + return getResourceAsStream("media/" + mediaFileName); } private static class XFormItem { diff --git a/collect_app/src/androidTest/assets/media/TrapLists.csv b/test-forms/src/main/resources/media/TrapLists.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/TrapLists.csv rename to test-forms/src/main/resources/media/TrapLists.csv diff --git a/collect_app/src/androidTest/assets/media/doc.png b/test-forms/src/main/resources/media/doc.png similarity index 100% rename from collect_app/src/androidTest/assets/media/doc.png rename to test-forms/src/main/resources/media/doc.png diff --git a/collect_app/src/androidTest/assets/media/espece.csv b/test-forms/src/main/resources/media/espece.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/espece.csv rename to test-forms/src/main/resources/media/espece.csv diff --git a/collect_app/src/androidTest/assets/media/external-csv-search-produce.csv b/test-forms/src/main/resources/media/external-csv-search-produce.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/external-csv-search-produce.csv rename to test-forms/src/main/resources/media/external-csv-search-produce.csv diff --git a/collect_app/src/androidTest/assets/media/external_csv_cities.csv b/test-forms/src/main/resources/media/external_csv_cities.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/external_csv_cities.csv rename to test-forms/src/main/resources/media/external_csv_cities.csv diff --git a/collect_app/src/androidTest/assets/media/external_csv_countries.csv b/test-forms/src/main/resources/media/external_csv_countries.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/external_csv_countries.csv rename to test-forms/src/main/resources/media/external_csv_countries.csv diff --git a/collect_app/src/androidTest/assets/media/external_csv_neighbourhoods.csv b/test-forms/src/main/resources/media/external_csv_neighbourhoods.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/external_csv_neighbourhoods.csv rename to test-forms/src/main/resources/media/external_csv_neighbourhoods.csv diff --git a/collect_app/src/androidTest/assets/media/external_data_10.xml b/test-forms/src/main/resources/media/external_data_10.xml similarity index 100% rename from collect_app/src/androidTest/assets/media/external_data_10.xml rename to test-forms/src/main/resources/media/external_data_10.xml diff --git a/collect_app/src/androidTest/assets/media/famous.jpg b/test-forms/src/main/resources/media/famous.jpg similarity index 100% rename from collect_app/src/androidTest/assets/media/famous.jpg rename to test-forms/src/main/resources/media/famous.jpg diff --git a/collect_app/src/androidTest/assets/media/file.gif b/test-forms/src/main/resources/media/file.gif similarity index 100% rename from collect_app/src/androidTest/assets/media/file.gif rename to test-forms/src/main/resources/media/file.gif diff --git a/collect_app/src/androidTest/assets/media/formWithExternalFiles-media/fruits.csv b/test-forms/src/main/resources/media/formWithExternalFiles-media/fruits.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/formWithExternalFiles-media/fruits.csv rename to test-forms/src/main/resources/media/formWithExternalFiles-media/fruits.csv diff --git a/collect_app/src/androidTest/assets/media/formWithExternalFiles-media/fruits.xml b/test-forms/src/main/resources/media/formWithExternalFiles-media/fruits.xml similarity index 100% rename from collect_app/src/androidTest/assets/media/formWithExternalFiles-media/fruits.xml rename to test-forms/src/main/resources/media/formWithExternalFiles-media/fruits.xml diff --git a/collect_app/src/androidTest/assets/media/formWithExternalFiles-media/itemsets.csv b/test-forms/src/main/resources/media/formWithExternalFiles-media/itemsets.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/formWithExternalFiles-media/itemsets.csv rename to test-forms/src/main/resources/media/formWithExternalFiles-media/itemsets.csv diff --git a/collect_app/src/androidTest/assets/media/formWithExternalFiles-media/last-saved.xml b/test-forms/src/main/resources/media/formWithExternalFiles-media/last-saved.xml similarity index 100% rename from collect_app/src/androidTest/assets/media/formWithExternalFiles-media/last-saved.xml rename to test-forms/src/main/resources/media/formWithExternalFiles-media/last-saved.xml diff --git a/collect_app/src/androidTest/assets/media/fruits.csv b/test-forms/src/main/resources/media/fruits.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/fruits.csv rename to test-forms/src/main/resources/media/fruits.csv diff --git a/collect_app/src/androidTest/assets/media/itemSets.csv b/test-forms/src/main/resources/media/itemSets.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/itemSets.csv rename to test-forms/src/main/resources/media/itemSets.csv diff --git a/collect_app/src/androidTest/assets/media/sampleAudio.wav b/test-forms/src/main/resources/media/sampleAudio.wav similarity index 100% rename from collect_app/src/androidTest/assets/media/sampleAudio.wav rename to test-forms/src/main/resources/media/sampleAudio.wav diff --git a/collect_app/src/androidTest/assets/media/sampleVideo.mp4 b/test-forms/src/main/resources/media/sampleVideo.mp4 similarity index 100% rename from collect_app/src/androidTest/assets/media/sampleVideo.mp4 rename to test-forms/src/main/resources/media/sampleVideo.mp4 diff --git a/collect_app/src/androidTest/assets/media/selectOneExternal-media/itemsets.csv b/test-forms/src/main/resources/media/selectOneExternal-media/itemsets.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/selectOneExternal-media/itemsets.csv rename to test-forms/src/main/resources/media/selectOneExternal-media/itemsets.csv diff --git a/collect_app/src/androidTest/assets/media/simple-search-external-csv-fruits.csv b/test-forms/src/main/resources/media/simple-search-external-csv-fruits.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/simple-search-external-csv-fruits.csv rename to test-forms/src/main/resources/media/simple-search-external-csv-fruits.csv diff --git a/collect_app/src/androidTest/assets/media/staff_list.csv b/test-forms/src/main/resources/media/staff_list.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/staff_list.csv rename to test-forms/src/main/resources/media/staff_list.csv diff --git a/collect_app/src/androidTest/assets/media/staff_rights.csv b/test-forms/src/main/resources/media/staff_rights.csv similarity index 100% rename from collect_app/src/androidTest/assets/media/staff_rights.csv rename to test-forms/src/main/resources/media/staff_rights.csv diff --git a/collect_app/src/androidTest/assets/media/test.m4a b/test-forms/src/main/resources/media/test.m4a similarity index 100% rename from collect_app/src/androidTest/assets/media/test.m4a rename to test-forms/src/main/resources/media/test.m4a