Skip to content

Commit 12cb96e

Browse files
brettchabotcopybara-androidxtest
authored andcommitted
Fix flaky EspressoIdleTest#onIdle_backgroundLooper_with_TestLooperManager.
Previously the TestLooperManager could be released while interrogation was occurring. With this change, LooperIdlingResourceInterrogationHandler will release the TestLooperManager on the Looper thread instead of immediately. PiperOrigin-RevId: 750706298
1 parent 53a1488 commit 12cb96e

File tree

2 files changed

+25
-17
lines changed

2 files changed

+25
-17
lines changed

espresso/core/java/androidx/test/espresso/base/LooperIdlingResourceInterrogationHandler.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public boolean barrierUp() {
7070
private volatile boolean started = false;
7171
private volatile Looper looper = null;
7272
private volatile boolean idle = true;
73+
private volatile boolean releasing = false;
7374

7475
private volatile TestLooperManagerCompat testLooperManager = null;
7576

@@ -101,7 +102,11 @@ public void run() {
101102
ir.looper = Looper.myLooper();
102103
ir.testLooperManager = TestLooperManagerCompat.acquire(ir.looper);
103104
ir.started = true;
104-
new Interrogator().loopAndInterrogate(ir.testLooperManager, ir);
105+
try {
106+
new Interrogator().loopAndInterrogate(ir.testLooperManager, ir);
107+
} finally {
108+
ir.testLooperManager.release();
109+
}
105110
}
106111
});
107112

@@ -118,41 +123,37 @@ public String getMessage() {
118123

119124
@Override
120125
public void quitting() {
121-
if (testLooperManager != null) {
122-
testLooperManager.release();
123-
testLooperManager = null;
124-
}
125-
transitionToIdle();
126+
releasing = true;
126127
}
127128

128129
@Override
129130
public boolean queueEmpty() {
130131
transitionToIdle();
131-
return true;
132+
return !releasing;
132133
}
133134

134135
@Override
135136
public boolean taskDueLong() {
136137
transitionToIdle();
137-
return true;
138+
return !releasing;
138139
}
139140

140141
@Override
141142
public boolean beforeTaskDispatch() {
142143
idle = false;
143-
return true;
144+
return !releasing;
144145
}
145146

146147
@Override
147148
public boolean taskDueSoon() {
148149
idle = false;
149-
return true;
150+
return !releasing;
150151
}
151152

152153
@Override
153154
public boolean barrierUp() {
154155
idle = false;
155-
return true;
156+
return !releasing;
156157
}
157158

158159
@Override
@@ -162,7 +163,7 @@ public Void get() {
162163

163164
@Override
164165
public boolean isIdleNow() {
165-
if (!started) {
166+
if (!started || releasing) {
166167
return false;
167168
}
168169
if (idle) {
@@ -193,9 +194,8 @@ private void transitionToIdle() {
193194
}
194195

195196
public void release() {
196-
if (testLooperManager != null) {
197-
testLooperManager.release();
198-
testLooperManager = null;
199-
}
197+
releasing = true;
198+
// post a message to looper to wake up interrogator if necessary
199+
new Handler(looper).post(() -> {});
200200
}
201201
}

espresso/core/javatests/androidx/test/espresso/base/EspressoIdleTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import androidx.test.espresso.Espresso;
1414
import androidx.test.espresso.IdlingRegistry;
1515
import androidx.test.ext.junit.runners.AndroidJUnit4;
16+
import java.util.concurrent.CountDownLatch;
1617
import java.util.concurrent.atomic.AtomicBoolean;
1718
import org.junit.Test;
1819
import org.junit.runner.RunWith;
@@ -92,14 +93,21 @@ public void onIdle_backgroundLooper_with_TestLooperManager() throws InterruptedE
9293
Looper looper = ht.getLooper();
9394

9495
IdlingRegistry.getInstance().registerLooperAsIdlingResource(looper);
96+
Espresso.onIdle();
9597
AtomicBoolean wasRun = new AtomicBoolean(false);
9698
new Handler(looper).post(() -> wasRun.set(true));
9799
Espresso.onIdle();
98100
assertThat(wasRun.get()).isTrue();
99-
IdlingRegistry.getInstance().unregisterLooperAsIdlingResource(looper);
100101

102+
IdlingRegistry.getInstance().unregisterLooperAsIdlingResource(looper);
101103
Espresso.onIdle();
102104

105+
// The Looper IdlingResource releases its TestLooperManager asynchronously on
106+
// the Looper thread. Post and wait for the Looper thread to clear
107+
CountDownLatch latch = new CountDownLatch(1);
108+
new Handler(looper).post(() -> latch.countDown());
109+
latch.await();
110+
103111
TestLooperManager manager = getInstrumentation().acquireLooperManager(looper);
104112
assertThat(manager).isNotNull();
105113
manager.release();

0 commit comments

Comments
 (0)