Skip to content

Commit c4d3071

Browse files
baishengzcopybara-github
authored andcommitted
Internal change
PiperOrigin-RevId: 710929424
1 parent 62f5d82 commit c4d3071

File tree

2 files changed

+113
-30
lines changed

2 files changed

+113
-30
lines changed

src/java/com/google/devtools/mobileharness/platform/testbed/adhoc/controller/AdhocTestbedDriver.java

Lines changed: 112 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.devtools.mobileharness.platform.testbed.adhoc.controller;
1818

19+
import static com.google.common.collect.ImmutableList.toImmutableList;
1920
import static com.google.common.collect.Multimaps.toMultimap;
2021

2122
import com.google.common.annotations.VisibleForTesting;
@@ -32,6 +33,7 @@
3233
import com.google.devtools.mobileharness.api.model.error.MobileHarnessException;
3334
import com.google.devtools.mobileharness.api.model.proto.Test.TestResult;
3435
import com.google.devtools.mobileharness.infra.controller.test.local.annotation.DoNotSubscribeTestEvent;
36+
import com.google.devtools.mobileharness.shared.util.concurrent.Barrier;
3537
import com.google.devtools.mobileharness.shared.util.concurrent.ConcurrencyUtil;
3638
import com.google.devtools.mobileharness.shared.util.concurrent.ConcurrencyUtil.SubTask;
3739
import com.google.devtools.mobileharness.shared.util.logging.MobileHarnessLogTag;
@@ -48,8 +50,6 @@
4850
import java.util.ArrayList;
4951
import java.util.List;
5052
import java.util.Map;
51-
import java.util.concurrent.BrokenBarrierException;
52-
import java.util.concurrent.CyclicBarrier;
5353
import java.util.function.BiFunction;
5454
import java.util.stream.Collectors;
5555
import java.util.stream.Stream;
@@ -80,6 +80,12 @@ public class AdhocTestbedDriver extends BaseDriver {
8080

8181
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
8282

83+
/**
84+
* Test property used to indicate whether the main/secondary driver's driver barrier's run method
85+
* is called.
86+
*/
87+
private static final String TEST_PROP_DRIVER_BARRIER_RUN_CALLED = "driver_barrier_run_called";
88+
8389
/**
8490
* A list of sub drivers which contains:
8591
*
@@ -93,11 +99,23 @@ public class AdhocTestbedDriver extends BaseDriver {
9399
*/
94100
private final ImmutableList<Driver> subDrivers;
95101

96-
/** Lets all threads wait until all decorators' preRun() finish before running the main driver. */
97-
private final CyclicBarrier preDriverBarrier;
102+
/**
103+
* Lets all threads wait until all decorators' preRun() finish before running the main driver.
104+
*
105+
* <p>Waiting threads may be woken up before all threads reach the barrier point if the barrier
106+
* wakes them up, for example any decorator stacks try to skip the main driver execution, or
107+
* awaiting threads are interrupted.
108+
*/
109+
private final Barrier preDriverBarrier;
98110

99-
/** Lets all threads wait until the main driver finishes before running decorators' postRun(). */
100-
private final CyclicBarrier postDriverBarrier;
111+
/**
112+
* Lets all threads wait until the main driver finishes before running decorators' postRun().
113+
*
114+
* <p>Waiting threads may be woken up before all threads reach the barrier point if the barrier
115+
* wakes them up, for example any decorator stacks try to skip the main driver execution, or
116+
* awaiting threads are interrupted.
117+
*/
118+
private final Barrier postDriverBarrier;
101119

102120
private final ListeningExecutorService threadPool;
103121
private final DriverFactory driverFactory;
@@ -109,6 +127,8 @@ public class AdhocTestbedDriver extends BaseDriver {
109127
@Nullable
110128
private final BiFunction<Driver, Class<? extends Decorator>, Decorator> decoratorExtender;
111129

130+
private volatile boolean mainDriverSkipped = false;
131+
112132
AdhocTestbedDriver(
113133
List<Device> devices,
114134
TestInfo testInfo,
@@ -124,8 +144,8 @@ public class AdhocTestbedDriver extends BaseDriver {
124144
this.driverWrapper = driverWrapper;
125145
this.decoratorExtender = null;
126146
this.subDrivers = ImmutableList.copyOf(createSubDrivers(devices));
127-
this.preDriverBarrier = new CyclicBarrier(devices.size());
128-
this.postDriverBarrier = new CyclicBarrier(devices.size());
147+
this.preDriverBarrier = new Barrier(devices.size());
148+
this.postDriverBarrier = new Barrier(devices.size());
129149

130150
// Sets up TestbedDevice.
131151
testInfo.log().atInfo().alsoTo(logger).log("Setting up TestbedDevice");
@@ -191,7 +211,7 @@ public void run(TestInfo testInfo) throws MobileHarnessException, InterruptedExc
191211
threadPool,
192212
/* resultMerger= */ results -> null);
193213
} finally {
194-
updateRootTestResultIfNeeded(testInfo);
214+
updateRootTestResultIfNeeded(testInfo, mainDriverSkipped);
195215
}
196216
}
197217

@@ -221,17 +241,28 @@ private String createTestNameOnSubDriver(Driver driver, TestInfo parentTest) {
221241
* sponge can reflect it as expected.
222242
*/
223243
@VisibleForTesting
224-
static void updateRootTestResultIfNeeded(TestInfo rootTestInfo) {
244+
static void updateRootTestResultIfNeeded(TestInfo rootTestInfo, boolean mainDriverSkipped) {
225245
TestResult rootTestResult = rootTestInfo.resultWithCause().get().type();
226246
if (rootTestResult.equals(TestResult.UNKNOWN)) {
227-
rootTestInfo
228-
.resultWithCause()
229-
.setNonPassing(
230-
TestResult.ERROR,
231-
new MobileHarnessException(
232-
ExtErrorId.MOBLY_TESTBED_ADHOC_DRIVER_END_WITH_UNKNOWN_RESULT,
233-
"Set root test result to ERROR because adhoc testbed driver ends with UNKNOWN"
234-
+ " test result. Maybe the primary driver has not been triggered."));
247+
if (mainDriverSkipped) {
248+
rootTestInfo
249+
.resultWithCause()
250+
.setNonPassing(
251+
TestResult.SKIP,
252+
new MobileHarnessException(
253+
ExtErrorId.MOBLY_TESTBED_ADHOC_DRIVER_END_WITH_UNKNOWN_RESULT,
254+
"Set root test result to SKIP because adhoc testbed driver ends with UNKNOWN"
255+
+ " and the primary driver was skipped."));
256+
} else {
257+
rootTestInfo
258+
.resultWithCause()
259+
.setNonPassing(
260+
TestResult.ERROR,
261+
new MobileHarnessException(
262+
ExtErrorId.MOBLY_TESTBED_ADHOC_DRIVER_END_WITH_UNKNOWN_RESULT,
263+
"Set root test result to ERROR because adhoc testbed driver ends with UNKNOWN"
264+
+ " test result. Maybe the primary driver has not been triggered."));
265+
}
235266
} else if (rootTestResult.equals(TestResult.PASS)) {
236267
// Subtests in mobly tests may have different results, b/175287972
237268
int errorTestsTotalCnt = 0;
@@ -347,7 +378,8 @@ private List<Driver> createSubDrivers(List<Device> devices) throws MobileHarness
347378
getTest().jobInfo().subDeviceSpecs().getSubDevice(i).decorators().getAll());
348379
subDriver.add(decoratedDriver);
349380
}
350-
return subDriver;
381+
// Wraps each decorator stack with a decorator to handle barrier.
382+
return subDriver.stream().map(DecoratorBarrier::new).collect(toImmutableList());
351383
}
352384

353385
private List<Driver> createRawSubDrivers(List<Device> devices) throws MobileHarnessException {
@@ -449,31 +481,54 @@ private DriverBarrier(Driver decorated) {
449481

450482
@Override
451483
public void run(TestInfo testInfo) throws MobileHarnessException, InterruptedException {
484+
testInfo.properties().add(TEST_PROP_DRIVER_BARRIER_RUN_CALLED, "true");
452485
try {
453486
testInfo
454487
.log()
455488
.atInfo()
456489
.alsoTo(logger)
457490
.log("Waiting on pre-driver barrier, device=[%s]", getDevice().getDeviceId());
458-
preDriverBarrier.await();
459-
460-
// The main "run" of AdhocTestbedDriver runs each stack using a sub-TestInfo specific to
461-
// each sub-device. We need to make sure the main driver is run against the TestInfo passed
462-
// to the AdhocTestbedDriver constructor.
463-
getDecorated().run(AdhocTestbedDriver.this.getTest());
464-
491+
boolean allPartiesInvokedOnPreDriverBarrier = preDriverBarrier.await();
465492
testInfo
466493
.log()
467494
.atInfo()
468495
.alsoTo(logger)
469-
.log("Waiting on post-driver barrier, device=[%s]", getDevice().getDeviceId());
470-
postDriverBarrier.await();
496+
.log(
497+
"All parties invoked on pre-driver barrier: %s, device=[%s]",
498+
allPartiesInvokedOnPreDriverBarrier, getDevice().getDeviceId());
499+
if (allPartiesInvokedOnPreDriverBarrier) {
500+
// The main "run" of AdhocTestbedDriver runs each stack using a sub-TestInfo specific to
501+
// each sub-device. We need to make sure the main driver is run against the TestInfo
502+
// passed to the AdhocTestbedDriver constructor.
503+
getDecorated().run(AdhocTestbedDriver.this.getTest());
504+
505+
testInfo
506+
.log()
507+
.atInfo()
508+
.alsoTo(logger)
509+
.log("Waiting on post-driver barrier, device=[%s]", getDevice().getDeviceId());
510+
boolean allPartiesInvokedOnPostDriverBarrier = postDriverBarrier.await();
511+
testInfo
512+
.log()
513+
.atInfo()
514+
.alsoTo(logger)
515+
.log(
516+
"All parties invoked on post-driver barrier: %s, device=[%s]",
517+
allPartiesInvokedOnPostDriverBarrier, getDevice().getDeviceId());
518+
} else {
519+
mainDriverSkipped = true;
520+
testInfo
521+
.log()
522+
.atInfo()
523+
.alsoTo(logger)
524+
.log("Skipping driver with device [%s]", getDevice().getDeviceId());
525+
}
471526

472-
// If non of the sub device decorators failed, set the device TestInfo result to PASS.
527+
// If none of the sub device decorators failed, set the device TestInfo result to PASS.
473528
if (testInfo.resultWithCause().get().type().equals(TestResult.UNKNOWN)) {
474529
testInfo.resultWithCause().setPass();
475530
}
476-
} catch (BrokenBarrierException e) {
531+
} catch (InterruptedException e) {
477532
InterruptedException exception =
478533
new InterruptedException(
479534
String.format(
@@ -484,4 +539,31 @@ public void run(TestInfo testInfo) throws MobileHarnessException, InterruptedExc
484539
}
485540
}
486541
}
542+
543+
@DoNotSubscribeTestEvent
544+
private class DecoratorBarrier extends BaseDecorator {
545+
546+
private DecoratorBarrier(Driver decorated) {
547+
super(decorated, decorated.getTest());
548+
}
549+
550+
@Override
551+
public void run(TestInfo testInfo) throws MobileHarnessException, InterruptedException {
552+
getDecorated().run(testInfo);
553+
554+
boolean driverBarrierRunCalled =
555+
testInfo.properties().getBoolean(TEST_PROP_DRIVER_BARRIER_RUN_CALLED).orElse(false);
556+
if (!driverBarrierRunCalled) {
557+
testInfo
558+
.log()
559+
.atInfo()
560+
.alsoTo(logger)
561+
.log("Driver barrier for device [%s] was not called", getDevice().getDeviceId());
562+
// The driver barrier (right before the driver) was not called for the current device's
563+
// decorators stack, it's possible that a decorator decides to skip the test (b/384636099)
564+
preDriverBarrier.stopAwaitations();
565+
postDriverBarrier.stopAwaitations();
566+
}
567+
}
568+
}
487569
}

src/java/com/google/devtools/mobileharness/platform/testbed/adhoc/controller/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ java_library(
3232
"//src/java/com/google/devtools/common/metrics/stability/converter",
3333
"//src/java/com/google/devtools/mobileharness/api/model/error",
3434
"//src/java/com/google/devtools/mobileharness/infra/controller/test/local/annotation",
35+
"//src/java/com/google/devtools/mobileharness/shared/util/concurrent:barrier",
3536
"//src/java/com/google/devtools/mobileharness/shared/util/concurrent:concurrency_util",
3637
"//src/java/com/google/devtools/mobileharness/shared/util/logging:google_logger",
3738
"//src/java/com/google/devtools/mobileharness/shared/util/logging:tag",

0 commit comments

Comments
 (0)