diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 192827c5..86c56f9b 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -100,6 +100,12 @@ emerge {
publishableApiKey.set(System.getenv("REAPER_API_KEY"))
}
+ performance {
+ projectPath.set(":performance")
+ tag.set("oneoff")
+ enabled.set(true)
+ }
+
vcs {
gitHub {
repoName.set("hackernews")
diff --git a/android/performance/.gitignore b/android/performance/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/android/performance/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/performance/build.gradle.kts b/android/performance/build.gradle.kts
new file mode 100644
index 00000000..49390343
--- /dev/null
+++ b/android/performance/build.gradle.kts
@@ -0,0 +1,21 @@
+// This is a com.android.test project which is automatically configured by the Emerge Tools Gradle plugin:
+//
+// - The SDK version targets are automatically set to be identical to the app project.
+// - The same build types as your app project are automatically created.
+// - Test libraries including UI Automator and Junit and are automatically added as dependencies.
+//
+// The configuration can be modified in this file as needed.
+
+plugins {
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ // Emerge's Performance Testing SDK (Required):
+ implementation("com.emergetools.test:performance:2.1.1")
+ // Emerge's UIAutomator helper library (Optional): https://github.com/EmergeTools/relax
+ implementation("com.emergetools.test:relax:0.1.0")
+
+ // Add additional dependencies here as needed.
+ // Note Espresso is not supported as it degrades performance.
+}
diff --git a/android/performance/src/main/AndroidManifest.xml b/android/performance/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..76073216
--- /dev/null
+++ b/android/performance/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/android/performance/src/main/java/com/emergetools/hackernews/ExamplePerformanceTest.kt b/android/performance/src/main/java/com/emergetools/hackernews/ExamplePerformanceTest.kt
new file mode 100644
index 00000000..b714e35b
--- /dev/null
+++ b/android/performance/src/main/java/com/emergetools/hackernews/ExamplePerformanceTest.kt
@@ -0,0 +1,70 @@
+package com.emergetools.hackernews
+
+import android.content.Context
+import android.content.Intent
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.emergetools.test.annotations.EmergeInit
+import com.emergetools.test.annotations.EmergeSetup
+import com.emergetools.test.annotations.EmergeTest
+
+private const val LAUNCH_TIMEOUT = 5000L
+private const val APP_PACKAGE_NAME = "com.emergetools.hackernews"
+
+/**
+ * An example performance test class.
+ *
+ * Performance test classes can have multiple tests, but tests in a given class share @EmergeInit and @EmergeSetup
+ * methods. For tests that require a different init or setup multiple test classes are supported.
+ *
+ * Note that each test (ie. each method annotated with @EmergeTest) will be run on a separate device, they cannot
+ * impact each other in any way.
+ */
+class ExamplePerformanceTest {
+
+ @EmergeInit
+ fun init() {
+ // OPTIONAL
+ // Runs just once after installing the app on the test device before any other method.
+ // Typically this is used to log into the app, if needed.
+ // Only one @EmergeInit method per class is supported.
+ }
+
+ @EmergeSetup
+ fun setup() {
+ // OPTIONAL
+ // Runs once before each test iteration.
+ // Typically this is used to navigate through to the screen where the performance test is meant to begin.
+ // Only one @EmergeSetup method per class is supported.
+ }
+
+ @EmergeTest
+ fun myPerformanceTest() {
+ // REQUIRED
+ // The performance test. This is where the app should go through a short flow whose performance is critical.
+ // This might involve launching a screen or any other operation supported by UI Automator.
+ // As an example here we launch the application from the home screen.
+
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ device.pressHome()
+
+ // Wait for launcher
+ device.wait(Until.hasObject(By.pkg(device.launcherPackageName).depth(0)), LAUNCH_TIMEOUT)
+
+ // Launch the app
+ val context = ApplicationProvider.getApplicationContext()
+ val intent = checkNotNull(context.packageManager.getLaunchIntentForPackage(APP_PACKAGE_NAME)) {
+ "Could not get launch intent for package $APP_PACKAGE_NAME"
+ }
+ // Clear out any previous instances
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+
+ // Wait for the app to appear
+ device.wait(Until.hasObject(By.pkg(APP_PACKAGE_NAME).depth(0)), LAUNCH_TIMEOUT)
+ }
+}
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 10f1afc5..3b81df2a 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -21,3 +21,4 @@ dependencyResolutionManagement {
rootProject.name = "Hacker News"
include(":app")
+include(":performance")