Skip to content
Open
Show file tree
Hide file tree
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 @@ -24,11 +24,11 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.TestSuiteResultLoader;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestCase;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestResult;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestStatus;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestSuiteResult;
import com.google.devtools.mobileharness.shared.util.testresult.loader.TestSuiteResultLoader;
import com.google.wireless.qa.mobileharness.shared.constant.PropertyName.Test;
import com.google.wireless.qa.mobileharness.shared.model.job.TestInfo;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ java_library(
deps = [
"//src/devtools/mobileharness/api/model/proto:test_java_proto",
"//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto",
"//src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result:test_suite_result_loader",
"//src/java/com/google/devtools/mobileharness/shared/util/logging:google_logger",
"//src/java/com/google/devtools/mobileharness/shared/util/testresult/loader",
"//src/java/com/google/wireless/qa/mobileharness/shared/constant:property",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"@maven//:com_google_guava_guava",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,3 @@ java_library(
"@protobuf//:protobuf_java_util",
],
)

java_library(
name = "test_suite_result_loader",
srcs = ["TestSuiteResultLoader.java"],
deps = [
"//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto",
"//src/java/com/google/devtools/mobileharness/api/model/error",
"//src/java/com/google/devtools/mobileharness/shared/util/file/local",
"//src/java/com/google/devtools/mobileharness/shared/util/logging:google_logger",
"//src/java/com/google/devtools/mobileharness/shared/util/path",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"@maven//:com_google_guava_guava",
"@protobuf//:protobuf_java",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

load("@rules_java//java:java_library.bzl", "java_library")

package(
default_applicable_licenses = ["//:license"],
default_visibility = [
"//:deviceinfra_all_pkg",
],
)

java_library(
name = "loader",
srcs = ["TestSuiteResultLoader.java"],
deps = [
"//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto",
"//src/java/com/google/devtools/mobileharness/api/model/error",
"//src/java/com/google/devtools/mobileharness/shared/util/file/local",
"//src/java/com/google/devtools/mobileharness/shared/util/logging:google_logger",
"//src/java/com/google/devtools/mobileharness/shared/util/path",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"@maven//:com_google_guava_guava",
"@protobuf//:protobuf_java",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.google.devtools.mobileharness.platform.android.instrumentation.result;
package com.google.devtools.mobileharness.shared.util.testresult.loader;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
Expand All @@ -27,37 +27,65 @@
import com.google.wireless.qa.mobileharness.shared.model.job.TestInfo;
import java.util.Optional;

/** Loader for Android instrumentation test result. */
/** Loader for test suite result across platforms (Android and iOS). */
public class TestSuiteResultLoader {

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

private static final String ANDROID_RESULT_FILE_NAME = "instrument_test_result.pb";
private static final String IOS_RESULT_FILE_NAME = "xctest_test_result.pb";

private final LocalFileUtil localFileUtil;

public TestSuiteResultLoader() {
this(new LocalFileUtil());
}

@VisibleForTesting
TestSuiteResultLoader(LocalFileUtil localFileUtil) {
public TestSuiteResultLoader(LocalFileUtil localFileUtil) {
this.localFileUtil = localFileUtil;
}

/**
* Loads test result from the gen file directory of the test.
*
* <p>It first tries to load from {@code instrument_test_result.pb} (Android), and if not found,
* falls back to {@code xctest_test_result.pb} (iOS).
*/
public Optional<TestSuiteResult> loadTestResult(TestInfo testInfo) {
try {
String genFileDir = testInfo.getGenFileDir();
String pbPath = PathUtil.join(genFileDir, "instrument_test_result.pb");

// Try loading Android result file first
Optional<TestSuiteResult> androidResult =
loadFromFile(testInfo, genFileDir, ANDROID_RESULT_FILE_NAME);
if (androidResult.isPresent()) {
return androidResult;
}

// Fallback to iOS result file
return loadFromFile(testInfo, genFileDir, IOS_RESULT_FILE_NAME);
} catch (MobileHarnessException e) {
logger.atWarning().withCause(e).log(
"Failed to get gen file directory for test %s.", testInfo.locator().getId());
}
return Optional.empty();
}

private Optional<TestSuiteResult> loadFromFile(
TestInfo testInfo, String genFileDir, String fileName) {
try {
String pbPath = PathUtil.join(genFileDir, fileName);
if (localFileUtil.isFileExist(pbPath)) {
byte[] bytes = localFileUtil.readBinaryFile(pbPath);
return Optional.of(
TestSuiteResult.parseFrom(bytes, ExtensionRegistryLite.getEmptyRegistry()));
} else {
logger.atInfo().log(
"No instrument_test_result.pb found for test %s.", testInfo.locator().getId());
logger.atInfo().log("No %s found for test %s.", fileName, testInfo.locator().getId());
}
} catch (InvalidProtocolBufferException | MobileHarnessException e) {
logger.atWarning().withCause(e).log(
"Failed to load test result for test %s.", testInfo.locator().getId());
"Failed to load test result from %s for test %s.", fileName, testInfo.locator().getId());
}
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,21 @@ java_library(
name = "job_result_util",
srcs = ["JobResultUtil.java"],
deps = [
":android_instrumentation_test_suite_result_converter",
":outcome",
":state",
":test_case",
":test_case_reference",
":test_result",
":test_suite_overview",
":test_suite_result_converter",
"//src/devtools/mobileharness/api/model/proto:test_java_proto",
"//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto",
"//src/java/com/google/devtools/mobileharness/api/model/error",
"//src/java/com/google/devtools/mobileharness/api/model/job/out",
"//src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor:retry_tests_grouper",
"//src/java/com/google/devtools/mobileharness/shared/util/file/local",
"//src/java/com/google/devtools/mobileharness/shared/util/logging:google_logger",
"//src/java/com/google/devtools/mobileharness/shared/util/path",
"//src/java/com/google/devtools/mobileharness/shared/util/testresult/loader",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"@maven//:com_google_guava_guava",
"@protobuf//:protobuf_java",
],
)

Expand Down Expand Up @@ -117,8 +114,8 @@ java_library(
)

java_library(
name = "android_instrumentation_test_suite_result_converter",
srcs = ["AndroidInstrumentationTestSuiteResultConverter.java"],
name = "test_suite_result_converter",
srcs = ["TestSuiteResultConverter.java"],
deps = [
":outcome",
":stack_trace",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,19 @@
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.flogger.FluentLogger;
import com.google.devtools.mobileharness.api.model.error.MobileHarnessException;
import com.google.devtools.mobileharness.api.model.job.out.Result.ResultTypeWithCause;
import com.google.devtools.mobileharness.infra.client.api.controller.job.retry.processor.RetryTestsGrouper;
import com.google.devtools.mobileharness.infra.client.api.controller.job.retry.processor.RetryTestsGrouper.GroupedTests;
import com.google.devtools.mobileharness.infra.client.api.controller.job.retry.processor.RetryTestsGrouper.ShardTestRuns;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestSuiteResult;
import com.google.devtools.mobileharness.shared.util.file.local.LocalFileUtil;
import com.google.devtools.mobileharness.shared.util.path.PathUtil;
import com.google.devtools.mobileharness.shared.util.testresult.loader.TestSuiteResultLoader;
import com.google.devtools.mobileharness.shared.util.testresult.rollup.Outcome.OutcomeSummary;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.wireless.qa.mobileharness.shared.model.job.JobInfo;
import com.google.wireless.qa.mobileharness.shared.model.job.TestInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/** Utility for computing job-level rolled up test results. */
public final class JobResultUtil {
Expand Down Expand Up @@ -93,24 +91,14 @@ private static TestResult loadTestResult(TestInfo testInfo) {
State.COMPLETE);
}

LocalFileUtil localFileUtil = new LocalFileUtil();
try {
String genFileDir = testInfo.getGenFileDir();
String pbPath = PathUtil.join(genFileDir, "instrument_test_result.pb");
if (localFileUtil.isFileExist(pbPath)) {
byte[] bytes = localFileUtil.readBinaryFile(pbPath);
TestSuiteResult testSuiteResult =
TestSuiteResult.parseFrom(bytes, ExtensionRegistryLite.getEmptyRegistry());
return AndroidInstrumentationTestSuiteResultConverter.toTestResult(testSuiteResult);
} else {
logger.atInfo().log(
"No instrument_test_result.pb found for test %s.", testInfo.locator().getId());
TestSuiteResultLoader loader = new TestSuiteResultLoader();
Optional<TestSuiteResult> testSuiteResultOpt = loader.loadTestResult(testInfo);
if (testSuiteResultOpt.isPresent()) {
boolean isIos = false;
if (testInfo.jobInfo() != null && testInfo.jobInfo().type() != null) {
isIos = Objects.equals(testInfo.jobInfo().type().getDriver(), "IosNativeXcTest");
}
} catch (InvalidProtocolBufferException | MobileHarnessException e) {
// Fall back to simple TestResult
logger.atWarning().withCause(e).log(
"Failed to load test result for test %s, fallback to simple TestResult.",
testInfo.locator().getId());
return TestSuiteResultConverter.toTestResult(testSuiteResultOpt.get(), isIos);
}
return buildFallbackTestResult(testInfo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.google.devtools.mobileharness.shared.util.testresult.rollup;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestResult;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestStatus;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestSuiteResult;
Expand All @@ -25,44 +27,64 @@
import java.time.Duration;
import java.time.Instant;

/** Helper converter class for Android Instrumentation test result formats. */
public final class AndroidInstrumentationTestSuiteResultConverter {
/** Helper converter class for test suite result formats. */
public final class TestSuiteResultConverter {

private AndroidInstrumentationTestSuiteResultConverter() {}
private TestSuiteResultConverter() {}

/**
* Converts a {@link TestSuiteResult} to a rollup {@link
* com.google.devtools.mobileharness.shared.util.testresult.rollup.TestResult}.
*/
public static com.google.devtools.mobileharness.shared.util.testresult.rollup.TestResult
toTestResult(TestSuiteResult testSuiteResult) {
toTestResult(TestSuiteResult testSuiteResult, boolean isIos) {
ImmutableList.Builder<TestCase> testCasesBuilder = ImmutableList.builder();
String suiteName =
testSuiteResult.hasTestSuiteMetaData()
? testSuiteResult.getTestSuiteMetaData().getTestSuiteName()
: "";

for (TestResult testResult : testSuiteResult.getTestResultList()) {
testCasesBuilder.add(toTestCase(testResult, suiteName));
testCasesBuilder.add(toTestCase(testResult, suiteName, isIos));
}

ImmutableList<TestCase> testCases = testCasesBuilder.build();
Duration totalElapsed =
testCases.stream().map(TestCase::elapsedTime).reduce(Duration.ZERO, Duration::plus);
TestSuiteOverview suiteOverview =
TestSuiteOverview.builder().setName(suiteName).setElapsedTime(totalElapsed).build();

ImmutableList<TestSuiteOverview> testSuiteOverviews;
if (isIos) {
// Group test cases by their suite name (which is the iOS package name, i.e., target name)
// and create an overview for each target.
ImmutableListMultimap<String, TestCase> groupedCases =
Multimaps.index(testCases, tc -> tc.testCaseReference().testSuiteName());
ImmutableList.Builder<TestSuiteOverview> overviewsBuilder = ImmutableList.builder();
for (String name : groupedCases.keySet()) {
Duration targetElapsed =
groupedCases.get(name).stream()
.map(TestCase::elapsedTime)
.reduce(Duration.ZERO, Duration::plus);
overviewsBuilder.add(
TestSuiteOverview.builder().setName(name).setElapsedTime(targetElapsed).build());
}
testSuiteOverviews = overviewsBuilder.build();
} else {
Duration totalElapsed =
testCases.stream().map(TestCase::elapsedTime).reduce(Duration.ZERO, Duration::plus);
testSuiteOverviews =
ImmutableList.of(
TestSuiteOverview.builder().setName(suiteName).setElapsedTime(totalElapsed).build());
}

OutcomeSummary outcomeSummary =
getOutcomeSummaryFromTestStatus(testSuiteResult.getTestStatus());
Outcome outcome = Outcome.create(outcomeSummary);
State state = State.COMPLETE;

return com.google.devtools.mobileharness.shared.util.testresult.rollup.TestResult.create(
testCases, ImmutableList.of(suiteOverview), outcome, state);
testCases, testSuiteOverviews, outcome, state);
}

/** Converts an Android Instrumentation test case {@link TestResult} to a {@link TestCase}. */
public static TestCase toTestCase(TestResult testResult, String suiteName) {
/** Converts a test case {@link TestResult} to a {@link TestCase}. */
public static TestCase toTestCase(TestResult testResult, String suiteName, boolean isIos) {
com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestCase
instrumentationTestCase = testResult.getTestCase();

Expand All @@ -73,11 +95,13 @@ public static TestCase toTestCase(TestResult testResult, String suiteName) {
+ "."
+ instrumentationTestCase.getTestClass();

String finalSuiteName = isIos ? instrumentationTestCase.getTestPackage() : suiteName;

TestCaseReference testCaseRef =
TestCaseReference.builder()
.setName(instrumentationTestCase.getTestMethod())
.setClassName(className)
.setTestSuiteName(suiteName)
.setTestSuiteName(finalSuiteName)
.build();

TestCase.Builder testCaseBuilder = TestCase.builder().setTestCaseReference(testCaseRef);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@

import com.google.devtools.mobileharness.api.model.error.BasicErrorId;
import com.google.devtools.mobileharness.api.model.error.MobileHarnessException;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.TestSuiteResultLoader;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestCase;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestResult;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestStatus;
import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestSuiteResult;
import com.google.devtools.mobileharness.shared.util.testresult.loader.TestSuiteResultLoader;
import com.google.wireless.qa.mobileharness.shared.constant.PropertyName;
import com.google.wireless.qa.mobileharness.shared.model.job.JobInfo;
import com.google.wireless.qa.mobileharness.shared.model.job.JobLocator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ java_library(
"//src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor:retry_tests_grouper",
"//src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor:test_retry_processor",
"//src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/strategy:flaky_test_retry_constants",
"//src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result:test_suite_result_loader",
"//src/java/com/google/devtools/mobileharness/shared/util/testresult/loader",
"//src/java/com/google/wireless/qa/mobileharness/shared/constant:property",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"//src/java/com/google/wireless/qa/mobileharness/shared/proto:job_java_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ java_library(
deps = [
"//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto",
"//src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result:test_suite_result_converter",
"//src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result:test_suite_result_loader",
"//src/java/com/google/devtools/mobileharness/shared/util/file/local",
"//src/java/com/google/devtools/mobileharness/shared/util/junit/xmlwriter/model",
"//src/java/com/google/devtools/mobileharness/shared/util/path",
"//src/java/com/google/wireless/qa/mobileharness/shared/model/job",
"//src/java/com/google/wireless/qa/mobileharness/shared/proto:job_java_proto",
"//src/javatests/com/google/devtools/mobileharness/builddefs:truth",
"//src/javatests/com/google/devtools/mobileharness/shared/util/runfiles:runfiles_util",
"@maven//:com_google_protobuf_protobuf_java",
Expand Down
Loading
Loading