diff --git a/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessor.java b/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessor.java index 5eb5b28e2c..f484356e0b 100644 --- a/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessor.java +++ b/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessor.java @@ -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; diff --git a/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD b/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD index 486d1d415d..cecb281264 100644 --- a/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD +++ b/src/java/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD @@ -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", diff --git a/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD b/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD index 886485d302..5a5bf47f41 100644 --- a/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD +++ b/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD @@ -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", - ], -) diff --git a/src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD b/src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD new file mode 100644 index 0000000000..6124b71cb1 --- /dev/null +++ b/src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD @@ -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", + ], +) diff --git a/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoader.java b/src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoader.java similarity index 57% rename from src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoader.java rename to src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoader.java index 69a826b36b..fdc438d8b7 100644 --- a/src/java/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoader.java +++ b/src/java/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoader.java @@ -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; @@ -27,11 +27,14 @@ 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() { @@ -39,25 +42,50 @@ public TestSuiteResultLoader() { } @VisibleForTesting - TestSuiteResultLoader(LocalFileUtil localFileUtil) { + public TestSuiteResultLoader(LocalFileUtil localFileUtil) { this.localFileUtil = localFileUtil; } + /** + * Loads test result from the gen file directory of the test. + * + *

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 loadTestResult(TestInfo testInfo) { try { String genFileDir = testInfo.getGenFileDir(); - String pbPath = PathUtil.join(genFileDir, "instrument_test_result.pb"); + + // Try loading Android result file first + Optional 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 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(); } diff --git a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD index 0dde75a3ae..0038b9afaf 100644 --- a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD +++ b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD @@ -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", ], ) @@ -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", diff --git a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtil.java b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtil.java index 169bb2783a..1384043331 100644 --- a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtil.java +++ b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtil.java @@ -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 { @@ -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 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); } diff --git a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/AndroidInstrumentationTestSuiteResultConverter.java b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/TestSuiteResultConverter.java similarity index 74% rename from src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/AndroidInstrumentationTestSuiteResultConverter.java rename to src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/TestSuiteResultConverter.java index 97e9fa2623..4cacb33742 100644 --- a/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/AndroidInstrumentationTestSuiteResultConverter.java +++ b/src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup/TestSuiteResultConverter.java @@ -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; @@ -25,17 +27,17 @@ 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 testCasesBuilder = ImmutableList.builder(); String suiteName = testSuiteResult.hasTestSuiteMetaData() @@ -43,14 +45,34 @@ private AndroidInstrumentationTestSuiteResultConverter() {} : ""; for (TestResult testResult : testSuiteResult.getTestResultList()) { - testCasesBuilder.add(toTestCase(testResult, suiteName)); + testCasesBuilder.add(toTestCase(testResult, suiteName, isIos)); } ImmutableList 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 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 groupedCases = + Multimaps.index(testCases, tc -> tc.testCaseReference().testSuiteName()); + ImmutableList.Builder 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()); @@ -58,11 +80,11 @@ private AndroidInstrumentationTestSuiteResultConverter() {} 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(); @@ -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); diff --git a/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessorTest.java b/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessorTest.java index 1be1a21dff..19e1116f46 100644 --- a/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessorTest.java +++ b/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/AndroidInstrumentationTestRetryProcessorTest.java @@ -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; diff --git a/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD b/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD index 8c565ae07a..6391847988 100644 --- a/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD +++ b/src/javatests/com/google/devtools/mobileharness/infra/client/api/controller/job/retry/processor/BUILD @@ -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", diff --git a/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD b/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD index 7a76ad3d58..64d43ce9a7 100644 --- a/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD +++ b/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/BUILD @@ -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", diff --git a/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD new file mode 100644 index 0000000000..904f7245e3 --- /dev/null +++ b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/BUILD @@ -0,0 +1,45 @@ +# 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:defs.bzl", "java_library") +load("//src/javatests/com/google/devtools/mobileharness/builddefs:junit_test_suites.bzl", "junit_test_suites") + +package(default_applicable_licenses = ["//:license"]) + +java_library( + name = "loader", + testonly = 1, + srcs = glob(["*.java"]), + deps = [ + "//src/devtools/mobileharness/platform/android/instrumentation/result/proto:test_suite_result_java_proto", + "//src/java/com/google/devtools/mobileharness/shared/util/file/local", + "//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", + "//src/java/com/google/wireless/qa/mobileharness/shared/proto:job_java_proto", + "//src/javatests/com/google/devtools/mobileharness/builddefs:truth", + "@maven//:junit_junit", + ], +) + +junit_test_suites( + name = "gen_tests", + sizes = [ + "small", + ], + deps = [ + ":loader", + ], +) diff --git a/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoaderTest.java b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoaderTest.java similarity index 81% rename from src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoaderTest.java rename to src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoaderTest.java index 8e213c7b68..25d12319b2 100644 --- a/src/javatests/com/google/devtools/mobileharness/platform/android/instrumentation/result/TestSuiteResultLoaderTest.java +++ b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/loader/TestSuiteResultLoaderTest.java @@ -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 static com.google.common.truth.Truth.assertThat; @@ -68,7 +68,7 @@ public void loadTestResult_resultFileDoesNotExist_returnsEmpty() throws Exceptio } @Test - public void loadTestResult_resultFileExists_returnsParsedResult() throws Exception { + public void loadTestResult_androidResultFileExists_returnsParsedResult() throws Exception { String genFileDir = testInfo.getGenFileDir(); TestSuiteResult testSuiteResult = TestSuiteResult.newBuilder().setTestStatus(TestStatus.PASSED).build(); @@ -83,6 +83,22 @@ public void loadTestResult_resultFileExists_returnsParsedResult() throws Excepti assertThat(result.get().getTestStatus()).isEqualTo(TestStatus.PASSED); } + @Test + public void loadTestResult_iosResultFileExists_returnsParsedResult() throws Exception { + String genFileDir = testInfo.getGenFileDir(); + TestSuiteResult testSuiteResult = + TestSuiteResult.newBuilder().setTestStatus(TestStatus.PASSED).build(); + byte[] testSuiteResultBytes = testSuiteResult.toByteArray(); + + String testSuiteResultPbPath = PathUtil.join(genFileDir, "xctest_test_result.pb"); + realLocalFileUtil.writeToFile(testSuiteResultPbPath, testSuiteResultBytes); + + Optional result = loader.loadTestResult(testInfo); + + assertThat(result).isPresent(); + assertThat(result.get().getTestStatus()).isEqualTo(TestStatus.PASSED); + } + @Test public void loadTestResult_invalidProtoBytes_returnsEmpty() throws Exception { String genFileDir = testInfo.getGenFileDir(); diff --git a/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD index 263fac9ec1..7fc462f58c 100644 --- a/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD +++ b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/BUILD @@ -38,6 +38,7 @@ java_library( "//src/java/com/google/devtools/mobileharness/shared/util/testresult/rollup:test_suite_overview_converter", "//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", "//src/javatests/com/google/devtools/mobileharness/builddefs:truth", "@maven//:com_google_guava_guava", "@maven//:junit_junit", diff --git a/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtilTest.java b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtilTest.java index b46dd749ce..a5565bdf37 100644 --- a/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtilTest.java +++ b/src/javatests/com/google/devtools/mobileharness/shared/util/testresult/rollup/JobResultUtilTest.java @@ -28,10 +28,12 @@ import com.google.devtools.mobileharness.platform.android.instrumentation.result.proto.TestSuiteResult; import com.google.devtools.mobileharness.shared.util.testresult.rollup.Outcome.OutcomeSummary; import com.google.protobuf.util.Timestamps; +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.TestInfo; import com.google.wireless.qa.mobileharness.shared.model.job.out.Properties; import com.google.wireless.qa.mobileharness.shared.model.job.out.Timing; +import com.google.wireless.qa.mobileharness.shared.proto.Job.JobType; import java.io.File; import java.io.FileOutputStream; import java.time.Duration; @@ -714,4 +716,70 @@ private void writeProto(File directory, String filename, TestSuiteResult proto) .setElapsedTime(Duration.ofSeconds(endTimeSeconds - startTimeSeconds)) .build(); } + + @Test + public void computeJobLevelTestResult_ios_success() throws Exception { + TestInfo runShard0 = + mockIosTestRun( + "test_shard_0", + "0", + com.google.devtools.mobileharness.api.model.proto.Test.TestResult.PASS, + /* flakyAttemptIndex= */ 0, + /* errorAttemptIndex= */ 0, + "com.example.Suite", + TestStatus.PASSED, + ImmutableList.of( + buildTestCaseInfo("MyClass", "testFoo", TestStatus.PASSED, Duration.ofSeconds(5)))); + + JobInfo jobInfo = mock(JobInfo.class, Mockito.RETURNS_DEEP_STUBS); + when(jobInfo.locator().getId()).thenReturn("job-1"); + when(jobInfo.tests().getAll().values()).thenReturn(ImmutableList.of(runShard0)); + + com.google.devtools.mobileharness.shared.util.testresult.rollup.TestResult result = + JobResultUtil.computeJobRunResult(jobInfo); + + assertThat(result.outcome().summary()).isEqualTo(OutcomeSummary.SUCCESS); + assertThat(result.testCases()).hasSize(1); + // In iOS, the testSuiteName of the test case should be mapped to the package name of the test + // case ("com.example") + // rather than the overall metadata suite name ("com.example.Suite") + assertThat(result.testCases().get(0).testCaseReference().testSuiteName()) + .isEqualTo("com.example"); + } + + private TestInfo mockIosTestRun( + String shardName, + String shardIndex, + com.google.devtools.mobileharness.api.model.proto.Test.TestResult mockResult, + int flakyAttemptIndex, + int errorAttemptIndex, + String testSuiteName, + TestStatus suiteStatus, + ImmutableList cases) + throws Exception { + TestInfo testInfo = mock(TestInfo.class, Mockito.RETURNS_DEEP_STUBS); + File genDir = tempFolder.newFolder(); + when(testInfo.getGenFileDir()).thenReturn(genDir.getAbsolutePath()); + + Properties properties = new Properties(new Timing()); + properties.add("flaky_attempt_index", String.valueOf(flakyAttemptIndex)); + properties.add("error_attempt_index", String.valueOf(errorAttemptIndex)); + if (shardIndex != null) { + properties.add(PropertyName.Test.SHARD_INDEX, shardIndex); + } + when(testInfo.properties()).thenReturn(properties); + + when(testInfo.locator().getName()).thenReturn(shardName); + when(testInfo.resultWithCause().get().type()).thenReturn(mockResult); + + JobType jobType = JobType.newBuilder().setDriver("IosNativeXcTest").build(); + when(testInfo.jobInfo().type()).thenReturn(jobType); + + if (testSuiteName != null) { + TestSuiteResult testSuiteResult = buildTestSuiteResult(testSuiteName, suiteStatus, cases); + writeProto(genDir, "xctest_test_result.pb", testSuiteResult); + } + + return testInfo; + } }