It’s more than 4 years since we launched the initial variant of emulator.wtf. Over this time we’ve made a ton of improvements to the system - initially we didn’t even have any way to view test results on the emulator.wtf web app or a way for users to sign up!
We think a 1.0 version of everything is long overdue. Initially we wanted to have the 0.x versions as an escape hatch to make breaking changes to either the CLI or integration interfaces. The interface has now stabilized enough that we’re happy to call it 1.0. There’s also a feeling of “pre-production” associated with a 0.x version which just isn’t true anymore.
The 1.0 version also brings us to an opportunity to make a few breaking changes in all our integrations. We’re shipping 1.0 release candidate versions with them over the next few days. For details, see below.
Breaking changes in 1.0
Video recording is now enabled by default
Video capture is immensely helpful when debugging UI tests. As emulator.wtf
does all the capturing and encoding outside the emulator process there’s really
no performance hit either. Starting from 1.0.0-rc01
video recording will
be on by default in all our integrations.
If you do not want to capture video, you can still disable the recording by explictly disabling it using one of the options below:
--no-record-video
(CLI)recordVideo.set(false)
(Gradle)record-video: false
(GitHub actions)
GPU acceleration is now enabled by default
We did measurements when we launched GPU-acceleration a bit over two years ago. Results were very clear: in all cases there’s a substantial improvement in performance, even when the tests do not have any UI component to them. This is due to rendering being offloaded from cpu so we can allocate more cores to the emulator.
We were initially worried about GPU acceleration stability, but we’ve found the GPU-accelerated emulators are actually more stable when compared to the software emulated GL stack of swiftshader. Turning on GPU-acceleration by default was a no-brainer for us.
In rare cases you may still want use swiftshader, for example when your goal is pixel perfect screenshots between various platforms. In that case, you can always switch to swiftshader by:
- adding
gpu=software
in ew-cli or GitHub Actions - specifying
GpuMode.SOFTWARE
when using the Gradle plugin
Default device is now model=Pixel7,version=30
The current default of model=Pixel2,version=27
doesn’t really reflect a common
Android device form factor any more and API 27 might even be lower than some app’s
minSdkVersion
. Starting from 1.0.0-rc01 the new default is
model=Pixel7,version=30
in ew-cli, the emulator.wtf Gradle plugin and GitHub
actions.
This does not mean the Pixel2 or version 27 emulators are unavailable though! You can still go all the way back to api 21 if your tests require it.
Fixed pulled directories containing a double path when downloaded
When asking to pull /sdcard/Download/foo
directory (with a file bar.txt
),
the local pulled file had a path of sdcard/Download/foo/foo/bar.txt
(note
the double-foo). This has been fixed in 1.0 and the path is now
sdcard/Download/foo/bar.txt
as expected. This sounds like a small thing,
but we didn’t want to break your workflow.
Using GITHUB_TOKEN
in place of emulator.wtf api token is no longer supported
Using the GITHUB_TOKEN
has always been a bit of a hack and is going away.
Fret not, however, as there’s a good replacement available - emulator.wtf now
supports using GitHub OIDC tokens for authentication. It has all the same
benefits that the old tokenless invoke had - you don’t need to specify any
API tokens or have to worry about rotation. As an added bonus you can control
access on a per-repository basis, give access to multiple GitHub organizations
at the same time and you don’t need to install the emulator.wtf GitHub app
for OIDC tokens to work.
Read more about OIDC authentication here.
Slight changes parsing --test-targets
and --test-targets-for-shard
arguments
We’ve made a few improvements to how --test-targets
and
--test-targets-for-shard
behave. Most notably using whitespace as a statement
separator no longer works.
# old
--test-targets "class com.example.One annotation com.example.MyAnnotation"
# new
--test-targets "class com.example.One,annotation com.example.MyAnnotation"
We’ve also merged the parsing rules between --test-targets
and
--test-targets-for-shard
so they’ll behave exactly the same. We, uh, have
no excuse why they didn’t in the first place :).
There are also new test target types you can specify, we finally added support
for both regex
and testFile
. Note that testFile
uses the local path
of your machine / CI job.
Gradle plugin now requires Java 17 and Android Gradle Plugin 8.1.0 or later
We’re dropping support for Android Gradle Plugin (AGP) versions 8.x or older to simplify delivering new features in the emulator.wtf Gradle Plugin. As AGP 8.1.x requires Java 17 so do we.
Gradle plugin uses type-safe APIs instead of maps
The Gradle plugin has seen some DSL improvements and we’ve swapped some
Map
/ String
types for fully typed equivalents for easier use and IDE
autocompletion.
here’s a device {}
DSL for specifying devices, which looks much clearer when
using Gradle scripts in Kotlin:
// old
devices = listOf(
mapOf(
"model" to "NexusLowResAtd",
"version" to "30"
),
mapOf(
"model" to "Pixel2",
"version" to "23"
)
)
// new
device {
model = DeviceModel.NEXUS_LOW_RES_ATD
version = 30
}
device {
model = DeviceModel.PIXEL_2
version = 23
}
Test-targets have gotten a similar makeover:
// old
testTargets = "class foo.bar.Baz"
// new
targets {
testClass("foo.bar.Baz")
}
And last, but not least, flakyTestRepeatMode
now takes a proper enum:
// Whether to reattempt full shards (ALL) or only failed tests (FAILED_ONLY)
// in case of test failures. Defaults to FAILED_ONLY.
flakyTestRepeatMode = FlakyRepeatMode.FAILED_ONLY
Gradle plugin automatically adds the emulator.wtf test-runtime-android dependency
The emulator.wtf Gradle plugin now automatically adds the test-runtime-android dependency. This will give you per-test video captures by default.
If, for some reason, you want to disable this behaviour you can control this
behaviour with the wtf.emulator.addruntimedependency=false
Gradle property
in your project or global gradle.properties
file.
All emulator.wtf GitHub actions now use the Node 24 runtime
The GitHub actions have been updated to use the Node 24 runtime. This should only affect you if you’re using self-hosted runners, in which case make sure your GHA runner is upgraded to v2.327.1 or later.
New features
Gradle-managed devices support in the emulator.wtf Gradle plugin
The gradle plugin 1.0.0-rc01 and later support backing Gradle-managed devices
with emulator.wtf emulators. This is a great way to quickly plug emulator.wtf
into your existing GMD-based test infrastructure. When running tests, the
configuration options from the emulatorwtf {}
block are still picked up.
Example of registering a Gradle-managed device backed by emulator.wtf:
import wtf.emulator.ewDevices
import wtf.emulator.DeviceModel
android {
testOptions.managedDevices.ewDevices {
register("ewPixel7api33") {
device = DeviceModel.PIXEL_7
apiLevel = 33
}
}
}
Baseline profiles in the emulator.wtf Gradle plugin
Using Gradle-managed devices opens up the door to generate baseline profiles with emulator.wtf emulators. This way you can improve your CI job times and stability by eliminating any use of local emulators altogether.
Expanding on the GMD example above, you can register emulator.wtf devices for use with the baseline plugin:
import wtf.emulator.ewDevices
import wtf.emulator.DeviceModel
android {
testOptions.managedDevices.ewDevices {
register("ewPixel7api33") {
device = DeviceModel.PIXEL_7
apiLevel = 33
}
}
}
baselineProfile {
managedDevices += "ewPixel7api33"
useConnectedDevices = false
}
ew-cli
improvements
A few smaller ew-cli
improvements coming with 1.0 and worth a separate
mention:
When using ew-cli
in machine-readable --json
mode with other tooling
you get a new runResultsSummary
field in the output JSON object. The
field contains useful things for quick CI reporting like test counts and
first encountered failure details (test method, stacktrace, etc).
You can finally customize the separator in comma-separated value arguments
in ew-cli
by prefixing the value with ^SEP^
, e.g. ^;^
to use the ;
as a separator instead of the default ,
.
For example if you wanted to pass in foo=ab,cd
and bar=baz
as test
runner arguments you finally can! See the example below.
ew-cli --environment-variables '^;^foo=ab,cd;bar=baz' ...
Other recent improvements
Some recent improvements from the past few years which you might’ve missed if you’ve been with us for a while:
- Develocity reporting from the Gradle plugin. If you use Develocity you can
wire up reporting emulator.wtf test results to Develocity by adding
testReporters = [TestReporter.DEVELOCITY]
to youremulatorwtf {}
configuration block in Gradle - Targeted runtime sharding - the easiest way to manage sharding within your tests. You indicate the target runtime and emulator.wtf will figure out both how many shards are needed and assign tests to those shards based on historical runtime. Read more about targeted runtime sharding here.
- Per-test videos - by including the test-runtime-android dependency you can get frame-perfect video captures of individual tests.
Pinning to a specific emulator.wtf version
The ew-cli
script always fetches the latest available version before running
your tests. This is great for ad-hoc command-line usage but not so great for
running tests in CI, a breaking change on our side could start failing your
builds out of the blue, a bad day for everyone.
Similarly, you might want to try out release candidate versions or hold off on upgrading to 1.x until you’ve dealt with the breaking changes above (although we really recommend updating!).
Here’s how you can pin emulator.wtf to a specific version:
CLI: use the
EW_VERSION
environment variable to pin to a specific version.You can set
EW_VERSION=1.0.0-rc01
to try out the release candidate:EW_VERSION=1.0.0-rc01 ew-cli --app ... --test ...
Gradle: the
wtf.emulator.gradle
plugin always uses a fixed CLI version, no need to do anything. To try out1.0.0-rc01
with the breaking changes, upgrade the Gradle plugin to1.0.0-rc01
or later.GitHub Actions: if you’re using any of our GitHub Actions then you can either depend on a major version tag like
v0
or a specific exact version likev0.9.10
. The specific versions will use a fixedew-cli
version to run the tests. For trying out the release candidate, use thev1-rc
tag.
What’s cooking?
Last but not least there are two bigger features coming that are not fully baked yet for inclusion in the 1.0 release.
Emulator sessions is a completely new mode of operation where instead of
running tests you can use ew-cli
to fire up an emulator and get a direct
adb connection to it. It works with tools like scrcpy
as well so you can get
a live interactable version of our emulators. This is great to debug tests
locally, but we have more interesting usecases in mind. Stay tuned!
Egress tunnel is a networking mode where any egress traffic from the
emulator is diverted to the machine running ew-cli
, whether it’s your local
development computer or the container your CI job is running in. It’s a great
way to expose backend test infrastructure for larger tests without having to
punch holes to firewalls.
Both of these features are in closed alpha today! Give us a ping at hello@emulator.wtf if you’d like to give them a go.