Skip to content

Commit

Permalink
Add super trivial smoke test
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinnerbone committed Mar 26, 2024
1 parent 77a5453 commit 8022726
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 106 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3

- name: Test
run: ./gradlew pixel3api34DebugAndroidTest

- name: Decode keystore
if: ${{ !github.event.pull_request.head.repo.fork }}
env:
Expand Down
17 changes: 17 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("UnstableApiUsage")

import com.github.willir.rust.CargoNdkBuildTask

plugins {
Expand Down Expand Up @@ -91,6 +93,18 @@ android {
isUniversalApk = true
}
}

testOptions {
managedDevices {
localDevices {
create("pixel3api34") {
device = "Pixel 3"
apiLevel = 34
systemImageSource = "aosp"
}
}
}
}
}

dependencies {
Expand All @@ -109,6 +123,9 @@ dependencies {
implementation(libs.androidx.games.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.appcompat)
androidTestImplementation(libs.androidx.uiautomator)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.androidx.test.rules)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
12 changes: 12 additions & 0 deletions app/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="28" />

<instrumentation android:targetPackage="rs.ruffle"
android:name="androidx.test.runner.AndroidJUnitRunner"/>

<application tools:replace="label" android:label="SmokeTest" />
</manifest>
22 changes: 0 additions & 22 deletions app/src/androidTest/java/rs/ruffle/ExampleInstrumentedTest.kt

This file was deleted.

82 changes: 82 additions & 0 deletions app/src/androidTest/java/rs/ruffle/SmokeTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package rs.ruffle

import android.R
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.matcher.ViewMatchers.assertThat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import java.io.File
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.notNullValue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

private const val BASIC_SAMPLE_PACKAGE = "rs.ruffle"
private const val LAUNCH_TIMEOUT = 5000L

@RunWith(AndroidJUnit4::class)
class SmokeTest {
private lateinit var device: UiDevice
private lateinit var traceOutput: File
private lateinit var swfFile: File

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

// Start from the home screen
device.pressHome()

// Wait for launcher
val launcherPackage: String = device.launcherPackageName
assertThat(launcherPackage, notNullValue())
device.wait(
Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT
)

// Launch the app
val context = ApplicationProvider.getApplicationContext<Context>()
traceOutput = File.createTempFile("trace", ".txt", context.cacheDir)
swfFile = File.createTempFile("movie", ".swf", context.cacheDir)
val inStream = InstrumentationRegistry.getInstrumentation().context.resources.openRawResource(
rs.ruffle.test.R.raw.helloflash
)
val bytes = inStream.readBytes()
swfFile.writeBytes(bytes)
val intent = context.packageManager.getLaunchIntentForPackage(
BASIC_SAMPLE_PACKAGE
)?.apply {
component = ComponentName("rs.ruffle", "rs.ruffle.PlayerActivity")
data = Uri.fromFile(swfFile)
putExtra("traceOutput", traceOutput.absolutePath)
// Clear out any previous instances
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
context.startActivity(intent)

// Wait for the app to appear
device.wait(
Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
LAUNCH_TIMEOUT
)
}

@Test
fun emulatorRunsASwf() {
device.waitForWindowUpdate(null, 1000)
assertThat(device, notNullValue())

val trace = traceOutput.readLines()
assertThat(trace, equalTo(listOf("Hello from Flash!")))
}
}
Binary file added app/src/androidTest/res/raw/helloflash.swf
Binary file not shown.
7 changes: 7 additions & 0 deletions app/src/main/java/rs/ruffle/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ class PlayerActivity : GameActivity() {
return intent.dataString
}

@Suppress("unused")
// Used by Rust
private val traceOutput: String?
get() {
return intent.getStringExtra("traceOutput")
}

@Suppress("unused")
// Used by Rust
private fun navigateToUrl(url: String?) {
Expand Down
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ gamesActivity = "2.0.2" # Needs to be in sync with android-activity crate
constraintlayout = "2.1.4"
appcompat = "1.6.1"
ktlint = "12.1.0"
uiautomator = "2.3.0"
androidXTestRunner = "1.5.2"
androidXTestRules = "1.5.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand All @@ -37,6 +40,9 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio
androidx-games-activity = { group = "androidx.games", name = "games-activity", version.ref = "gamesActivity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidXTestRunner" }
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidXTestRules" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
Expand Down
22 changes: 22 additions & 0 deletions src/java.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use jni::objects::{
use jni::signature::{Primitive, ReturnType};
use jni::JNIEnv;
use ruffle_core::ContextMenuItem;
use std::path::PathBuf;
use std::sync::OnceLock;

/// Handles to various items on the Java `PlayerActivity` class.
Expand All @@ -16,6 +17,7 @@ pub struct JavaInterface {
show_context_menu: JMethodID,
get_swf_bytes: JMethodID,
get_swf_uri: JMethodID,
get_trace_output: JMethodID,
get_loc_on_screen: JMethodID,
}

Expand Down Expand Up @@ -109,6 +111,23 @@ impl JavaInterface {
url
}

pub fn get_trace_output(env: &mut JNIEnv, this: &JObject) -> Option<PathBuf> {
let result = unsafe {
env.call_method_unchecked(this, Self::get().get_trace_output, ReturnType::Object, &[])
};
let object = result
.expect("getTraceOutput() must never throw")
.l()
.unwrap();
if object.is_null() {
return None;
}
let string_object = JString::from(object);
let java_string = unsafe { env.get_string_unchecked(&string_object) };
let url = java_string.unwrap().to_string_lossy().to_string();
Some(url.into())
}

pub fn get_loc_on_screen(env: &mut JNIEnv, this: &JObject) -> (i32, i32) {
let result = unsafe {
env.call_method_unchecked(this, Self::get().get_loc_on_screen, ReturnType::Array, &[])
Expand Down Expand Up @@ -150,6 +169,9 @@ impl JavaInterface {
get_swf_uri: env
.get_method_id(class, "getSwfUri", "()Ljava/lang/String;")
.expect("getSwfUri must exist"),
get_trace_output: env
.get_method_id(class, "getTraceOutput", "()Ljava/lang/String;")
.expect("getTraceOutput must exist"),
get_loc_on_screen: env
.get_method_id(class, "getLocOnScreen", "()[I")
.expect("getLocOnScreen must exist"),
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod java;
mod keycodes;
mod navigator;
mod task;
mod trace;

use custom_event::RuffleEvent;

Expand Down Expand Up @@ -41,6 +42,7 @@ use ruffle_core::{
};

use crate::keycodes::android_keycode_to_ruffle;
use crate::trace::FileLogBackend;
use java::JavaInterface;
use ruffle_render_wgpu::{backend::WgpuRenderBackend, target::SwapChainTarget};

Expand Down Expand Up @@ -78,14 +80,14 @@ fn run(app: AndroidApp) {
};

log::info!("Starting event loop...");
let trace_output;

unsafe {
let vm = JavaVM::from_raw(app.vm_as_ptr() as *mut sys::JavaVM).expect("JVM must exist");
let activity = JObject::from_raw(app.activity_as_ptr() as jobject);
let _ = vm
.get_env()
.unwrap()
.set_rust_field(activity, "eventLoopHandle", sender.clone());
let mut jni_env = vm.get_env().unwrap();
trace_output = JavaInterface::get_trace_output(&mut jni_env, &activity);
let _ = jni_env.set_rust_field(activity, "eventLoopHandle", sender.clone());
}

while !quit {
Expand Down Expand Up @@ -222,6 +224,7 @@ fn run(app: AndroidApp) {
.with_audio(AAudioAudioBackend::new().unwrap())
.with_storage(MemoryStorageBackend::default())
.with_navigator(navigator)
.with_log(FileLogBackend::new(trace_output.as_deref()))
.with_video(
ruffle_video_software::backend::SoftwareVideoBackend::new(),
)
Expand Down
Loading

0 comments on commit 8022726

Please sign in to comment.