Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1219,128 +1219,165 @@ private String getClassPathForTestServices() {
}

private List<ExecutedTest> runTest(
Instrumentation instrumentation,
String testMethodTarget,
boolean collectCodeCoverage,
@Nullable String coverageDataPath,
boolean enableDebug,
HostTestSize size,
boolean dumpHProfData,
boolean withAnimation,
Map<String, String> extraInstrumentationOptions) {
Instrumentation instrumentation,
String testMethodTarget,
boolean collectCodeCoverage,
@Nullable String coverageDataPath,
boolean enableDebug,
HostTestSize size,
boolean dumpHProfData,
boolean withAnimation,
Map<String, String> extraInstrumentationOptions) {

long testTimeout = size.getTestTimeout(TimeUnit.SECONDS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The body of the runTest method (and also buildInstrumentationShellCommand) is indented with 2 spaces, whereas the rest of the file consistently uses 4 spaces for method bodies. Please update the indentation to 4 spaces to maintain consistency with the existing codebase.

if (testTimeoutOverride.isPresent()) {
testTimeout = testTimeoutOverride.get();
}

List<String> adbArgs = Lists.newArrayList();
String shellCommand =
buildInstrumentationShellCommand(
instrumentation,
testMethodTarget,
collectCodeCoverage,
coverageDataPath,
enableDebug,
dumpHProfData,
withAnimation,
extraInstrumentationOptions,
testTimeout);

if (installBasicServices) {
// We exec the instrumentation through a wrapper launcher defined in basic_services.apk to
// allow test code to execute shell commands with root|shell user privileges.
adbArgs.add("CLASSPATH=" + getClassPathForTestServices());
adbArgs.add("SM_EXIT=1");
adbArgs.add("app_process / androidx.test.services.shellexecutor.ShellMain");
}
InstrumentationTestRunnerProcessor stdoutProcessor =
new InstrumentationTestRunnerProcessor(new EventBus());
SimpleLineListProcessor stderrProcessor = new SimpleLineListProcessor();
SubprocessCommunicator.Builder builder =
communicatorBuilderProvider
.get()
.withStdoutProcessor(stdoutProcessor)
.withStderrProcessor(stderrProcessor);

adbArgs.add("am");
adbArgs.add("instrument");
List<String> partialArgs = Lists.newArrayList("shell", shellCommand);

if (collectCodeCoverage) {
adbArgs.addAll(Lists.newArrayList("-e", "coverage", "true"));
adbArgs.addAll(Lists.newArrayList(
"-e", "coverageDataPath", coverageDataPath));
}
if (enableDebug) {
adbArgs.addAll(Lists.newArrayList("-e", "debug", "true"));
}

if (dumpHProfData) {
adbArgs.addAll(Lists.newArrayList("-e", "hprofDataFile", "hprof.dump"));
try {
makeCheckedCall(
builder,
prefixArgsWithDeviceSerial(partialArgs.toArray(new String[0])),
testTimeout);
} catch (IllegalStateException e) {
StringBuilder allLines = new StringBuilder();
for (ExecutedTest executedTest : stdoutProcessor.getResult()) {
allLines.append(executedTest.getAllLines());
}
throw new RuntimeException(
String.format(
"Error when executing adb.\n STDOUT: %s\n STDERR: %s",
allLines, Joiner.on("\n").join(stderrProcessor.getResult())),
e);
}

for (String key : extraInstrumentationOptions.keySet()) {
adbArgs.add("-e");
adbArgs.add(ShellUtils.shellEscape(key));
adbArgs.add(ShellUtils.shellEscape(extraInstrumentationOptions.get(key)));
}
return stdoutProcessor.getResult();
}

if (!withAnimation) {
if (device.getApiVersion() >= 10) {
adbArgs.add("--no_window_animation");
} // not supported for less then gingerbread.
}
long testTimeout = size.getTestTimeout(TimeUnit.SECONDS);
if (testTimeoutOverride.isPresent()) {
testTimeout = testTimeoutOverride.get();
}
/**
* Constrói a linha de comando a ser passada para {@code adb shell} contendo a invocação completa
* do {@code am instrument}.
*/
Comment on lines +1280 to +1283

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Javadoc comment is written in Portuguese, but the rest of the codebase and the PR description are in English. Please translate it to English to maintain consistency.

Suggested change
/**
* Constrói a linha de comando a ser passada para {@code adb shell} contendo a invocação completa
* do {@code am instrument}.
*/
/**
* Builds the command line to be passed to {@code adb shell} containing the complete invocation
* of {@code am instrument}.
*/

private String buildInstrumentationShellCommand(
Instrumentation instrumentation,
String testMethodTarget,
boolean collectCodeCoverage,
@Nullable String coverageDataPath,
boolean enableDebug,
boolean dumpHProfData,
boolean withAnimation,
Map<String, String> extraInstrumentationOptions,
long testTimeout) {

List<String> tokens = Lists.newArrayList();

// Prefixo opcional para serviços básicos (ShellMain)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is written in Portuguese. Please translate it to English to match the rest of the codebase.

Suggested change
// Prefixo opcional para serviços básicos (ShellMain)
// Optional prefix for basic services (ShellMain)

if (installBasicServices) {
tokens.add("CLASSPATH=" + getClassPathForTestServices());
tokens.add("SM_EXIT=1");
tokens.add("app_process / androidx.test.services.shellexecutor.ShellMain");
}

adbArgs.addAll(
Lists.newArrayList(
"-e", "testTimeoutSeconds", String.valueOf(testTimeout)));

boolean runTestsThroughOrchestrator = installBasicServices
&& ORCHESTRATOR_ENABLED_RUNNERS.contains(instrumentation.getInstrumentationClass());


if (runTestsThroughOrchestrator) {
adbArgs.addAll(
Lists.newArrayList(
"-r",
"-w",
"-e",
"class",
ShellUtils.shellEscape(testMethodTarget),
"-e",
"targetInstrumentation",
instrumentation.getFullName(),
ORCHESTRATOR_COMPONENT_NAME));
} else {
adbArgs.addAll(
Lists.newArrayList(
"-r",
"-w",
"-e",
"class",
ShellUtils.shellEscape(testMethodTarget),
instrumentation.getFullName()));
}
tokens.add("am");
tokens.add("instrument");

// Opções de cobertura
if (collectCodeCoverage) {
tokens.add("-e");
tokens.add("coverage");
tokens.add("true");
tokens.add("-e");
tokens.add("coverageDataPath");
tokens.add(coverageDataPath);
}
Comment on lines +1307 to +1315

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since coverageDataPath is annotated with @Nullable, it can potentially be null. If collectCodeCoverage is true but coverageDataPath is null, adding it to tokens and then calling Joiner.on(" ").join(tokens) will throw a NullPointerException because Guava's Joiner is fail-fast on null values.

We should add a precondition check to ensure coverageDataPath is not null when collectCodeCoverage is true. Additionally, the comment has been translated to English to match the rest of the codebase.

Suggested change
// Opções de cobertura
if (collectCodeCoverage) {
tokens.add("-e");
tokens.add("coverage");
tokens.add("true");
tokens.add("-e");
tokens.add("coverageDataPath");
tokens.add(coverageDataPath);
}
// Coverage options
if (collectCodeCoverage) {
checkArgument(coverageDataPath != null, "coverageDataPath cannot be null when collectCodeCoverage is true");
tokens.add("-e");
tokens.add("coverage");
tokens.add("true");
tokens.add("-e");
tokens.add("coverageDataPath");
tokens.add(coverageDataPath);
}


if (installBasicServices) {
// adb shell commands _may_ return their exit code from the device - if they are run on the
// right system image and the host machine has the right version of adb installed
// otherwise they wont. SM_EXIT kills itself at the end, so we do not want that
// exit code bubbling up.
adbArgs.add("||");
adbArgs.add("true");
}
// Debug
if (enableDebug) {
tokens.add("-e");
tokens.add("debug");
tokens.add("true");
}

InstrumentationTestRunnerProcessor stdoutProcessor =
new InstrumentationTestRunnerProcessor(new EventBus());
SimpleLineListProcessor stderrProcessor = new SimpleLineListProcessor();
SubprocessCommunicator.Builder builder = communicatorBuilderProvider.get();
builder
.withStdoutProcessor(stdoutProcessor)
.withStderrProcessor(stderrProcessor);
String shellArgs = Joiner.on(" ").join(adbArgs);
// HPROF
if (dumpHProfData) {
tokens.add("-e");
tokens.add("hprofDataFile");
tokens.add("hprof.dump");
}

List<String> partialArgs = Lists.newArrayList("shell", shellArgs);
// Extras genéricos
for (Map.Entry<String, String> entry : extraInstrumentationOptions.entrySet()) {
tokens.add("-e");
tokens.add(ShellUtils.shellEscape(entry.getKey()));
tokens.add(ShellUtils.shellEscape(entry.getValue()));
}
Comment on lines +1331 to +1336

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is written in Portuguese. Please translate it to English to match the rest of the codebase.

Suggested change
// Extras genéricos
for (Map.Entry<String, String> entry : extraInstrumentationOptions.entrySet()) {
tokens.add("-e");
tokens.add(ShellUtils.shellEscape(entry.getKey()));
tokens.add(ShellUtils.shellEscape(entry.getValue()));
}
// Generic extras
for (Map.Entry<String, String> entry : extraInstrumentationOptions.entrySet()) {
tokens.add("-e");
tokens.add(ShellUtils.shellEscape(entry.getKey()));
tokens.add(ShellUtils.shellEscape(entry.getValue()));
}


try {
makeCheckedCall(builder,
prefixArgsWithDeviceSerial(partialArgs.toArray(new String[partialArgs.size()])),
testTimeout);
} catch (IllegalStateException e) {
StringBuilder allLines = new StringBuilder();
for (ExecutedTest executedTest : stdoutProcessor.getResult()) {
allLines.append(executedTest.getAllLines());
}
// Animação (apenas API ≥ 10)
if (!withAnimation && device.getApiVersion() >= 10) {
tokens.add("--no_window_animation");
}
Comment on lines +1338 to +1341

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is written in Portuguese. Please translate it to English to match the rest of the codebase.

Suggested change
// Animação (apenas API 10)
if (!withAnimation && device.getApiVersion() >= 10) {
tokens.add("--no_window_animation");
}
// Animation (only API >= 10)
if (!withAnimation && device.getApiVersion() >= 10) {
tokens.add("--no_window_animation");
}


throw new RuntimeException(String.format(
"Error when executing adb.\n STDOUT: %s\n STDERR: %s",
allLines,
Joiner.on("\n").join(stderrProcessor.getResult())), e);
}
// Timeout
tokens.add("-e");
tokens.add("testTimeoutSeconds");
tokens.add(String.valueOf(testTimeout));

// Decisão sobre orquestrador
boolean useOrchestrator =
installBasicServices
&& ORCHESTRATOR_ENABLED_RUNNERS.contains(instrumentation.getInstrumentationClass());
Comment on lines +1348 to +1351

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is written in Portuguese. Please translate it to English to match the rest of the codebase.

Suggested change
// Decisão sobre orquestrador
boolean useOrchestrator =
installBasicServices
&& ORCHESTRATOR_ENABLED_RUNNERS.contains(instrumentation.getInstrumentationClass());
// Orchestrator decision
boolean useOrchestrator =
installBasicServices
&& ORCHESTRATOR_ENABLED_RUNNERS.contains(instrumentation.getInstrumentationClass());


if (useOrchestrator) {
tokens.add("-r");
tokens.add("-w");
tokens.add("-e");
tokens.add("class");
tokens.add(ShellUtils.shellEscape(testMethodTarget));
tokens.add("-e");
tokens.add("targetInstrumentation");
tokens.add(instrumentation.getFullName());
tokens.add(ORCHESTRATOR_COMPONENT_NAME);
} else {
tokens.add("-r");
tokens.add("-w");
tokens.add("-e");
tokens.add("class");
tokens.add(ShellUtils.shellEscape(testMethodTarget));
tokens.add(instrumentation.getFullName());
}

return stdoutProcessor.getResult();
// Se usamos serviços básicos, ocultamos o código de saída do ShellMain
if (installBasicServices) {
tokens.add("||");
tokens.add("true");
}

Comment on lines +1373 to 1377

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment is written in Portuguese. Please translate it to English to match the rest of the codebase.

Suggested change
if (installBasicServices) {
tokens.add("||");
tokens.add("true");
}
// If we use basic services, we hide the exit code of ShellMain
if (installBasicServices) {
tokens.add("||");
tokens.add("true");
}

return Joiner.on(" ").join(tokens);
}

private boolean isApkAlreadyInstalled(String apkPath, String appPackageName) throws IOException {
checkNotNull(apkPath);
checkNotNull(appPackageName);
Expand Down