Skip to content

Commit cf54d26

Browse files
Staacksclaude
andcommitted
The save test survives the collection rebuilding its list under the tap.
The phone job of the first T1 run after the container suites landed failed with a StaleObjectException on the entry it had just found: the collection reloads its list in onResume, so an object looked up a moment earlier is gone by the time it is clicked, and a tap landing during the rebuild is swallowed without opening anything. The tablet job and the local runs happened to win the race. So neither half is done once any more. Finding and tapping the entry is retried until the experiment is actually open, with the deadline deciding the message: an entry that never shows up says the collection does not list it, one that shows up but never opens says the tap did not take. The opened activity is recognised by identity rather than by title, because reopening a saved experiment gives it the same title as the one just closed. Three runs in a row on the phone profile, 28 to 32 seconds each. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9b44010 commit cf54d26

1 file changed

Lines changed: 73 additions & 20 deletions

File tree

app/src/androidTest/java/de/rwth_aachen/phyphox/SaveToCollectionTest.java

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import androidx.test.uiautomator.UiDevice;
2020
import androidx.test.uiautomator.UiObject2;
2121
import androidx.test.uiautomator.UiObjectNotFoundException;
22+
import androidx.test.uiautomator.StaleObjectException;
2223
import androidx.test.uiautomator.UiScrollable;
2324
import androidx.test.uiautomator.UiSelector;
2425
import androidx.test.uiautomator.Until;
@@ -176,33 +177,85 @@ private List<File> awaitNewExperiments(int expected) {
176177
+ expected + ": " + added);
177178
}
178179

179-
//The collection as the user reaches it, with the experiment closed: a save that writes the
180-
//file but never registers or refreshes the entry has to fail here, which is why this does not
181-
//look at the directory or open anything by intent.
182-
private UiObject2 findInCollection(String title) throws Exception {
183-
FixtureExperiment.close(FixtureExperiment.activity());
180+
//Closes whatever experiment is open and returns to the collection, waiting until it is
181+
//really gone - otherwise the next lookup would find the experiment that is still on screen.
182+
private Experiment backToCollection() {
183+
Experiment open = FixtureExperiment.activity();
184+
FixtureExperiment.close(open);
185+
long deadline = System.currentTimeMillis() + 10000;
186+
while (FixtureExperiment.activity() != null && System.currentTimeMillis() < deadline)
187+
settle(100);
184188
FixtureExperiment.bringToForeground();
189+
return open;
190+
}
191+
192+
//Opens a saved experiment the way the user does: find its entry in the collection and tap it.
193+
//A save that writes the file but never registers or refreshes the entry fails here, which is
194+
//the point of going through the list rather than opening anything by intent.
195+
//
196+
//Both halves have to be retried rather than done once. The collection rebuilds its list in
197+
//onResume, so an entry found a moment earlier can go stale under the tap (that is a
198+
//StaleObjectException, and it is what made this flaky in CI), and a tap that lands during the
199+
//rebuild is swallowed without opening anything.
200+
private Experiment openFromCollection(String title) {
201+
FixtureExperiment.suppressHints();
202+
Experiment previous = backToCollection();
185203

186-
UiObject2 entry = device().wait(Until.findObject(By.text(title)), 10000);
187-
if (entry == null) {
204+
boolean everListed = false;
205+
long deadline = System.currentTimeMillis() + 40000;
206+
while (System.currentTimeMillis() < deadline) {
207+
UiObject2 entry = device().wait(Until.findObject(By.text(title)), 2000);
208+
if (entry == null) {
209+
scrollTowards(title);
210+
continue;
211+
}
212+
everListed = true;
188213
try {
189-
UiScrollable list = new UiScrollable(new UiSelector().scrollable(true));
190-
list.setAsVerticalList();
191-
list.scrollTextIntoView(title);
192-
} catch (UiObjectNotFoundException e) {
193-
//Nothing to scroll, or the entry is not there at all - the assertion below says so
214+
entry.click();
215+
} catch (StaleObjectException e) {
216+
continue; //the list rebuilt under the tap - look it up again
194217
}
195-
entry = device().wait(Until.findObject(By.text(title)), 5000);
218+
Experiment opened = awaitOpened(previous, 8000);
219+
if (opened != null)
220+
return opened;
221+
FixtureExperiment.bringToForeground(); //the tap did not take, try once more
196222
}
197-
assertNotNull("the collection does not list \"" + title + "\" after saving it", entry);
198-
return entry;
223+
throw new AssertionError(everListed
224+
? "tapping \"" + title + "\" in the collection never opened it"
225+
: "the collection does not list \"" + title + "\" after saving it");
199226
}
200227

201-
//Opens an entry by tapping it in the collection, the way the user does.
202-
private Experiment openFromCollection(String title) throws Exception {
203-
FixtureExperiment.suppressHints();
204-
findInCollection(title).click();
205-
return FixtureExperiment.awaitLoaded();
228+
//A loaded experiment activity that is not the one we came from, or null within the timeout.
229+
//Identity rather than title: reopening a saved experiment gives it the same title as the one
230+
//that was just closed.
231+
private Experiment awaitOpened(Experiment previous, long millis) {
232+
long deadline = System.currentTimeMillis() + millis;
233+
while (System.currentTimeMillis() < deadline) {
234+
Experiment activity = FixtureExperiment.activity();
235+
if (activity != null && activity != previous
236+
&& activity.experiment != null && activity.experiment.loaded)
237+
return activity;
238+
settle(100);
239+
}
240+
return null;
241+
}
242+
243+
private void scrollTowards(String title) {
244+
try {
245+
UiScrollable list = new UiScrollable(new UiSelector().scrollable(true));
246+
list.setAsVerticalList();
247+
list.scrollTextIntoView(title);
248+
} catch (UiObjectNotFoundException e) {
249+
//Nothing to scroll, or the entry is not there (yet) - the caller's deadline decides
250+
}
251+
}
252+
253+
private void settle(long millis) {
254+
try {
255+
Thread.sleep(millis);
256+
} catch (InterruptedException e) {
257+
Thread.currentThread().interrupt();
258+
}
206259
}
207260

208261
//The drawable an image element ended up with, or null if it has none. The fixture image is

0 commit comments

Comments
 (0)