Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Make it possible to consume Espresso server as a library #1020

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ annotationVersion | The target version of `androidx.annotation:annotation` pakag

### additionalAppDependencies

The value of this entry must be a non empty array of dependent module names with their versions. The scripts adds all these items as `implementation` lines of `dependencies` category in the app [build.gradle.kts](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/app/build.gradle.kts) script. Example: `["xerces.xercesImpl:2.8.0", "xerces.xmlParserAPIs:2.6.2"]`
The value of this entry must be a non empty array of dependent module names with their versions. The scripts adds all these items as `api` lines of `dependencies` category in the library [build.gradle.kts](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/library/build.gradle.kts) script. Example: `["xerces.xercesImpl:2.8.0", "xerces.xmlParserAPIs:2.6.2"]`

### additionalAndroidTestDependencies

Expand Down
161 changes: 35 additions & 126 deletions espresso-server/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,52 @@ plugins {
kotlin("android")
}

val appiumCompileSdk: String by project
val appiumMinSdk: String by project
val appiumTargetSdk: String by project
val appiumBuildTools: String by project
val appiumTargetPackage: String by project
val appiumSourceCompatibility: String by project
val appiumTargetCompatibility: String by project
val appiumJvmTarget: String by project
val appiumKotlin: String by project
val appiumAndroidxTestVersion: String by project
val appiumAnnotationVersion: String by project
val appiumComposeVersion: String by project
val appiumGsonVersion: String by project
val appiumEspressoVersion: String by project
val appiumMockitoVersion: String by project
val appiumNanohttpdVersion: String by project
val appiumRobolectricVersion: String by project
val appiumJUnitVersion: String by project
val appiumUiAutomatorVersion: String by project

android {
compileSdk = getIntProperty("appiumCompileSdk", 34)
buildToolsVersion = getStringProperty("appiumBuildTools", "33.0.2")
compileSdk = appiumCompileSdk.toInt()
buildToolsVersion = appiumBuildTools
namespace = "io.appium.espressoserver"

defaultConfig {
// <instrumentation android:targetPackage=""/>
applicationId = getStringProperty("appiumTargetPackage", "io.appium.espressoserver")
applicationId = appiumTargetPackage
// <manifest package=""/>
testApplicationId = "io.appium.espressoserver.test"
testHandleProfiling = false
testFunctionalTest = false
minSdk = getIntProperty("appiumMinSdk", 21)
targetSdk = getIntProperty("appiumTargetSdk", 34)
minSdk = appiumMinSdk.toInt()
targetSdk = appiumTargetSdk.toInt()
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
getByName("debug") {
isZipAlignEnabled = getBooleanProperty("appiumZipAlign", true)
}
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}

sourceSets {
getByName("test") {
java.srcDirs("src/androidTest/java")
}
}

testOptions {
unitTests.isReturnDefaultValues = true
}

signingConfigs {
getByName("debug") {
findProperty("appiumKeystoreFile")?.also {
Expand All @@ -61,124 +70,24 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.valueOf(
getStringProperty(
"appiumSourceCompatibility",
"VERSION_1_8"
).toUpperCase()
)
targetCompatibility = JavaVersion.valueOf(
getStringProperty(
"appiumTargetCompatibility",
"VERSION_1_8"
).toUpperCase()
)
sourceCompatibility = JavaVersion.valueOf(appiumSourceCompatibility.uppercase())
targetCompatibility = JavaVersion.valueOf(appiumTargetCompatibility.uppercase())
}

kotlinOptions {
jvmTarget = getStringProperty("appiumJvmTarget", JavaVersion.VERSION_1_8.toString())
jvmTarget = appiumJvmTarget
}

packagingOptions {
packaging {
resources.excludes.add("META-INF/**")
}

namespace = "io.appium.espressoserver"
}

val kotlinVersion = rootProject.extra["appiumKotlin"]
val composeVersion = getStringProperty("appiumComposeVersion", Version.compose)
val espressoVersion = getStringProperty("appiumEspressoVersion", Version.espresso)
val annotationVersion = getStringProperty("appiumAnnotationVersion", Version.annotation)

dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
// additionalAppDependencies placeholder (don't change or delete this line)

testImplementation("org.powermock:powermock-api-mockito2:${Version.mocklib}")
testImplementation("org.powermock:powermock-classloading-xstream:${Version.mocklib}")
testImplementation("org.powermock:powermock-module-junit4-rule:${Version.mocklib}")
testImplementation("org.powermock:powermock-module-junit4:${Version.mocklib}")
testImplementation("androidx.annotation:annotation:${annotationVersion}")
testImplementation("androidx.test.espresso:espresso-contrib:${espressoVersion}")
testImplementation("androidx.test.espresso:espresso-core:${espressoVersion}")
testImplementation("androidx.test.espresso:espresso-web:${espressoVersion}")
testImplementation("androidx.test.uiautomator:uiautomator:${Version.uia}")
testImplementation("androidx.test:core:${Version.testlib}")
testImplementation("androidx.test:runner:${Version.testlib}")
testImplementation("androidx.test:rules:${Version.testlib}")
testImplementation("com.google.code.gson:gson:${Version.gson}")
testImplementation("junit:junit:${Version.junit}")
testImplementation("org.mockito:mockito-core:${Version.mockito}")
testImplementation("org.nanohttpd:nanohttpd-webserver:${Version.nanohttpd}")
testImplementation("org.robolectric:robolectric:${Version.robolectric}")
testImplementation("org.jetbrains.kotlin:kotlin-test:${kotlinVersion}")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
testImplementation("androidx.compose.ui:ui-test:${composeVersion}")
testImplementation("androidx.compose.ui:ui-test-junit4:${composeVersion}")

androidTestImplementation("androidx.annotation:annotation:${annotationVersion}")
androidTestImplementation("androidx.test.espresso:espresso-contrib:${espressoVersion}") {
// Exclude transitive dependencies to limit conflicts with AndroidX libraries from AUT.
// Link to PR with fix and discussion https://github.com/appium/appium-espresso-driver/pull/596
isTransitive = false
}
androidTestImplementation("androidx.test.espresso:espresso-web:${espressoVersion}") {
because("Espresso Web Atoms support (mobile: webAtoms)")
}
androidTestImplementation("androidx.test.uiautomator:uiautomator:${Version.uia}") {
because("UiAutomator support (mobile: uiautomator)")
}
androidTestImplementation("androidx.test:core:${Version.testlib}")
androidTestImplementation("androidx.test:runner:${Version.testlib}")
androidTestImplementation("androidx.test:rules:${Version.testlib}")
androidTestImplementation("com.google.code.gson:gson:${Version.gson}")
androidTestImplementation("org.nanohttpd:nanohttpd-webserver:${Version.nanohttpd}")
androidTestImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
androidTestImplementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
androidTestImplementation("androidx.compose.ui:ui-test:${composeVersion}") {
because("Android Compose support")
}
androidTestImplementation("androidx.compose.ui:ui-test-junit4:${composeVersion}") {
because("Android Compose support")
}
androidTestImplementation(project(":library"))
androidTestImplementation("junit:junit:$appiumJUnitVersion")
androidTestImplementation("androidx.test:core:$appiumAndroidxTestVersion")
androidTestImplementation("androidx.test:runner:$appiumAndroidxTestVersion")

// additionalAndroidTestDependencies placeholder (don't change or delete this line)
}

configurations.all {
resolutionStrategy.eachDependency {
// To avoid "androidx.annotation:annotation" version conflict.
if (requested.group == "androidx.annotation" && !requested.name.contains("annotation")) {
useVersion(annotationVersion)
}
}
}

tasks.withType<Test> {
systemProperty("skipespressoserver", "true")
}

object Version {
const val espresso = "3.5.1"
const val testlib = "1.5.0"
const val mocklib = "2.0.9"
const val gson = "2.10.1"
const val uia = "2.2.0"
const val nanohttpd = "2.3.1"
const val annotation = "1.6.0"
const val mockito = "5.1.1"
const val robolectric = "4.9.2"
const val junit = "4.13.2"
const val compose = "1.1.1"
}

fun Project.getStringProperty(name: String, default: String): String =
properties.getOrDefault(name, default).toString()

fun Project.getIntProperty(name: String, default: Int): Int =
this.getStringProperty(name, default.toString()).toInt()

fun Project.getBooleanProperty(name: String, default: Boolean): Boolean =
this.getStringProperty(name, default.toString()).toBoolean()
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,11 @@

package io.appium.espressoserver

import org.junit.Assume
import org.junit.Test

import java.io.IOException

import androidx.test.filters.LargeTest
import io.appium.espressoserver.lib.drivers.DriverContext
import io.appium.espressoserver.lib.handlers.exceptions.DuplicateRouteException
import io.appium.espressoserver.lib.http.Server

import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import org.junit.Test

/**
* Instrumentation test, which will execute on an Android device.
Expand All @@ -42,51 +31,12 @@ import androidx.compose.ui.test.junit4.AndroidComposeTestRule
class EspressoServerRunnerTest {

@get:Rule
val composeRule = AndroidComposeTestRule(
activityRule = EmptyTestRule(),
activityProvider = { error("Can't provide current activity") }
).also {
composeTestRule = it
}

private val syncComposeClock = Thread {
while (!Server.isStopRequestReceived) {
if (context.currentStrategyType == DriverContext.StrategyType.COMPOSE) {
composeTestRule.mainClock.advanceTimeByFrame()
}
// Let Android run measure, draw and in general any other async operations. AndroidComposeTestRule.android.kt:325
Thread.sleep(ANDROID_ASYNC_WAIT_TIME_MS)
}
}
val server = Server()

@Test
@Throws(InterruptedException::class, IOException::class, DuplicateRouteException::class)
fun startEspressoServer() {
if (System.getProperty("skipespressoserver") != null) {
Assume.assumeTrue(true)
return
}
try {
Server.start()
syncComposeClock.start()
while (!Server.isStopRequestReceived) {
Thread.sleep(1000)
}
} finally {
Server.stop()
syncComposeClock.join()
}
server.run()

assertEquals(true, true) // Keep Codacy happy
}

class EmptyTestRule : TestRule {
override fun apply(base: Statement, description: Description): Statement = base
}

companion object {
lateinit var composeTestRule: AndroidComposeTestRule<*, *>
val context = DriverContext()
const val ANDROID_ASYNC_WAIT_TIME_MS = 10L
}
}
15 changes: 6 additions & 9 deletions espresso-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
extra.apply {
set("appiumKotlin", properties.getOrDefault("appiumKotlin", "1.8.10"))
set(
"appiumAndroidGradlePlugin",
properties.getOrDefault("appiumAndroidGradlePlugin", "8.5.0")
)
}
val appiumKotlin: String by project
val appiumAndroidGradlePlugin: String by project

repositories {
google()
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.extra["appiumKotlin"]}")
classpath("com.android.tools.build:gradle:${rootProject.extra["appiumAndroidGradlePlugin"]}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$appiumKotlin")
classpath("com.android.tools.build:gradle:$appiumAndroidGradlePlugin")

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle.kts files
Expand All @@ -27,6 +22,8 @@ allprojects {
google()
mavenCentral()
}

group = "io.appium.espressoserver"
}

tasks.register("clean", Delete::class) {
Expand Down
22 changes: 22 additions & 0 deletions espresso-server/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,25 @@ org.gradle.vfs.watch=true
org.gradle.caching=true
# https://youtrack.jetbrains.com/issue/KT-46708
kotlin.stdlib.default.dependency=false

appiumCompileSdk=34
appiumMinSdk=21
appiumTargetSdk=34
appiumBuildTools=34.0.0
appiumTargetPackage=io.appium.espressoserver
appiumSourceCompatibility=VERSION_1_8
appiumTargetCompatibility=VERSION_1_8
appiumJvmTarget=1.8

appiumAndroidGradlePlugin=8.5.0
appiumKotlin=1.8.10
appiumAndroidxTestVersion=1.5.0
appiumAnnotationVersion=1.6.0
appiumComposeVersion=1.1.1
appiumGsonVersion=2.10.1
appiumEspressoVersion=3.5.1
appiumMockitoVersion=5.1.1
appiumNanohttpdVersion=2.3.1
appiumRobolectricVersion=4.9.2
appiumJUnitVersion=4.13.2
appiumUiAutomatorVersion=2.2.0
Loading
Loading