Skip to content

Commit

Permalink
Fix #3315: Set up UIAutomator with Bazel (#3505)
Browse files Browse the repository at this point in the history
* Introducing UiAutomator

* Using 23 api level

* some local tweaks

* Updated implementation based on adb commands

* Nit fixes

* Nit fixes

* Requested changes

* fix class name

* update dependencies

* Nit fixes

* Requested changes

* updated maven_install

* Nit fix

* Add CODEOWNERS

* Added KDoc

* NIT

* Added comments

* Added oppia_instrumentation_test.bzl

* added exempted_file_path

* Helper utility shifted to testing module

* NIT

* NIT

* deleted the previous utility

* ignoring instrumentation in ComputeAffectedTests.kt

* NIT

* NIT

* NIT

* changed startsWith("instrumentation/")

* filtered bazel targets

* NIT

* NIT

* filtering the instrumentation from the total affected targets

* NIT

* changed directory to javatests

* NIT

* NIT

* fixed scripts

* NIT

* updated the ComputedAffectedTests

* ignoring player directory
  • Loading branch information
FareesHussain authored Aug 18, 2021
1 parent 772e776 commit a55f808
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 9 deletions.
8 changes: 8 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ NOTICE @BenHenning
# Global model ownership.
/model/ @vinitamurthi @BenHenning


#####################################################################################
# instrumentation #
#####################################################################################

# End-to-end test utilities and modules.
/instrumentation/src/java/**/*.kt @anandwana001 @BenHenning

#####################################################################################
# global overrides #
#####################################################################################
Expand Down
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ package_group(
"//app/...",
"//data/...",
"//domain/...",
"//instrumentation/...",
"//testing/...",
"//utility/...",
],
Expand Down
15 changes: 15 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,21 @@ http_archive(

load("@rules_jvm_external//:defs.bzl", "maven_install")

ATS_TAG = "1edfdab3134a7f01b37afabd3eebfd2c5bb05151"

ATS_SHA256 = "dcd1ff76aef1a26329d77863972780c8fe1fc8ff625747342239f0489c2837ec"

http_archive(
name = "android_test_support",
sha256 = ATS_SHA256,
strip_prefix = "android-test-%s" % ATS_TAG,
urls = ["https://github.com/android/android-test/archive/%s.tar.gz" % ATS_TAG],
)

load("@android_test_support//:repo.bzl", "android_test_repositories")

android_test_repositories()

# Note to developers: new dependencies should be added to //third_party:versions.bzl, not here.
maven_install(
artifacts = DAGGER_ARTIFACTS + get_maven_dependencies(),
Expand Down
30 changes: 30 additions & 0 deletions instrumentation/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
This library contains all android_binary and android_instrumentation_test targets to each
test suite in instrumentation module as a oppia_instrumentation_test wrapper.
Note that:
- All the oppia_instrumentation_test targets are named similar to the respective class name
of the test suite.
"""

exports_files(["src/javatests/AndroidManifest.xml"])

# Used for end-to-end tests
android_binary(
name = "oppia_test",
testonly = True,
custom_package = "org.oppia.android",
enable_data_binding = True,
manifest = "//app:src/main/AndroidManifest.xml",
manifest_values = {
"applicationId": "org.oppia.android",
"minSdkVersion": "19",
"targetSdkVersion": "29",
"versionCode": "0",
"versionName": "0.1-test",
},
multidex = "native",
visibility = ["//:oppia_testing_visibility"],
deps = [
"//app",
],
)
40 changes: 40 additions & 0 deletions instrumentation/oppia_instrumentation_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
Instrumentation macros to define up end-to-end tests.
"""

load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")

def oppia_instrumentation_test(
name,
srcs,
deps):
"""
Creates library, binary, and instrumentation test for each test suite.
Args:
name: str. The class name of the test suite.
srcs: list of str. List of test files corresponding to this test suite.
deps: list of str. The list of dependencies needed to build and run the test.
"""
kt_android_library(
name = "%s_lib" % name,
testonly = True,
srcs = srcs,
deps = deps,
)

native.android_binary(
name = "%sBinary" % name,
testonly = True,
custom_package = "org.oppia.android",
instruments = "//instrumentation:oppia_test",
manifest = "//instrumentation:src/javatests/AndroidManifest.xml",
deps = [":%s_lib" % name],
)

# TODO(#3617): Target isn't supported yet.
native.android_instrumentation_test(
name = name,
target_device = "@android_test_support//tools/android/emulated_devices/generic_phone:android_23_x86_qemu2",
test_app = ":%sBinary" % name,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
This library contains the utilities used in end-to-end testing.
"""

load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")

kt_android_library(
name = "e2e_test_helper",
testonly = True,
srcs = [
"EndToEndTestHelper.kt",
],
visibility = ["//:oppia_testing_visibility"],
deps = [
"//third_party:androidx_test_ext_junit",
"//third_party:androidx_test_uiautomator_uiautomator",
"//third_party:com_google_truth_truth",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.oppia.android.instrumentation.testing

import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import com.google.common.truth.Truth.assertThat

/** This object contains common operations used for end-to-end tests. */
object EndToEndTestHelper {

private val OPPIA_PACKAGE = "org.oppia.android"
private val LAUNCH_TIMEOUT_SECONDS = 30000L
private val TRANSITION_TIMEOUT_SECONDS = 5000L

/** Starts Oppia from the home screen. */
fun UiDevice.startOppiaFromScratch() {
// Start from the home screen
pressHome()

// Wait for launcher
val launcherPackage = launcherPackageName
assertThat(launcherPackage).isNotNull()
wait(Until.hasObject(By.pkg(launcherPackage).depth(1)), LAUNCH_TIMEOUT_SECONDS)

// Launch the blueprint app
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager
.getLaunchIntentForPackage(OPPIA_PACKAGE)
intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) // Clear out any previous instances
context.startActivity(intent)

// Wait for the app to appear
wait(Until.hasObject(By.pkg(OPPIA_PACKAGE)), LAUNCH_TIMEOUT_SECONDS)
}

/** Waits for the view with given resourceId to appear. */
fun UiDevice.waitForRes(resourceId: String, timeout: Long = TRANSITION_TIMEOUT_SECONDS) {
wait(Until.hasObject(By.res(resourceId)), timeout)
}

/** Waits for the view with given text to appear. */
fun UiDevice.waitForText(text: String) {
wait(Until.hasObject(By.text(text)), TRANSITION_TIMEOUT_SECONDS)
}

/** Return the UiObject with the given text. */
fun UiDevice.findObjectByText(text: String): UiObject2? {
waitForText(text)
return findObject(By.text(text))
}

/** Returns the UiObject for the given resourceId. */
fun UiDevice.findObjectByRes(resourceId: String): UiObject2? {
waitForRes(resourceId)
return findObject(By.res("$OPPIA_PACKAGE:id/$resourceId"))
}

/** Performs a scroll until the view with the give text is visible. */
fun scrollRecyclerViewTextIntoView(text: String, isVertical: Boolean = true) {
val recyclerView = UiScrollable(UiSelector().scrollable(true))
if (isVertical) {
recyclerView.setAsVerticalList()
} else {
recyclerView.setAsHorizontalList()
}
recyclerView.scrollTextIntoView(text)
}
}
11 changes: 11 additions & 0 deletions instrumentation/src/javatests/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xmxl version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.oppia.android.app.instrumentation">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="29" />
<instrumentation
android:targetPackage="org.oppia.android"
android:name="androidx.test.runner.AndroidJUnitRunner" />
<application android:label="OppiaTest" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
This library contains the test suite source files of the player package.
"""

load("//instrumentation:oppia_instrumentation_test.bzl", "oppia_instrumentation_test")

oppia_instrumentation_test(
name = "ExplorationPlayerTest",
srcs = [
"ExplorationPlayerTest.kt",
],
deps = [
"//instrumentation/src/java/org/oppia/android/instrumentation/testing:e2e_test_helper",
"//third_party:androidx_test_ext_junit",
"//third_party:androidx_test_runner",
"//third_party:androidx_test_uiautomator_uiautomator",
"//third_party:com_google_truth_truth",
"//third_party:junit_junit",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.oppia.android.instrumentation.player

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.oppia.android.instrumentation.testing.EndToEndTestHelper.findObjectByRes
import org.oppia.android.instrumentation.testing.EndToEndTestHelper.findObjectByText
import org.oppia.android.instrumentation.testing.EndToEndTestHelper.scrollRecyclerViewTextIntoView
import org.oppia.android.instrumentation.testing.EndToEndTestHelper.startOppiaFromScratch
import org.oppia.android.instrumentation.testing.EndToEndTestHelper.waitForRes

/** Tests for Explorations. */
class ExplorationPlayerTest {
private lateinit var device: UiDevice

@Before
fun setUp() {
// Initialize UiDevice instance
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.startOppiaFromScratch()
}

@Test
fun testExploration_prototypeExploration_toolbarTitle_isDisplayedSuccessfully() {
navigateToPrototypeExploration()
assertThat(device.findObjectByRes("exploration_toolbar_title")).isNotNull()
}

/** Navigates and opens the Prototype Exploration using the admin profile. */
private fun navigateToPrototypeExploration() {
device.findObjectByRes("skip_text_view")?.click()
device.findObjectByRes("get_started_button")?.click()
device.waitForRes("profile_select_text")
device.findObjectByText("Admin")?.click()
scrollRecyclerViewTextIntoView("First Test Topic")
device.findObjectByText("First Test Topic")?.click()
device.findObjectByText("LESSONS")?.click()
device.findObjectByText("First Story")?.click()
scrollRecyclerViewTextIntoView("Chapter 1: Prototype Exploration")
device.findObjectByText("Chapter 1: Prototype Exploration")?.click()
}
}
1 change: 1 addition & 0 deletions scripts/assets/test_file_exemptions.textproto
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/FloatExt
exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/FractionExtensions.kt"
exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/JsonAssetRetriever.kt"
exempted_file_path: "domain/src/test/java/org/oppia/android/domain/classify/InteractionObjectTestBuilder.kt"
exempted_file_path: "instrumentation/src/java/org/oppia/android/instrumentation/testing/EndToEndTestHelper.kt"
exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandExecutor.kt"
exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandResult.kt"
exempted_file_path: "scripts/src/java/org/oppia/android/scripts/license/LicenseFetcher.kt"
Expand Down
2 changes: 1 addition & 1 deletion scripts/buildifier_lint_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ else
buildifier_file_path="$github_actions_path/oppia-android-tools/buildifier"
fi

$buildifier_file_path --lint=warn --mode=check --warnings=-native-android,+out-of-order-load,+unsorted-dict-items -r app data domain model testing utility third_party tools scripts BUILD.bazel WORKSPACE oppia_android_test.bzl
$buildifier_file_path --lint=warn --mode=check --warnings=-native-android,+out-of-order-load,+unsorted-dict-items -r app data domain instrumentation model testing utility third_party tools scripts BUILD.bazel WORKSPACE oppia_android_test.bzl

status=$?

Expand Down
4 changes: 2 additions & 2 deletions scripts/ktlint_lint_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ else
jar_file_path="$github_actions_path/oppia-android-tools/ktlint"
fi

java -jar $jar_file_path --android app/src/**/*.kt data/src/**/*.kt domain/src/**/*.kt testing/src/**/*.kt utility/src/**/*.kt scripts/src/**/*.kt
java -jar $jar_file_path --android app/src/**/*.kt data/src/**/*.kt domain/src/**/*.kt testing/src/**/*.kt utility/src/**/*.kt scripts/src/**/*.kt instrumentation/src/**/*.kt

status=$?

Expand All @@ -25,7 +25,7 @@ else
echo "********************************"
echo "Ktlint issue found."
echo "Please fix the above issues.
You can also use the java -jar $jar_file_path -F --android domain/src/**/*.kt utility/src/**/*.kt data/src/**/*.kt app/src/**/*.kt testing/src/**/*.kt scripts/src/**/*.kt
You can also use the java -jar $jar_file_path -F --android domain/src/**/*.kt utility/src/**/*.kt data/src/**/*.kt app/src/**/*.kt testing/src/**/*.kt scripts/src/**/*.kt instrumentation/src/**/*.kt
command to fix the most common issues."
echo "Please note, there might be a possibility where the above command will not fix the issue.
In that case, you will have to fix it yourself."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,23 @@ private fun computeAffectedTargetsForDevelopBranch(bazelClient: BazelClient, out

val allTestTargets = bazelClient.retrieveAllTestTargets()
println()

// Filtering out the targets to be ignored.
val nonInstrumentationAffectedTestTargets = allTestTargets.filter { targetPath ->
!targetPath
.startsWith(
"//instrumentation/src/javatests/org/oppia/android/instrumentation/player",
ignoreCase = true
)
}

println(
"Affected test targets:" +
"\n${allTestTargets.joinToString(separator = "\n") { "- $it" }}"
"\n${nonInstrumentationAffectedTestTargets.joinToString(separator = "\n") { "- $it" }}"
)
outputFile.printWriter().use { writer -> allTestTargets.forEach { writer.println(it) } }
outputFile.printWriter().use { writer ->
nonInstrumentationAffectedTestTargets.forEach { writer.println(it) }
}
}

private fun computeAffectedTargetsForNonDevelopBranch(
Expand Down Expand Up @@ -103,10 +115,22 @@ private fun computeAffectedTargetsForNonDevelopBranch(
println("Affected test targets due to transitive build deps: $transitiveTestTargets")

val allAffectedTestTargets = (affectedTestTargets + transitiveTestTargets).toSet()

// Filtering out the targets to be ignored.
val nonInstrumentationAffectedTestTargets = allAffectedTestTargets.filter { targetPath ->
!targetPath
.startsWith(
"//instrumentation/src/javatests/org/oppia/android/instrumentation/player",
ignoreCase = true
)
}

println()
println(
"Affected test targets:" +
"\n${allAffectedTestTargets.joinToString(separator = "\n") { "- $it" }}"
"\n${nonInstrumentationAffectedTestTargets.joinToString(separator = "\n") { "- $it" }}"
)
outputFile.printWriter().use { writer -> allAffectedTestTargets.forEach { writer.println(it) } }
outputFile.printWriter().use { writer ->
nonInstrumentationAffectedTestTargets.forEach { writer.println(it) }
}
}
Loading

0 comments on commit a55f808

Please sign in to comment.