| #!/usr/bin/env vpython3 |
| # Copyright 2019 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unittests for xcode_log_parser.py.""" |
| |
| import json |
| import mock |
| import os |
| import unittest |
| |
| from test_result_util import TestStatus |
| import test_runner |
| import test_runner_test |
| import xcode_log_parser |
| import constants |
| |
| |
| OUTPUT_PATH = '/tmp/attempt_0' |
| XCRESULT_PATH = '/tmp/attempt_0.xcresult' |
| XCODE11_DICT = { |
| 'path': '/Users/user1/Xcode.app', |
| 'version': '11.0', |
| 'build': '11M336w', |
| } |
| # A sample of json result when executing xcresulttool on .xcresult dir without |
| # --id. Some unused keys and values were removed. |
| XCRESULT_ROOT = """ |
| { |
| "_type" : { |
| "_name" : "ActionsInvocationRecord" |
| }, |
| "actions" : { |
| "_values" : [ |
| { |
| "actionResult" : { |
| "_type" : { |
| "_name" : "ActionResult" |
| }, |
| "diagnosticsRef" : { |
| "id" : { |
| "_value" : "DIAGNOSTICS_REF_ID" |
| } |
| }, |
| "logRef" : { |
| "id" : { |
| "_value" : "0~6jr1GkZxoWVzWfcUNA5feff3l7g8fPHJ1rqKetCBa3QXhCGY74PnEuRwzktleMTFounMfCdDpSr1hRfhUGIUEQ==" |
| } |
| }, |
| "testsRef" : { |
| "id" : { |
| "_value" : "0~iRbOkDnmtKVIvHSV2jkeuNcg4RDTUaCLZV7KijyxdCqvhqtp08MKxl0MwjBAPpjmruoI7qNHzBR1RJQAlANNHA==" |
| } |
| }, |
| "metrics" : { |
| "_type" : { |
| "_name" : "ResultMetrics" |
| }, |
| "testsCount" : { |
| "_type" : { |
| "_name" : "Int" |
| }, |
| "_value" : "2" |
| }, |
| "testsFailedCount" : { |
| "_type" : { |
| "_name" : "Int" |
| }, |
| "_value" : "2" |
| } |
| } |
| } |
| } |
| ] |
| }, |
| "issues" : { |
| "testFailureSummaries" : { |
| "_values" : [ |
| { |
| "documentLocationInCreatingWorkspace" : { |
| "url" : { |
| "_value" : "file:\/\/\/..\/..\/ios\/web\/shell\/test\/page_state_egtest.mm#CharacterRangeLen=0&EndingLineNumber=130&StartingLineNumber=130" |
| } |
| }, |
| "message" : { |
| "_value": "Fail. Screenshots: {\\n\\"Failure\\": \\"path.png\\"\\n}" |
| }, |
| "testCaseName" : { |
| "_value": "-[PageStateTestCase testZeroContentOffsetAfterLoad]" |
| } |
| } |
| ] |
| } |
| }, |
| "metrics" : { |
| "testsCount" : { |
| "_value" : "2" |
| }, |
| "testsFailedCount" : { |
| "_value" : "1" |
| } |
| } |
| }""" |
| |
| XCRESULT_MISSING_ACTIONRESULT_METRICS = b""" |
| { |
| "_type" : { |
| "_name" : "ActionsInvocationRecord" |
| }, |
| "actions" : { |
| "_values" : [ |
| { |
| "actionResult" : { |
| "metrics" : { |
| "_type" : { |
| "_name" : "ResultMetrics" |
| }, |
| "errorCount" : { |
| "_type" : { |
| "_name" : "Int" |
| }, |
| "_value" : "1" |
| } |
| } |
| } |
| } |
| ] |
| }, |
| "metrics" : { |
| "errorCount" : { |
| "_type" : { |
| "_name" : "Int" |
| }, |
| "_value" : "1" |
| }, |
| "testsCount" : { |
| "_type" : { |
| "_name" : "Int" |
| }, |
| "_value" : "30" |
| } |
| } |
| }""" |
| |
| REF_ID = b""" |
| { |
| "actions": { |
| "_values": [{ |
| "actionResult": { |
| "testsRef": { |
| "id": { |
| "_value": "REF_ID" |
| } |
| } |
| } |
| }] |
| } |
| }""" |
| |
| # A sample of json result when executing xcresulttool on .xcresult dir with |
| # "testsRef" as --id input. Some unused keys and values were removed. |
| TESTS_REF = """ |
| { |
| "summaries": { |
| "_values": [{ |
| "testableSummaries": { |
| "_type": { |
| "_name": "Array" |
| }, |
| "_values": [{ |
| "tests": { |
| "_type": { |
| "_name": "Array" |
| }, |
| "_values": [{ |
| "identifier" : { |
| "_value" : "All tests" |
| }, |
| "name" : { |
| "_value" : "All tests" |
| }, |
| "subtests": { |
| "_values": [{ |
| "identifier" : { |
| "_value" : "ios_web_shell_eg2tests_module.xctest" |
| }, |
| "name" : { |
| "_value" : "ios_web_shell_eg2tests_module.xctest" |
| }, |
| "subtests": { |
| "_values": [{ |
| "identifier" : { |
| "_value" : "PageStateTestCase" |
| }, |
| "name" : { |
| "_value" : "PageStateTestCase" |
| }, |
| "subtests": { |
| "_values": [{ |
| "testStatus": { |
| "_value": "Success" |
| }, |
| "duration" : { |
| "_type" : { |
| "_name" : "Double" |
| }, |
| "_value" : "35.38412606716156" |
| }, |
| "identifier": { |
| "_value": "PageStateTestCase/testMethod1" |
| }, |
| "name": { |
| "_value": "testMethod1" |
| } |
| }, |
| { |
| "summaryRef": { |
| "id": { |
| "_value": "0~7Q_uAuUSJtx9gtHM08psXFm3g_xiTTg5bpdoDO88nMXo_iMwQTXpqlrlMe5AtkYmnZ7Ux5uEgAe83kJBfoIckw==" |
| } |
| }, |
| "testStatus": { |
| "_value": "Failure" |
| }, |
| "identifier": { |
| "_value": "PageStateTestCase\/testZeroContentOffsetAfterLoad" |
| }, |
| "name": { |
| "_value": "testZeroContentOffsetAfterLoad" |
| } |
| }, |
| { |
| "testStatus": { |
| "_value": "Expected Failure" |
| }, |
| "duration" : { |
| "_type" : { |
| "_name" : "Double" |
| }, |
| "_value" : "28.988606716156" |
| }, |
| "identifier": { |
| "_value": "PageStateTestCase/testMethod2" |
| }, |
| "name": { |
| "_value": "testMethod2" |
| } |
| }, |
| { |
| "testStatus": { |
| "_value": "Skipped" |
| }, |
| "duration" : { |
| "_type" : { |
| "_name" : "Double" |
| }, |
| "_value" : "0.0606716156" |
| }, |
| "identifier": { |
| "_value": "PageStateTestCase/testMethod3" |
| }, |
| "name": { |
| "_value": "testMethod3" |
| } |
| }] |
| } |
| }] |
| } |
| }] |
| } |
| }] |
| } |
| }] |
| } |
| }] |
| } |
| } |
| """ |
| |
| # A sample of json result when executing xcresulttool on .xcresult dir with |
| # a single test summaryRef id value as --id input. Some unused keys and values |
| # were removed. |
| SINGLE_TEST_SUMMARY_REF = """ |
| { |
| "_type" : { |
| "_name" : "ActionTestSummary", |
| "_supertype" : { |
| "_name" : "ActionTestSummaryIdentifiableObject", |
| "_supertype" : { |
| "_name" : "ActionAbstractTestSummary" |
| } |
| } |
| }, |
| "activitySummaries" : { |
| "_values" : [ |
| { |
| "attachments" : { |
| "_values" : [ |
| { |
| "filename" : { |
| "_value" : "Screenshot_25659115-F3E4-47AE-AA34-551C94333D7E.jpg" |
| }, |
| "payloadRef" : { |
| "id" : { |
| "_value" : "SCREENSHOT_REF_ID_1" |
| } |
| } |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Start Test at 2020-10-19 14:12:58.111" |
| } |
| }, |
| { |
| "subactivities" : { |
| "_values" : [ |
| { |
| "attachments" : { |
| "_values" : [ |
| { |
| "filename" : { |
| "_value" : "Screenshot_23D95D0E-8B97-4F99-BE3C-A46EDE5999D7.jpg" |
| }, |
| "payloadRef" : { |
| "id" : { |
| "_value" : "SCREENSHOT_REF_ID_2" |
| } |
| } |
| } |
| ] |
| }, |
| "subactivities" : { |
| "_values" : [ |
| { |
| "subactivities" : { |
| "_values" : [ |
| { |
| "attachments" : { |
| "_values" : [ |
| { |
| "filename" : { |
| "_value" : "Crash_3F0A2B1C-7ADA-436E-A54C-D4C39B8411F8.crash" |
| }, |
| "payloadRef" : { |
| "id" : { |
| "_value" : "CRASH_REF_ID_IN_ACTIVITY_SUMMARIES" |
| } |
| } |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Wait for org.chromium.ios-web-shell-eg2tests to idle" |
| } |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Activate org.chromium.ios-web-shell-eg2tests" |
| } |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Open org.chromium.ios-web-shell-eg2tests" |
| } |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Set Up" |
| } |
| }, |
| { |
| "title" : { |
| "_value" : "Find the Target Application 'org.chromium.ios-web-shell-eg2tests'" |
| } |
| }, |
| { |
| "attachments" : { |
| "_values" : [ |
| { |
| "filename" : { |
| "_value" : "Screenshot_278BA84B-2196-4CCD-9D31-2C07DDDC9DFC.jpg" |
| }, |
| "payloadRef" : { |
| "id" : { |
| "_value" : "SCREENSHOT_REF_ID_3" |
| } |
| } |
| |
| } |
| ] |
| }, |
| "title" : { |
| "_value" : "Uncaught Exception at page_state_egtest.mm:131: \\nCannot scroll, the..." |
| } |
| }, |
| { |
| "title" : { |
| "_value" : "Uncaught Exception: Immediately halt execution of testcase (EarlGreyInternalTestInterruptException)" |
| } |
| }, |
| { |
| "title" : { |
| "_value" : "Tear Down" |
| } |
| } |
| ] |
| }, |
| "failureSummaries" : { |
| "_values" : [ |
| { |
| "attachments" : { |
| "_values" : [ |
| { |
| "filename" : { |
| "_value" : "kXCTAttachmentLegacyScreenImageData_1_6CED1FE5-96CA-47EA-9852-6FADED687262.jpeg" |
| }, |
| "payloadRef" : { |
| "id" : { |
| "_value" : "SCREENSHOT_REF_ID_IN_FAILURE_SUMMARIES" |
| } |
| } |
| } |
| ] |
| }, |
| "fileName" : { |
| "_value" : "\/..\/..\/ios\/web\/shell\/test\/page_state_egtest.mm" |
| }, |
| "lineNumber" : { |
| "_value" : "131" |
| }, |
| "message" : { |
| "_value" : "Some logs." |
| } |
| }, |
| { |
| "message" : { |
| "_value" : "Immediately halt execution of testcase (EarlGreyInternalTestInterruptException)" |
| } |
| } |
| ] |
| }, |
| "identifier" : { |
| "_value" : "PageStateTestCase\/testZeroContentOffsetAfterLoad" |
| }, |
| "name" : { |
| "_value" : "testZeroContentOffsetAfterLoad" |
| }, |
| "testStatus" : { |
| "_value" : "Failure" |
| } |
| }""" |
| |
| APP_SIDE_FAILURE_LOG = """Will attempt to recover by breaking constraint |
| <NSLayoutConstraint:0x1000444e0e0 H:[UIView:0x100031b0fc0]-(4)-[UIView:0x100031b1180] (active)> |
| |
| Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. |
| The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. |
| [1009/111922.254778:WARNING:base_earl_grey_test_case_app_interface.mm(21)] ********************************* |
| Starting test: -[SmokeTestCase testOpenTab] |
| 2023-10-09 09:25:00.318076-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] Unable to simultaneously satisfy constraints. |
| Probably at least one of the constraints in the following list is one you don't want. |
| Try this: |
| (1) look at each constraint and try to figure out which you don't expect; |
| (2) find the code that added the unwanted constraint or constraints and fix it. |
| ( |
| "<NSLayoutConstraint:0x12003d1a6e0 H:|-(0)-[UIView:0x12002478a80] (active, names: '|':UIView:0x120029d4380 )>", |
| "<NSLayoutConstraint:0x12003d1a680 UIView:0x12002478a80.trailing == UIView:0x120029d4380.trailing (active)>", |
| "<NSLayoutConstraint:0x12003d1a440 H:|-(8)-[UIView:0x1200247d080] (active, names: '|':UIView:0x12002478a80 )>", |
| "<NSLayoutConstraint:0x12003d18340 H:[UIView:0x1200247d080]-(4)-[UIView:0x1200247ec80] (active)>", |
| "<NSLayoutConstraint:0x12003d182e0 UIView:0x1200247ec80.trailing == UIView:0x12002478a80.trailing - 8 (active)>", |
| "<NSLayoutConstraint:0x12003d42a00 'UIView-Encapsulated-Layout-Width' UIView:0x120029d4380.width == 0 (active)>" |
| ) |
| |
| Will attempt to recover by breaking constraint |
| <NSLayoutConstraint:0x12003d18340 H:[UIView:0x1200247d080]-(4)-[UIView:0x1200247ec80] (active)> |
| |
| Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. |
| The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. |
| 2023-10-09 09:25:01.480402-0700 ios_chrome_eg2tests[56690:4719583] [unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled |
| 2023-10-09 09:25:02.383910-0700 ios_chrome_eg2tests[56690:4719754] [VoiceShortcutClient] -[VCVoiceShortcutClient unsafeSetupXPCConnection]_block_invoke Client connection to VCVoiceShortcut XPC server interrupted |
| 2023-10-09 09:25:02.385353-0700 ios_chrome_eg2tests[56690:4719754] [Intents] -[INVoiceShortcutCenter getAllVoiceShortcutsWithCompletion:]_block_invoke Error from -getVoiceShortcutsWithCompletion: Error Domain=NSCocoaErrorDomain Code=4097 "Couldn’t communicate with a helper application." |
| [1009/092503.896821:ERROR:loopback_server.cc(907)] Loopback sync cannot read the persistent state file (/Users/chrome-bot/Library/Developer/CoreSimulator/Devices/82CF3734-9FF2-4C1B-920C-B3345C0CA891/data/Containers/Data/Application/F2938797-9668-4622-947A-895503B62BCD/tmp/.org.chromium.ost.chrome.unittests.dev.lxaMyc/profile.pb) with error FILE_ERROR_NOT_FOUND |
| 2023-10-09 09:25:03.964248-0700 ios_chrome_eg2tests[56690:4719583] [unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled |
| 2023-10-09 09:25:06.074170-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] Unable to simultaneously satisfy constraints. |
| Probably at least one of the constraints in the following list is one you don't want. |
| Try this: |
| (1) look at each constraint and try to figure out which you don't expect; |
| (2) find the code that added the unwanted constraint or constraints and fix it. |
| ( |
| "<NSLayoutConstraint:0x12003d2a8c0 H:|-(0)-[UIView:0x1200235e300] (active, names: '|':UIView:0x12002a10fc0 )>", |
| "<NSLayoutConstraint:0x12003d20360 UIView:0x1200235e300.trailing == UIView:0x12002a10fc0.trailing (active)>", |
| "<NSLayoutConstraint:0x12003d21d40 H:|-(8)-[UIView:0x1200235f640] (active, names: '|':UIView:0x1200235e300 )>", |
| "<NSLayoutConstraint:0x12003d24b60 UIView:0x1200235f640.width == 16 (active)>", |
| "<NSLayoutConstraint:0x12003d9c200 H:[UIView:0x1200235f640]-(4)-[UIView:0x1200235c380] (active)>", |
| "<NSLayoutConstraint:0x12003d98a20 UIView:0x1200235c380.trailing == UIView:0x1200235e300.trailing - 8 (active)>", |
| "<NSLayoutConstraint:0x12001326f60 'UIView-Encapsulated-Layout-Width' UIView:0x12002a10fc0.width == 0 (active)>" |
| ) |
| |
| Will attempt to recover by breaking constraint |
| <NSLayoutConstraint:0x12003d24b60 UIView:0x1200235f640.width == 16 (active)> |
| |
| Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. |
| The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. |
| 2023-10-09 09:25:06.075364-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] Unable to simultaneously satisfy constraints. |
| Probably at least one of the constraints in the following list is one you don't want. |
| Try this: |
| (1) look at each constraint and try to figure out which you don't expect; |
| (2) find the code that added the unwanted constraint or constraints and fix it. |
| ( |
| "<NSLayoutConstraint:0x12003d2a8c0 H:|-(0)-[UIView:0x1200235e300] (active, names: '|':UIView:0x12002a10fc0 )>", |
| "<NSLayoutConstraint:0x12003d20360 UIView:0x1200235e300.trailing == UIView:0x12002a10fc0.trailing (active)>", |
| "<NSLayoutConstraint:0x12003d21d40 H:|-(8)-[UIView:0x1200235f640] (active, names: '|':UIView:0x1200235e300 )>", |
| "<NSLayoutConstraint:0x12003d9c200 H:[UIView:0x1200235f640]-(4)-[UIView:0x1200235c380] (active)>", |
| "<NSLayoutConstraint:0x12003d98a20 UIView:0x1200235c380.trailing == UIView:0x1200235e300.trailing - 8 (active)>", |
| "<NSLayoutConstraint:0x12001326f60 'UIView-Encapsulated-Layout-Width' UIView:0x12002a10fc0.width == 0 (active)>" |
| ) |
| |
| Will attempt to recover by breaking constraint |
| <NSLayoutConstraint:0x12003d9c200 H:[UIView:0x1200235f640]-(4)-[UIView:0x1200235c380] (active)> |
| |
| Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. |
| The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. |
| [1009/111926.491858:FATAL:chrome_earl_grey_app_interface.mm(147)] Check failed: NO. |
| 0 ios_chrome_eg2testsMain 0x000000012a31b174 base::debug::CollectStackTrace(void const**, unsigned long) + 48 |
| 1 ios_chrome_eg2testsMain 0x000000012a2ed878 base::debug::StackTrace::StackTrace(unsigned long) + 92 |
| 2 ios_chrome_eg2testsMain 0x000000012a2ed910 base::debug::StackTrace::StackTrace(unsigned long) + 36 |
| 3 ios_chrome_eg2testsMain 0x000000012a2ed8dc base::debug::StackTrace::StackTrace() + 40 |
| 4 ios_chrome_eg2testsMain 0x000000012a03d7e0 logging::LogMessage::~LogMessage() + 204 |
| 5 ios_chrome_eg2testsMain 0x000000012a03e748 logging::LogMessage::~LogMessage() + 28 |
| 6 ios_chrome_eg2testsMain 0x000000012a03e774 logging::LogMessage::~LogMessage() + 28 |
| 7 ios_chrome_eg2testsMain 0x000000012a00bba8 logging::CheckError::~CheckError() + 112 |
| 8 ios_chrome_eg2testsMain 0x000000012a00bc08 logging::CheckError::~CheckError() + 28 |
| 9 ios_chrome_eg2testsMain 0x0000000126745008 +[ChromeEarlGreyAppInterface crashApp] + 104 |
| more of the stack trace and crash report logs... |
| |
| Standard output and standard error from com.google.chrome.unittests.dev with process ID 1358 beginning at 2023-10-09 15:19:36 +0000 |
| |
| 2023-10-09 11:19:37.449520-0400 ios_chrome_eg2tests[1358:24891823] [User Defaults] Not updating lastKnownShmemState in CFPrefsPlistSource<0x6000030083f0> (Domain: com.apple.keyboard.preferences.plist, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: Yes): 0 -> 323 |
| 2023-10-09 11:19:37.449594-0400 ios_chrome_eg2tests[1358:24891823] [User Defaults] Source was stale because shmem was null: CFPrefsPlistSource<0x6000030083f0> (Domain: com.apple.keyboard.preferences.plist, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: Yes) |
| |
| """ |
| |
| APP_SIDE_FAILURE_LOG_EXPECTED = f"""App crashed and disconnected. |
| Showing logs from application under test. For complete logs see attempt_0_simulator#0_StandardOutputAndStandardError-com.google.chrome.unittests.dev.txt in CAS outputs, which can be found in the swarming task of the shard this test ran on. |
| |
| Starting test: -[SmokeTestCase testOpenTab] |
| 2023-10-09 09:25:00.318076-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] {constants.LAYOUT_CONSTRAINT_MSG}. |
| 2023-10-09 09:25:01.480402-0700 ios_chrome_eg2tests[56690:4719583] [unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled |
| 2023-10-09 09:25:02.383910-0700 ios_chrome_eg2tests[56690:4719754] [VoiceShortcutClient] -[VCVoiceShortcutClient unsafeSetupXPCConnection]_block_invoke Client connection to VCVoiceShortcut XPC server interrupted |
| 2023-10-09 09:25:02.385353-0700 ios_chrome_eg2tests[56690:4719754] [Intents] -[INVoiceShortcutCenter getAllVoiceShortcutsWithCompletion:]_block_invoke Error from -getVoiceShortcutsWithCompletion: Error Domain=NSCocoaErrorDomain Code=4097 "Couldn’t communicate with a helper application." |
| [1009/092503.896821:ERROR:loopback_server.cc(907)] Loopback sync cannot read the persistent state file (/Users/chrome-bot/Library/Developer/CoreSimulator/Devices/82CF3734-9FF2-4C1B-920C-B3345C0CA891/data/Containers/Data/Application/F2938797-9668-4622-947A-895503B62BCD/tmp/.org.chromium.ost.chrome.unittests.dev.lxaMyc/profile.pb) with error FILE_ERROR_NOT_FOUND |
| 2023-10-09 09:25:03.964248-0700 ios_chrome_eg2tests[56690:4719583] [unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled |
| 2023-10-09 09:25:06.074170-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] {constants.LAYOUT_CONSTRAINT_MSG}. |
| 2023-10-09 09:25:06.075364-0700 ios_chrome_eg2tests[56690:4719583] [LayoutConstraints] {constants.LAYOUT_CONSTRAINT_MSG}. |
| [1009/111926.491858:FATAL:chrome_earl_grey_app_interface.mm(147)] Check failed: NO. |
| 0 ios_chrome_eg2testsMain 0x000000012a31b174 base::debug::CollectStackTrace(void const**, unsigned long) + 48 |
| 1 ios_chrome_eg2testsMain 0x000000012a2ed878 base::debug::StackTrace::StackTrace(unsigned long) + 92 |
| 2 ios_chrome_eg2testsMain 0x000000012a2ed910 base::debug::StackTrace::StackTrace(unsigned long) + 36 |
| 3 ios_chrome_eg2testsMain 0x000000012a2ed8dc base::debug::StackTrace::StackTrace() + 40 |
| 4 ios_chrome_eg2testsMain 0x000000012a03d7e0 logging::LogMessage::~LogMessage() + 204 |
| 5 ios_chrome_eg2testsMain 0x000000012a03e748 logging::LogMessage::~LogMessage() + 28 |
| 6 ios_chrome_eg2testsMain 0x000000012a03e774 logging::LogMessage::~LogMessage() + 28 |
| 7 ios_chrome_eg2testsMain 0x000000012a00bba8 logging::CheckError::~CheckError() + 112 |
| 8 ios_chrome_eg2testsMain 0x000000012a00bc08 logging::CheckError::~CheckError() + 28 |
| 9 ios_chrome_eg2testsMain 0x0000000126745008 +[ChromeEarlGreyAppInterface crashApp] + 104 |
| more of the stack trace and crash report logs... |
| |
| |
| """ |
| |
| |
| def _xcresulttool_get_side_effect(xcresult_path, ref_id=None): |
| """Side effect for _xcresulttool_get in XcodeLogParser tested.""" |
| if ref_id is None: |
| return XCRESULT_ROOT |
| if ref_id == 'testsRef': |
| return TESTS_REF |
| # Other situation in use cases of xcode_log_parser is asking for single test |
| # summary ref. |
| return SINGLE_TEST_SUMMARY_REF |
| |
| |
| class UtilMethodsTest(test_runner_test.TestCase): |
| """Test case for utility methods not related with Parser class.""" |
| |
| def setUp(self): |
| self.summary_xcode16_with_parallel = { |
| 'tests': { |
| '_values': ['TestSuite1', 'TestSuite2'] |
| } |
| } |
| |
| # Example test summary when running xcode version lower than 16. |
| # It could also be when running xcode version 16 without xcode |
| # parallelization enabled. |
| self.summary_pre_xcode16 = { |
| 'tests': { |
| '_values': [{ |
| 'subtests': { |
| '_values': [{ |
| 'subtests': { |
| '_values': ['TestSuite1', 'TestSuite2'] |
| } |
| }] |
| } |
| }] |
| } |
| } |
| |
| def testParseTestsForInterruptedRun(self): |
| test_output = """ |
| Test case '-[DownloadManagerTestCase testVisibleFileNameAndOpenInDownloads]' passed on 'Clone 2 of iPhone X 15.0 test simulator - ios_chrome_ui_eg2tests_module-Runner (34498)' (20.715 seconds) |
| Test case '-[SyncFakeServerTestCase testSyncDownloadBookmark]' passed on 'Clone 1 of iPhone X 15.0 test simulator - ios_chrome_ui_eg2tests_module-Runner (34249)' (14.880 seconds) |
| Random lines |
| t = 53.90s Tear Down |
| Test Case '-[LinkToTextTestCase testGenerateLinkForSimpleText]' failed (55.316 seconds). |
| t = nans Suite Tear Down |
| Test Suite 'LinkToTextTestCase' failed at 2021-06-15 07:13:17.406. |
| Executed 1 test, with 6 failures (6 unexpected) in 55.316 (55.338) seconds |
| Test Suite 'ios_chrome_ui_eg2tests_module.xctest' failed at 2021-06-15 07:13:17.407. |
| Executed 1 test, with 6 failures (6 unexpected) in 55.316 (55.340) seconds |
| Test Suite 'Selected tests' failed at 2021-06-15 07:13:17.408. |
| Executed 1 test, with 6 failures (6 unexpected) in 55.316 (55.342) seconds |
| """ |
| test_output_list = test_output.split('\n') |
| expected_passed = set([ |
| 'DownloadManagerTestCase/testVisibleFileNameAndOpenInDownloads', |
| 'SyncFakeServerTestCase/testSyncDownloadBookmark' |
| ]) |
| expected_failed = set(['LinkToTextTestCase/testGenerateLinkForSimpleText']) |
| expected_failed_message = 'Test failed in interrupted(timedout) run.' |
| |
| results = xcode_log_parser.parse_passed_failed_tests_for_interrupted_run( |
| test_output_list) |
| self.assertEqual(results.expected_tests(), expected_passed) |
| self.assertEqual(results.unexpected_tests(), expected_failed) |
| for result in results.test_results: |
| if result.name == 'LinkToTextTestCase/testGenerateLinkForSimpleText': |
| self.assertEqual(result.test_log, expected_failed_message) |
| |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def test_xcode16_parallel(self, mock_xcode_version): |
| mock_xcode_version.return_value = True |
| result = xcode_log_parser.get_test_suites( |
| self.summary_xcode16_with_parallel, True) |
| self.assertEqual(result, ['TestSuite1', 'TestSuite2']) |
| |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def test_xcode16_not_parallel(self, mock_xcode_version): |
| mock_xcode_version.return_value = True |
| result = xcode_log_parser.get_test_suites(self.summary_pre_xcode16, False) |
| self.assertEqual(result, ['TestSuite1', 'TestSuite2']) |
| |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def test_pre_xcode16_parallel(self, mock_xcode_version): |
| mock_xcode_version.return_value = False |
| result = xcode_log_parser.get_test_suites(self.summary_pre_xcode16, True) |
| self.assertEqual(result, ['TestSuite1', 'TestSuite2']) |
| |
| |
| class XcodeLogParserTest(test_runner_test.TestCase): |
| """Test case to test XcodeLogParser.""" |
| |
| def setUp(self): |
| super(XcodeLogParserTest, self).setUp() |
| self.mock(test_runner, 'get_current_xcode_info', lambda: XCODE11_DICT) |
| |
| @mock.patch('subprocess.check_output', autospec=True) |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def testXcresulttoolGetRoot(self, mock_xcode_version, mock_process): |
| mock_xcode_version.return_value = False |
| mock_process.return_value = b'%JSON%' |
| xcode_log_parser.XcodeLogParser()._xcresulttool_get('xcresult_path') |
| self.assertTrue( |
| os.path.join(XCODE11_DICT['path'], 'usr', 'bin') in os.environ['PATH']) |
| self.assertEqual( |
| ['xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path'], |
| mock_process.mock_calls[0][1][0]) |
| |
| @mock.patch('subprocess.check_output', autospec=True) |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def testXcresulttoolGetRef(self, mock_xcode_version, mock_process): |
| mock_xcode_version.return_value = False |
| mock_process.side_effect = [REF_ID, b'JSON'] |
| xcode_log_parser.XcodeLogParser()._xcresulttool_get('xcresult_path', |
| 'testsRef') |
| self.assertEqual( |
| ['xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path'], |
| mock_process.mock_calls[0][1][0]) |
| self.assertEqual([ |
| 'xcresulttool', 'get', '--format', 'json', '--path', 'xcresult_path', |
| '--id', 'REF_ID'], mock_process.mock_calls[1][1][0]) |
| |
| def testXcresulttoolListFailedTests(self): |
| failure_message = ( |
| 'file:///../../ios/web/shell/test/page_state_egtest.mm#' |
| 'CharacterRangeLen=0&EndingLineNumber=130&StartingLineNumber=130\n' |
| 'Fail. Screenshots: {\n\"Failure\": \"path.png\"\n}') |
| expected = set(['PageStateTestCase/testZeroContentOffsetAfterLoad']) |
| results = xcode_log_parser.XcodeLogParser()._list_of_failed_tests( |
| json.loads(XCRESULT_ROOT)) |
| self.assertEqual(expected, results.failed_tests()) |
| log = results.test_results[0].test_log |
| self.assertEqual(log, failure_message) |
| |
| def testXcresulttoolListFailedTestsExclude(self): |
| excluded = set(['PageStateTestCase/testZeroContentOffsetAfterLoad']) |
| results = xcode_log_parser.XcodeLogParser()._list_of_failed_tests( |
| json.loads(XCRESULT_ROOT), excluded=excluded) |
| self.assertEqual(set([]), results.all_test_names()) |
| |
| @mock.patch('xcode_log_parser.XcodeLogParser._export_data') |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| def testGetTestStatuses(self, mock_xcresult, mock_export): |
| mock_xcresult.side_effect = _xcresulttool_get_side_effect |
| # self.assertEqual(test_result.test_log, lo |
| expected_failure_log = ( |
| 'Logs from "failureSummaries" in .xcresult:\n' |
| 'file: /../../ios/web/shell/test/page_state_egtest.mm, line: 131\n' |
| 'Some logs.\n' |
| 'file: , line: \n' |
| 'Immediately halt execution of testcase ' |
| '(EarlGreyInternalTestInterruptException)\n') |
| expected_expected_tests = set([ |
| 'PageStateTestCase/testMethod1', 'PageStateTestCase/testMethod2', |
| 'PageStateTestCase/testMethod3' |
| ]) |
| results = xcode_log_parser.XcodeLogParser()._get_test_statuses( |
| OUTPUT_PATH, False) |
| self.assertEqual(expected_expected_tests, results.expected_tests()) |
| seen_failed_test = False |
| for test_result in results.test_results: |
| if test_result.name == 'PageStateTestCase/testZeroContentOffsetAfterLoad': |
| seen_failed_test = True |
| self.assertEqual(test_result.test_log, expected_failure_log) |
| self.assertEqual(test_result.duration, None) |
| crash_file_name = ( |
| 'attempt_0_PageStateTestCase_testZeroContentOffsetAfterLoad_' |
| 'Crash_3F0A2B1C-7ADA-436E-A54C-D4C39B8411F8.crash' |
| ) |
| jpeg_file_name = ( |
| 'attempt_0_PageStateTestCase_testZeroContentOffsetAfterLoad' |
| '_kXCTAttachmentLegacyScreenImageData_1' |
| '_6CED1FE5-96CA-47EA-9852-6FADED687262.jpeg') |
| self.assertDictEqual( |
| { |
| crash_file_name: '/tmp/%s' % crash_file_name, |
| jpeg_file_name: '/tmp/%s' % jpeg_file_name, |
| }, test_result.attachments) |
| if test_result.name == 'PageStateTestCase/testMethod1': |
| self.assertEqual(test_result.duration, 35384) |
| if test_result.name == 'PageStateTestCase/testMethod2': |
| self.assertEqual(test_result.duration, 28988) |
| if test_result.name == 'PageStateTestCase/testMethod3': |
| self.assertEqual(test_result.duration, 60) |
| |
| self.assertTrue(seen_failed_test) |
| |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('xcode_log_parser.XcodeLogParser._extract_artifacts_for_test') |
| @mock.patch('xcode_log_parser.XcodeLogParser.export_diagnostic_data') |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| def testCollectTestTesults(self, mock_root, mock_exist_file, *args): |
| expected_passed = set([ |
| 'PageStateTestCase/testMethod1', 'PageStateTestCase/testMethod2', |
| 'PageStateTestCase/testMethod3' |
| ]) |
| expected_failed = set(['PageStateTestCase/testZeroContentOffsetAfterLoad']) |
| |
| mock_root.side_effect = _xcresulttool_get_side_effect |
| mock_exist_file.return_value = True |
| results = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, []) |
| |
| # Length ensures no duplicate results from |_get_test_statuses| and |
| # |_list_of_failed_tests|. |
| self.assertEqual(len(results.test_results), 4) |
| self.assertEqual(expected_passed, results.expected_tests()) |
| self.assertEqual(expected_failed, results.unexpected_tests()) |
| # Ensure format. |
| for test in results.test_results: |
| self.assertTrue(isinstance(test.name, str)) |
| if test.status == TestStatus.FAIL: |
| self.assertTrue(isinstance(test.test_log, str)) |
| |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('xcode_log_parser.XcodeLogParser.copy_artifacts') |
| @mock.patch('xcode_log_parser.XcodeLogParser.export_diagnostic_data') |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| def testCollectTestsRanZeroTests(self, mock_root, mock_exist_file, *args): |
| metrics_json = '{"actions": {}}' |
| mock_root.return_value = metrics_json |
| mock_exist_file.return_value = True |
| results = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, []) |
| self.assertTrue(results.crashed) |
| self.assertEqual(results.crash_message, '0 tests executed!') |
| self.assertEqual(len(results.all_test_names()), 0) |
| |
| @mock.patch('xcode_log_parser.XcodeLogParser._list_of_failed_tests') |
| @mock.patch('xcode_log_parser.XcodeLogParser._get_test_statuses') |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('xcode_log_parser.XcodeLogParser.copy_artifacts') |
| @mock.patch('xcode_log_parser.XcodeLogParser.export_diagnostic_data') |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| def testFallbackOnRootMetrics(self, mock_root, mock_exist_file, *args): |
| mock_root.return_value = XCRESULT_MISSING_ACTIONRESULT_METRICS |
| mock_exist_file.return_value = True |
| results = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, []) |
| self.assertTrue(results.crashed != True) |
| self.assertNotEqual(results.crash_message, '0 tests executed!') |
| |
| @mock.patch('os.path.exists', autospec=True) |
| def testCollectTestsDidNotRun(self, mock_exist_file): |
| mock_exist_file.return_value = False |
| results = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, []) |
| self.assertTrue(results.crashed) |
| self.assertEqual(results.crash_message, |
| '/tmp/attempt_0 with staging data does not exist.\n') |
| self.assertEqual(len(results.all_test_names()), 0) |
| |
| @mock.patch('os.path.exists', autospec=True) |
| def testCollectTestsInterruptedRun(self, mock_exist_file): |
| mock_exist_file.side_effect = [True, False] |
| results = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, []) |
| self.assertTrue(results.crashed) |
| self.assertEqual( |
| results.crash_message, |
| '/tmp/attempt_0.xcresult with test results does not exist.\n') |
| self.assertEqual(len(results.all_test_names()), 0) |
| |
| @mock.patch('subprocess.check_output', autospec=True) |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def testCopyScreenshots(self, mock_xcode_version, mock_xcresulttool_get, |
| mock_path_exists, mock_process): |
| mock_xcode_version.return_value = False |
| mock_path_exists.return_value = True |
| mock_xcresulttool_get.side_effect = _xcresulttool_get_side_effect |
| xcode_log_parser.XcodeLogParser().copy_artifacts(OUTPUT_PATH) |
| mock_process.assert_any_call([ |
| 'xcresulttool', 'export', '--type', 'file', '--id', |
| 'SCREENSHOT_REF_ID_IN_FAILURE_SUMMARIES', '--path', XCRESULT_PATH, |
| '--output-path', |
| '/tmp/attempt_0_PageStateTestCase_testZeroContentOffsetAfterLoad' |
| '_kXCTAttachmentLegacyScreenImageData_1' |
| '_6CED1FE5-96CA-47EA-9852-6FADED687262.jpeg' |
| ]) |
| mock_process.assert_any_call([ |
| 'xcresulttool', 'export', '--type', 'file', '--id', |
| 'CRASH_REF_ID_IN_ACTIVITY_SUMMARIES', '--path', XCRESULT_PATH, |
| '--output-path', |
| '/tmp/attempt_0_PageStateTestCase_testZeroContentOffsetAfterLoad' |
| '_Crash_3F0A2B1C-7ADA-436E-A54C-D4C39B8411F8.crash' |
| ]) |
| # Ensures screenshots in activitySummaries are not copied. |
| self.assertEqual(2, mock_process.call_count) |
| |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('subprocess.check_output', autospec=True) |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def testExportDiagnosticData(self, mock_xcode_version, mock_xcresulttool_get, |
| mock_path_exists, mock_process, _): |
| mock_xcode_version.return_value = False |
| mock_path_exists.return_value = True |
| mock_xcresulttool_get.side_effect = _xcresulttool_get_side_effect |
| xcode_log_parser.XcodeLogParser.export_diagnostic_data(OUTPUT_PATH) |
| mock_process.assert_called_with([ |
| 'xcresulttool', 'export', '--type', 'directory', '--id', |
| 'DIAGNOSTICS_REF_ID', '--path', XCRESULT_PATH, '--output-path', |
| '/tmp/attempt_0.xcresult_diagnostic' |
| ]) |
| |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('shutil.copy') |
| @mock.patch('subprocess.check_output', autospec=True) |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| @mock.patch('xcode_util.using_xcode_16_or_higher') |
| def testStdoutCopiedInExportDiagnosticData(self, mock_xcode_version, |
| mock_xcresulttool_get, |
| mock_path_exists, mock_process, |
| mock_copy, _): |
| mock_xcode_version.return_value = False |
| output_path_in_test = 'test_data/attempt_0' |
| xcresult_path_in_test = 'test_data/attempt_0.xcresult' |
| mock_path_exists.return_value = True |
| mock_xcresulttool_get.side_effect = _xcresulttool_get_side_effect |
| xcode_log_parser.XcodeLogParser.export_diagnostic_data( |
| output_path_in_test) |
| # os.walk() walks folders in unknown sequence. Use try-except blocks to |
| # assert that any of the 2 assertions is true. |
| try: |
| mock_copy.assert_any_call( |
| 'test_data/attempt_0.xcresult_diagnostic/test_module-UUID/test_module-UUID1/StandardOutputAndStandardError.txt', |
| 'test_data/attempt_0/../attempt_0_simulator#1_StandardOutputAndStandardError.txt' |
| ) |
| except AssertionError: |
| mock_copy.assert_any_call( |
| 'test_data/attempt_0.xcresult_diagnostic/test_module-UUID/test_module-UUID1/StandardOutputAndStandardError.txt', |
| 'test_data/attempt_0/../attempt_0_simulator#0_StandardOutputAndStandardError.txt' |
| ) |
| try: |
| mock_copy.assert_any_call( |
| 'test_data/attempt_0.xcresult_diagnostic/test_module-UUID/test_module-UUID2/StandardOutputAndStandardError-org.chromium.gtest.ios-chrome-eg2tests.txt', |
| 'test_data/attempt_0/../attempt_0_simulator#1_StandardOutputAndStandardError-org.chromium.gtest.ios-chrome-eg2tests.txt' |
| ) |
| except AssertionError: |
| mock_copy.assert_any_call( |
| 'test_data/attempt_0.xcresult_diagnostic/test_module-UUID/test_module-UUID2/StandardOutputAndStandardError-org.chromium.gtest.ios-chrome-eg2tests.txt', |
| 'test_data/attempt_0/../attempt_0_simulator#0_StandardOutputAndStandardError-org.chromium.gtest.ios-chrome-eg2tests.txt' |
| ) |
| |
| @mock.patch('os.path.exists', autospec=True) |
| def testCollectTestResults_interruptedTests(self, mock_path_exists): |
| mock_path_exists.side_effect = [True, False] |
| output = [ |
| '[09:03:42:INFO] Test case \'-[TestCase1 method1]\' passed on device.', |
| '[09:06:40:INFO] Test Case \'-[TestCase2 method1]\' passed on device.', |
| '[09:09:00:INFO] Test case \'-[TestCase2 method1]\' failed on device.', |
| '** BUILD INTERRUPTED **', |
| ] |
| not_found_message = ['%s with test results does not exist.' % XCRESULT_PATH] |
| res = xcode_log_parser.XcodeLogParser().collect_test_results( |
| OUTPUT_PATH, output) |
| self.assertTrue(res.crashed) |
| self.assertEqual('\n'.join(not_found_message + output), res.crash_message) |
| self.assertEqual( |
| set(['TestCase1/method1', 'TestCase2/method1']), res.expected_tests()) |
| |
| @mock.patch('file_util.zip_and_remove_folder') |
| @mock.patch('xcode_log_parser.XcodeLogParser._extract_artifacts_for_test') |
| @mock.patch('xcode_log_parser.XcodeLogParser.export_diagnostic_data') |
| @mock.patch('os.path.exists', autospec=True) |
| @mock.patch('xcode_log_parser.XcodeLogParser._xcresulttool_get') |
| @mock.patch('xcode_log_parser.XcodeLogParser._list_of_failed_tests') |
| def testArtifactsDiagnosticLogsExportedInCollectTestTesults( |
| self, mock_get_failed_tests, mock_root, mock_exist_file, |
| mock_export_diagnostic_data, mock_extract_artifacts, mock_zip): |
| mock_root.side_effect = _xcresulttool_get_side_effect |
| mock_exist_file.return_value = True |
| xcode_log_parser.XcodeLogParser().collect_test_results(OUTPUT_PATH, []) |
| mock_export_diagnostic_data.assert_called_with(OUTPUT_PATH) |
| mock_extract_artifacts.assert_called() |
| |
| @mock.patch('os.listdir') |
| @mock.patch( |
| 'builtins.open', new=mock.mock_open(read_data=APP_SIDE_FAILURE_LOG)) |
| def testLogAppSideFailureReason(self, mock_listdir): |
| test_name = 'SmokeTestCase/testOpenTab' |
| mock_listdir.return_value = [ |
| 'run_1696864672.xctestrun', 'attempt_0.xcresult.zip', |
| 'attempt_0.xcresult_diagnostic.zip', |
| 'attempt_0_simulator#0_StandardOutputAndStandardError-com.google.chrome.unittests.dev.txt', |
| 'attempt_0_simulator#0_StandardOutputAndStandardError.txt', |
| ] |
| app_side_failure_message = \ |
| xcode_log_parser.XcodeLogParser()._get_app_side_failure( |
| test_name, OUTPUT_PATH) |
| self.assertEqual(app_side_failure_message, APP_SIDE_FAILURE_LOG_EXPECTED) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |