$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Espresso, Beyond the Basics. DroidCon Vietnam
Search
Iñaki Villar
April 15, 2017
Technology
0
120
Espresso, Beyond the Basics. DroidCon Vietnam
Presentation of Espresso at Droidon Vietnam 2017
Iñaki Villar
April 15, 2017
Tweet
Share
More Decks by Iñaki Villar
See All by Iñaki Villar
Scaling Android Builds in Pandemic Times
cdsap
1
160
Building Android Projects with kts
cdsap
2
280
The Build Shrugged
cdsap
1
88
State of Testing in Kotlin
cdsap
0
270
Dexs, R8 and 3.3
cdsap
0
380
Deep Dive Work Manager
cdsap
0
350
Advanced Topics Android
cdsap
0
120
Kotlin: Server-Client
cdsap
0
100
Droidcon Dubai : Kotlin - Server - Client
cdsap
0
75
Other Decks in Technology
See All in Technology
Oracle Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
1
400
AI駆動開発ライフサイクル(AI-DLC)の始め方
ryansbcho79
0
150
日本Rubyの会: これまでとこれから
snoozer05
PRO
5
230
アプリにAIを正しく組み込むための アーキテクチャ── 国産LLMの現実と実践
kohju
0
220
フルカイテン株式会社 エンジニア向け採用資料
fullkaiten
0
9.9k
re:Invent2025 3つの Frontier Agents を紹介 / introducing-3-frontier-agents
tomoki10
0
400
20251222_サンフランシスコサバイバル術
ponponmikankan
2
140
TED_modeki_共創ラボ_20251203.pdf
iotcomjpadmin
0
140
NIKKEI Tech Talk #41: セキュア・バイ・デザインからクラウド管理を考える
sekido
PRO
0
200
Building Serverless AI Memory with Mastra × AWS
vvatanabe
0
490
AI時代のワークフロー設計〜Durable Functions / Step Functions / Strands Agents を添えて〜
yakumo
3
2.1k
Bedrock AgentCore Memoryの新機能 (Episode) を試してみた / try Bedrock AgentCore Memory Episodic functionarity
hoshi7_n
2
1.8k
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
55
12k
So, you think you're a good person
axbom
PRO
0
1.8k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
410
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
0
1k
Facilitating Awesome Meetings
lara
57
6.7k
The Cult of Friendly URLs
andyhume
79
6.7k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
680
How STYLIGHT went responsive
nonsquared
100
6k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
1
860
Six Lessons from altMBA
skipperchong
29
4.1k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
69
Mind Mapping
helmedeiros
PRO
0
39
Transcript
Espresso Beyond the basics
None
onView(withId(R.id.button_take_photo)) .perform(click()) .check(matches(isDisplayed()));
onView(withId(R.id.button_take_photo)) .perform(click()) .check(matches(isDisplayed()));
Structure
Instrumentation
Instrumentation MonitorningInstrumentation
Instrumentation MonitorningInstrumentation AndroidJunitRunner
class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage
coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage
coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage
coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
Instrumentation MonitorningInstrumentation AndroidJunitRunner class notClass size log annotation notAnnotation numShards
shardIndex delay_msec coverage coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle
class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage
coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
None
public static ViewInteraction onView(final Matcher<View> viewMatcher) { return BASE.plus(new ViewInteractionModule(viewMatcher)).viewInteraction();
}
BaseLayerComponent
BaseLayerModule UiControllerModule BaseLayerComponent
BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule
Recycler BaseLayerComponent
BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule
Recycler BaseLayerComponent
BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule
Recycler BaseLayerComponent
BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule
Recycler BaseLayerComponent UiController
BaseLayerComponent
FailureHolder BaseLayerComponent FailureHandler ActivityRootLister InteractionComp IdlingRegistry
FailureHolder BaseLayerComponent FailureHandler ActivityRootLister InteractionComp InteractionModule Matcher<Root> Matcher<View> ViewFinder View
IdlingRegistry
None
Synchronization
perform(click()); MainActivityTest
perform(click()); new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); MainActivityTest ViewActions
perform(click()); new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); MotionEvents.sendDown(uiController, coordinates, precision);
MainActivityTest ViewActions Tapper
perform(click()); new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); MotionEvents.sendDown(uiController, coordinates, precision);
uiController.injectMotionEvent(motionEvent); MainActivityTest ViewActions Tapper MotionEvents
InjectKeyEvent loopMainThreadUntilIdle
InjectKeyEvent loopMainThreadUntilIdle loopUntil Asynctask Idling
InjectKeyEvent loopMainThreadUntilIdle keyEventExecutor submit(event) loopUntil loopUntil Asynctask Idling KeyInject
@Override public void loopMainThreadUntilIdle() { do { if (!asyncTaskMonitor.isIdleNow()) {
asyncTaskMonitor.notifyWhenIdle(…); condChecks.add(IdleCondition.ASYNC_TASKS_HAVE_IDLED); } if (!compatTaskMonitor.isIdleNow()) { compatTaskMonitor.notifyWhenIdle(…); condChecks.add(IdleCondition.COMPAT_TASKS_HAVE_IDLED); } if (!idlingResourceRegistry.allResourcesAreIdle()) { idlingResourceRegistry.notifyWhenAllResourcesAreIdle(…); condChecks.add(IdleCondition.DYNAMIC_TASKS_HAVE_IDLED); } loopUntil(condChecks); } while (!sIdleNow()); } UiControllerImpl.java
@Override public void loopMainThreadUntilIdle() { do { if (!asyncTaskMonitor.isIdleNow()) {
asyncTaskMonitor.notifyWhenIdle(…); condChecks.add(IdleCondition.ASYNC_TASKS_HAVE_IDLED); } if (!compatTaskMonitor.isIdleNow()) { compatTaskMonitor.notifyWhenIdle(…); condChecks.add(IdleCondition.COMPAT_TASKS_HAVE_IDLED); } if (!idlingResourceRegistry.allResourcesAreIdle()) { idlingResourceRegistry.notifyWhenAllResourcesAreIdle(…); condChecks.add(IdleCondition.DYNAMIC_TASKS_HAVE_IDLED); } loopUntil(condChecks); } while (!sIdleNow()); } UiControllerImpl.java
private final AtomicReference<IdleMonitor> monitor = new AtomicReference<IdleMonitor>(null); private final ThreadPoolExecutor
pool; private final AtomicInteger activeBarrierChecks = new AtomicInteger(0); AsyncTaskPoolMonitor.java
boolean isIdleNow() { if (!pool.getQueue().isEmpty()) { return false; } else
{ int activeCount = pool.getActiveCount(); if (0 != activeCount) { if (monitor.get() == null) { activeCount = activeCount - activeBarrierChecks.get(); } } return 0 == activeCount; } } private final AtomicReference<IdleMonitor> monitor = new AtomicReference<IdleMonitor>(null); private final ThreadPoolExecutor pool; private final AtomicInteger activeBarrierChecks = new AtomicInteger(0); AsyncTaskPoolMonitor.java
IdlingResourceRegistry.java
public boolean registerResources(final List resourceList) { } public boolean unregisterResources(final
List resourceList) { } IdlingResourceRegistry.java
public boolean registerResources(final List resourceList) { } public boolean unregisterResources(final
List resourceList) { } IdlingResourceRegistry.java boolean allResourcesAreIdle() { for (int i = idleState.nextSetBit(0); i >= 0 && i < resources.size(); i = idleState.nextSetBit(i + 1)) { idleState.set(i, resources.get(i).isIdleNow()); } return idleState.cardinality() == resources.size(); }
None
Guava
abstract class WrappingExecutorService implements ExecutorService { private final ExecutorService delegate;
protected GuavaWrappingExecutorService(ExecutorService delegate) { this.delegate = checkNotNull(delegate); } protected abstract <T> Callable<T> wrapTask(Callable<T> callable); protected Runnable wrapTask(Runnable command) { final Callable<Object> wrapped = wrapTask(Executors.callable(command); return new Runnable() { @Override public void run() { wrapped.call(); } }; } @Override public final void execute(Runnable command) { delegate.execute(wrapTask(command)); } } Guava
abstract class WrappingExecutorService implements ExecutorService { private final ExecutorService delegate;
protected GuavaWrappingExecutorService(ExecutorService delegate) { this.delegate = checkNotNull(delegate); } protected abstract <T> Callable<T> wrapTask(Callable<T> callable); protected Runnable wrapTask(Runnable command) { final Callable<Object> wrapped = wrapTask(Executors.callable(command); return new Runnable() { @Override public void run() { wrapped.call(); } }; } @Override public final void execute(Runnable command) { delegate.execute(wrapTask(command)); } } Guava
abstract class WrappingExecutorService implements ExecutorService { private final ExecutorService delegate;
protected GuavaWrappingExecutorService(ExecutorService delegate) { this.delegate = checkNotNull(delegate); } protected abstract <T> Callable<T> wrapTask(Callable<T> callable); protected Runnable wrapTask(Runnable command) { final Callable<Object> wrapped = wrapTask(Executors.callable(command); return new Runnable() { @Override public void run() { wrapped.call(); } }; } @Override public final void execute(Runnable command) { delegate.execute(wrapTask(command)); } } Guava
abstract class WrappingExecutorService implements ExecutorService { private final ExecutorService delegate;
protected GuavaWrappingExecutorService(ExecutorService delegate) { this.delegate = checkNotNull(delegate); } protected abstract <T> Callable<T> wrapTask(Callable<T> callable); protected Runnable wrapTask(Runnable command) { final Callable<Object> wrapped = wrapTask(Executors.callable(command); return new Runnable() { @Override public void run() { wrapped.call(); } }; } @Override public final void execute(Runnable command) { delegate.execute(wrapTask(command)); } } Guava
public class IdlingExecutorService extends GuavaWrappingExecutorService { private final CountingIdlingResource mIdlingResource;
public IdlingResourceExecutorService(ExecutorService delegate, CountingIdlingResource idling) { super(delegate); mIdlingResource = idling; }
@Override protected <T> Callable<T> wrapTask(Callable<T> callable) { return new WrappedCallable<>(callable);
} private final class WrappedCallable<T> implements Callable<T> { private final Callable<T> delegate; public WrappedCallable(Callable<T> delegate) { this.delegate = delegate; } @Override public T call() throws Exception { mIdlingResource.increment(); T call; try { call = delegate.call(); } finally { mIdlingResource.decrement(); } } }
@Override protected <T> Callable<T> wrapTask(Callable<T> callable) { return new WrappedCallable<>(callable);
} private final class WrappedCallable<T> implements Callable<T> { private final Callable<T> delegate; public WrappedCallable(Callable<T> delegate) { this.delegate = delegate; } @Override public T call() throws Exception { mIdlingResource.increment(); T call; try { call = delegate.call(); } finally { mIdlingResource.decrement(); } } }
RxJava
public class RxSchedulerHook extends RxJavaSchedulersHook { private Scheduler customScheduler;
private CountingIdlingResource idling; private static RxSchedulerHook sInstance; public RxSchedulerHook(CountingIdlingResource countingIdlingResource) { FailedTest watcher = new FailedTest(); idling = countingIdlingResource; customScheduler = new Scheduler() { @Override public Scheduler.Worker createWorker() { return new CustomWorker(); } }; } RxJava
public class RxSchedulerHook extends RxJavaSchedulersHook { private Scheduler customScheduler;
private CountingIdlingResource idling; private static RxSchedulerHook sInstance; public RxSchedulerHook(CountingIdlingResource countingIdlingResource) { FailedTest watcher = new FailedTest(); idling = countingIdlingResource; customScheduler = new Scheduler() { @Override public Scheduler.Worker createWorker() { return new CustomWorker(); } }; } RxJava
public class RxSchedulerHook extends RxJavaSchedulersHook { private Scheduler customScheduler;
private CountingIdlingResource idling; private static RxSchedulerHook sInstance; public RxSchedulerHook(CountingIdlingResource countingIdlingResource) { FailedTest watcher = new FailedTest(); idling = countingIdlingResource; customScheduler = new Scheduler() { @Override public Scheduler.Worker createWorker() { return new CustomWorker(); } }; } RxJava
@Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit
unit) { return super.schedule(new Action0() { @Override public void call() { action.call(); } }, delayTime, unit); } @Override public Subscription schedule(final Action0 action) { return super.schedule(new Action0() { @Override public void call() { idling.increment(); try { action.call(); } finally { idling.decrement(); } } }); } RxJava
@Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit
unit) { return super.schedule(new Action0() { @Override public void call() { action.call(); } }, delayTime, unit); } @Override public Subscription schedule(final Action0 action) { return super.schedule(new Action0() { @Override public void call() { idling.increment(); try { action.call(); } finally { idling.decrement(); } } }); } RxJava
@Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit
unit) { return super.schedule(new Action0() { @Override public void call() { action.call(); } }, delayTime, unit); } @Override public Subscription schedule(final Action0 action) { return super.schedule(new Action0() { @Override public void call() { idling.increment(); try { action.call(); } finally { idling.decrement(); } } }); } RxJava
RxJavaPlugins.getInstance().registerSchedulersHook(RxSchedulerHook.get()); RxJava
OkHttp3IdlingResource
@Before public void setUp() { OkHttpClient client = new OkHttpClient();
IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", client); Espresso.registerIdlingResources(resource); } OkHttp3IdlingResource
@Before public void setUp() { OkHttpClient client = new OkHttpClient();
IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", client); Espresso.registerIdlingResources(resource); } private OkHttp3IdlingResource(String name, Dispatcher dispatcher) { this.name = name; this.dispatcher = dispatcher; dispatcher.setIdleCallback(new Runnable() { public void run() { ResourceCallback callback = OkHttp3IdlingResource.this.callback; if(callback != null) { callback.onTransitionToIdle(); } } }); } public boolean isIdleNow() { return this.dispatcher.runningCallsCount() == 0; } OkHttp3IdlingResource
Rules
@Rule public ActivityTestRule<Main> activityTestRule = new ActivityTestRule<>(Main){ @Override protected void
beforeActivityLaunched() { super.beforeActivityLaunched(); } @Override protected void afterActivityLaunched() { super.afterActivityLaunched(); } @Override protected void afterActivityFinished() { super.afterActivityFinished(); } }; ActivityTestRule
@Rule public ActivityTestRule<Main> activityTestRule = new ActivityTestRule<>(Main){ @Override protected void
beforeActivityLaunched() { super.beforeActivityLaunched(); } @Override protected void afterActivityLaunched() { super.afterActivityLaunched(); } @Override protected void afterActivityFinished() { super.afterActivityFinished(); } }; ActivityTestRule
@Rule public IntentTestRule<Main> activityTestRule = new IntentTestRule<>(Main) IntentTestRule
@Rule public IntentTestRule<Main> activityTestRule = new IntentTestRule<>(Main) public
IntentsTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) { super(activityClass, initialTouchMode, launchActivity); } @Override protected void afterActivityLaunched() { Intents.init(); super.afterActivityLaunched(); } @Override protected void afterActivityFinished() { super.afterActivityFinished(); Intents.release(); } IntentTestRule
public class CustomActivityTestRule<T extends Activity> extends ActivityTestRule<T> { public
CustomActivityTestRule(Class<T> activity) { super(activity); } public CustomActivityTestRule(Class<T> activity, boolean initialTouchMode) { super(activity, initialTouchMode); } @Override protected void afterActivityLaunched() { Intents.init(); super.afterActivityLaunched(); } @Override protected void afterActivityFinished() { super.afterActivityFinished(); Intents.release(); } CustomActivityTestRule
public class CustomActivityTestRule<T extends Activity> extends ActivityTestRule<T> { public
CustomActivityTestRule(Class<T> activity) { super(activity); } public CustomActivityTestRule(Class<T> activity, boolean initialTouchMode) { super(activity, initialTouchMode); } @Override protected void afterActivityLaunched() { Intents.init(); super.afterActivityLaunched(); } @Override protected void afterActivityFinished() { super.afterActivityFinished(); Intents.release(); } CustomActivityTestRule
public interface TestRule { Statement apply(Statement base, Description description); }
TestRule
public interface TestRule { Statement apply(Statement base, Description description); }
public class UiThreadTestRule implements TestRule { private static final String LOG_TAG = "UiThreadTestRule"; @Override public Statement apply(final Statement base, Description description) { return new UiThreadStatement(base, shouldRunOnUiThread(description)); } protected boolean shouldRunOnUiThread(Description description) { return description.getAnnotation(UiThreadTest.class) != null; } } TestRule
private class ActivityStatement extends Statement { private final Statement
mBase; public ActivityStatement(Statement base) { mBase = base; } @Override public void evaluate() throws Throwable { try { if (mLaunchActivity) { act = launchActivity(getActivityIntent()); } mBase.evaluate(); } finally { finishActivity(); } } } TestRule
public class TraceTestRule implements TestRule { private Trace trace;
@Override public Statement apply(final Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { try { trace.start(); base.evaluate(); } finally { trace.end(); } } }; } } TestRule
public class TraceTestRule implements TestRule { private Trace trace;
@Override public Statement apply(final Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { try { trace.start(); base.evaluate(); } finally { trace.end(); } } }; } } @Rule public TraceTestRule traceTestRule = new TraceTestRule(getTargetContext()); TestRule TestRule
public abstract class TestWatcher implements TestRule TestWatcher
return new Statement() { @Override public void evaluate() throws Throwable
{ List<Throwable> errors = new ArrayList<Throwable>(); startingQuietly(description, errors); try { base.evaluate(); succeededQuietly(description, errors); } catch (AssumptionViolatedException e) { errors.add(e); skippedQuietly(e, description, errors); } catch (Throwable e) { errors.add(e); failedQuietly(e, description, errors); } finally { finishedQuietly(description, errors); } MultipleFailureException.assertEmpty(errors); } }; public abstract class TestWatcher implements TestRule TestWatcher
return new Statement() { @Override public void evaluate() throws Throwable
{ List<Throwable> errors = new ArrayList<Throwable>(); startingQuietly(description, errors); try { base.evaluate(); succeededQuietly(description, errors); } catch (AssumptionViolatedException e) { errors.add(e); skippedQuietly(e, description, errors); } catch (Throwable e) { errors.add(e); failedQuietly(e, description, errors); } finally { finishedQuietly(description, errors); } MultipleFailureException.assertEmpty(errors); } }; public abstract class TestWatcher implements TestRule TestWatcher
public class FailedTest extends TestWatcher { public FailedTest() {
uiAutomation = UiDevice.getInstance(); } @Override protected void failed(Throwable e, Description description) { super.failed(e, description); String fileNameBase = getFileNameWithoutExtension(description); saveScreenshot(fileNameBase); saveInfo(fileNameBase); } @Override protected void succeeded(Description description) { super.succeeded(description); } @Override protected void skipped(AssumptionViolatedException e, Description description) { super.skipped(e, description); } TestWatcher
public class FailedTest extends TestWatcher { public FailedTest() {
uiAutomation = UiDevice.getInstance(); } @Override protected void failed(Throwable e, Description description) { super.failed(e, description); String fileNameBase = getFileNameWithoutExtension(description); saveScreenshot(fileNameBase); saveInfo(fileNameBase); } @Override protected void succeeded(Description description) { super.succeeded(description); } @Override protected void skipped(AssumptionViolatedException e, Description description) { super.skipped(e, description); } TestWatcher
public class FailedTest extends TestWatcher { public FailedTest() {
uiAutomation = UiDevice.getInstance(); } @Override protected void failed(Throwable e, Description description) { super.failed(e, description); String fileNameBase = getFileNameWithoutExtension(description); saveScreenshot(fileNameBase); saveInfo(fileNameBase); } @Override protected void succeeded(Description description) { super.succeeded(description); } @Override protected void skipped(AssumptionViolatedException e, Description description) { super.skipped(e, description); } TestWatcher
public class RuleChain implements TestRule{ public RuleChain ruleChain = RuleChain.outerRule(new
LogRule("outer rule") .around(new LogRule("middle around rule") .around(new LogRule("inner around rule”)))); RuleChain
public class RuleChain implements TestRule{ public RuleChain ruleChain = RuleChain.outerRule(new
LogRule("outer rule") .around(new LogRule("middle around rule") .around(new LogRule("inner around rule”)))); RuleChain
public class RuleChain implements TestRule{ public RuleChain ruleChain = RuleChain.outerRule(new
LogRule("outer rule") .around(new LogRule("middle around rule") .around(new LogRule("inner around rule”)))); Starting outer rule Starting middle rule Starting inner rule Finishing inner rule Finishing middle rule Finishing outer rule RuleChain
public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class);
public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class); @Rule public
RuleChain chain = RuleChain.outerRule(new FailedTest() .around(activityRule);
public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class); @Rule public
RuleChain chain = RuleChain.outerRule(new FailedTest() .around(activityRule); @Rule public RuleChain chain = RuleChain.outerRule(new FailedTest()) .around(new TraceTestRule()) .around(activityRule);
public class Device { public Device() { } private
static boolean deviceEquals(String device) { return Build.DEVICE.equals(device); } public static class Genymotion implements Condition { public Genymotion() { } public boolean isSatisfied() { return Device.deviceEquals("vbox86p"); } } } AndroidTestRules
Permissions
None
@Before public void grantPhonePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getInstrumentation().getUiAutomation().executeShellCommand( "pm grant " + getTargetContext().getPackageName() + " android.permission.CALL_PHONE"); } }
afterEvaluate { tasks.each { task -> if (task.name.startsWith('connectedAndroidTest')) { task.dependsOn
grantAnimationPermissions } } }
afterEvaluate { tasks.each { task -> if (task.name.startsWith('connectedAndroidTest')) { task.dependsOn
grantAnimationPermissions } } } devices=$($adb devices | grep -v 'List of devices' | cut -f1 | grep '.') for device in $devices; do echo "Setting permissions to device" $device "for package" $package $adb -s $device shell pm grant $package android.permission.xxxx done
None
ATSL Spoon Fork TestButler Burst RxPresso AndroidTestRules
Anton Batishchev Anton Malinskiy Chiu-ki Chan Erik Hellman Friedger Müffke
Iordanis Giannakakis Jake Wharton Jose Alcérreca Michael Bailey Paul Blundell Stephan Linzner Valera Zakharov Xavi Rigau ATSL Spoon Fork TestButler Burst RxPresso AndroidTestRules
Cảm ơn
Cảm ơn Agoda đang tuyển dụng
+IñakiVillar @inyaki_mwc Cảm ơn Agoda đang tuyển dụng