Эспрессо замерзает при просмотре с зацикленной анимацией

У меня есть представление, в котором элемент анимируется следующим образом в бесконечном цикле:

<translate
    android:fromXDelta="0%"
    android:toXDelta="100%"
    android:duration="10000"
    android:repeatCount="-1"
    android:repeatMode="reverse"/>

Когда Espresso открывает мою активность, она может выполнять некоторые операции, но быстро зависает. Я предполагаю, что Espresso ожидает, пока поток пользовательского интерфейса станет бездействующим, чего в этом случае никогда не происходит.

Единственный способ протестировать это представление для реализации механизма отключения анимации? У меня мог бы быть другой класс, обрабатывающий анимацию, который можно было бы высмеивать для тестов. Или построить условные обозначения времени.

Изменить: мне еще предстоит создать пример проекта, чтобы попытаться воссоздать проблему, но пока вот некоторые дополнительные подробности:

1) Я использую ActivityRule Джейка Уортона для автоматического запуска моей активности (https://gist.github.com/JakeWharton/1c2f2cadab2ddd97f9fb).

2) Это мой тест:

onView(withId(R.id.btn_yes)).perform(click());

3) Вот полная трассировка стека. Обратите внимание на AppNotIdleException:

Running tests
Test running started
android.support.test.espresso.PerformException: Error performing 'single click' on view 'with id: com.myapp:id/btn_yes'.
at android.support.test.espresso.PerformException$Builder.build(PerformException.java:83)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:70)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:53)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:185)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
at com.myapp.espresso.MyActivityTest.yesButtonTest(MyActivityTest.java:53)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at com.myapp.espresso.ActivityRule$2.evaluate(ActivityRule.java:129)
at org.junit.rules.RunRules.evaluate(RunRules.java:18)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)
Caused by: android.support.test.espresso.AppNotIdleException: Looped for 3580 iterations over 60 SECONDS. The following Idle Conditions failed .
at android.support.test.espresso.IdlingPolicy.handleTimeout(IdlingPolicy.java:61)
at android.support.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:471)
at android.support.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:402)
at android.support.test.espresso.base.UiControllerImpl.injectMotionEvent(UiControllerImpl.java:226)
at android.support.test.espresso.action.MotionEvents.sendDown(MotionEvents.java:78)
at android.support.test.espresso.action.Tap.sendSingleTap(Tap.java:133)
at android.support.test.espresso.action.Tap.access$100(Tap.java:35)
at android.support.test.espresso.action.Tap$1.sendTap(Tap.java:40)
at android.support.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:98)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:144)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

4) Трассировки стека при приостановке выполнения теста, когда он завис:

Тема №1:

"Instr: android.support.test.runner.AndroidJUnitRunner@4549" prio=5 waiting
  java.lang.Thread.State: WAITING
     blocks Instr: android.support.test.runner.AndroidJUnitRunner@4549
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Thread.parkFor(Thread.java:1220)
      - locked <0x13a3> (a java.lang.Object)
      at sun.misc.Unsafe.park(Unsafe.java:299)
      at java.util.concurrent.locks.LockSupport.park(LockSupport.java:157)
      at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:400)
      at java.util.concurrent.FutureTask.get(FutureTask.java:162)
      at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:181)
      at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
      at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)

Тема №2:

"main@4663" prio=5 runnable
  java.lang.Thread.State: RUNNABLE
      at android.view.ThreadedRenderer.nSyncAndDrawFrame(ThreadedRenderer.java:-1)
      at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:333)
      at android.view.ViewRootImpl.draw(ViewRootImpl.java:2492)
      at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2337)
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1968)
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
      at android.view.Choreographer.doCallbacks(Choreographer.java:580)
      at android.view.Choreographer.doFrame(Choreographer.java:550)
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.support.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:461)
      at android.support.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:402)
      at android.support.test.espresso.base.UiControllerImpl.injectMotionEvent(UiControllerImpl.java:226)
      at android.support.test.espresso.action.MotionEvents.sendDown(MotionEvents.java:78)
      at android.support.test.espresso.action.Tap.sendSingleTap(Tap.java:133)
      at android.support.test.espresso.action.Tap.access$100(Tap.java:35)
      at android.support.test.espresso.action.Tap$1.sendTap(Tap.java:40)
      at android.support.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:98)
      at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:144)
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:135)
      at android.app.ActivityThread.main(ActivityThread.java:5221)
      at java.lang.reflect.Method.invoke(Method.java:-1)
      at java.lang.reflect.Method.invoke(Method.java:372)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

person siger    schedule 09.04.2015    source источник
comment
Насколько я понимаю, MessageQueue считает себя бездействующим, если сообщения не готовы прямо сейчас, поэтому бесконечная анимация не должна этому препятствовать. Я бы профилировал ваше приложение с помощью инструмента monitor, чтобы увидеть, что еще происходит в основном потоке. Может быть, какой-то плохой код постоянно и без задержек планирует себя.   -  person Daniel Lubarov    schedule 13.04.2015
comment
Поток пользовательского интерфейса считается бездействующим, если его очередь сообщений пуста или его getWhen() - now() меньше 17 мс. Таким образом, анимация заблокирует поток пользовательского интерфейса. Тем не менее, то, что вы описываете, не соответствует обычным симптомам. Если поток пользовательского интерфейса не простаивает в течение 60 секунд, Espresso не пройдет тест с сообщением об этом исключении. @mieroy: Вы так долго ждали? Если вы запустите тест в режиме отладки и прервете выполнение в отладчике, какую трассировку стека вы получите для потока выполнения теста и для основного потока вашего приложения?   -  person haffax    schedule 14.04.2015
comment
@haffax, я добавил к моему вопросу 3 трассировки стека: тестовый бегун и основные потоки во время выполнения (пункт № 4) и ошибки после 60 с (пункт № 3). Должен признаться, я плохо понимаю, как планируются события анимации и как работает механизм обнаружения бездействия.   -  person siger    schedule 15.04.2015


Ответы (1)


Да, проблема с тестом связана с анимацией.

Единственное решение, которое я могу придумать, это действительно отключить бесконечную анимацию.

Как правило, рекомендуется отключать анимацию при выполнении функциональных тестов, так как она всегда приводит к некоторой нестабильности.

Небольшая предыстория:

Поток пользовательского интерфейса никогда не простаивает с точки зрения Espresso, потому что очередь сообщений всегда содержит событие, обработка которого запланирована ближе, чем пороговое значение, используемое для определения того, простаивает ли поток.

Когда вы смотрите на QueueInterrogator, вы можете видеть, что determineQueueState() возвращает TASK_DUE_SOON, когда очередь сообщений содержит событие, запланированное для обработки менее чем через 16 мс. UiController продолжит выполнение только тогда, когда будут выполнены все условия простоя, в случае QueueInterrogator это только в том случае, если очередь сообщений пуста или следующее сообщение запланировано для обработки через 16 мс или позже.

Анимации сделают недействительными View, которые они преобразуют, и это вызовет новый обход иерархии представлений с помощью Choreographer. И это триггерное сообщение, поступающее от ViewRoot к Choreographer, не позволяет вашей очереди сообщений потока пользовательского интерфейса простаивать.

person haffax    schedule 15.04.2015
comment
Спасибо за подробное расследование. Я пришел к выводу, что это правда ;) Интересно, что смена анимаций с использованием Animation framework на Animator избавила от проблемы. Теперь у меня есть обратная проблема, когда я не могу заставить Espresso дождаться завершения некоторых других (конечных) анимаций, прежде чем продолжить оценки :) В конечном итоге мне все равно придется реализовать способ отключения анимации для (Espresso) тестов. Спасибо! - person siger; 17.04.2015