| # -*- coding: utf-8 -*- |
| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module containing unit tests for failure_message_lib.""" |
| |
| from __future__ import print_function |
| |
| from chromite.lib import constants |
| from chromite.lib import cros_test_lib |
| from chromite.lib import failure_message_lib |
| |
| |
| DEFAULT_BUILD_SCRIPT_FAILURE_EXTRA_INFO = ( |
| '{\"shortname\": \"security_test_image\"}') |
| DEFAULT_PACKAGE_BUILD_FAILURE_EXTRA_INFO = ( |
| '{\"shortname\": \"./build_image\", \"failed_packages\":' |
| '[\"chromeos-base/chromeos-chrome\", \"chromeos-base/chromeos-init\"]}') |
| |
| |
| class StageFailureHelper(object): |
| """Helper method to create StageFailure instances for test.""" |
| |
| @classmethod |
| def CreateStageFailure( |
| cls, failure_id=1, build_stage_id=1, outer_failure_id=None, |
| exception_type='StepFailure', exception_message='exception message', |
| exception_category=constants.EXCEPTION_CATEGORY_UNKNOWN, |
| extra_info=None, timestamp=None, stage_name='stage_1', board='board_1', |
| stage_status=constants.BUILDER_STATUS_PASSED, build_id=1, |
| master_build_id=None, builder_name='builder_name_1', |
| build_number='build_number_1', |
| build_config='config_1', build_status=constants.BUILDER_STATUS_PASSED, |
| important=True, buildbucket_id='bb_id'): |
| return failure_message_lib.StageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, timestamp, |
| stage_name, board, stage_status, build_id, master_build_id, |
| builder_name, build_number, build_config, build_status, |
| important, buildbucket_id) |
| |
| @classmethod |
| def GetStageFailure( |
| cls, |
| failure_id=1, |
| build_stage_id=1, |
| outer_failure_id=None, |
| exception_type='StepFailure', |
| exception_message='exception msg', |
| exception_category=constants.EXCEPTION_CATEGORY_UNKNOWN, |
| extra_info=None, |
| stage_name='Paygen', |
| build_config=None): |
| return cls.CreateStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, None, stage_name, |
| None, None, None, None, None, None, build_config, None, None, |
| None) |
| |
| |
| class StageFailureTests(cros_test_lib.TestCase): |
| """Tests for StageFailure.""" |
| |
| def testGetStageFailureFromMessage(self): |
| """Test GetStageFailureFromMessage.""" |
| failure_message = FailureMessageHelper.GetStageFailureMessage() |
| stage_failure = failure_message_lib.StageFailure.GetStageFailureFromMessage( |
| failure_message) |
| |
| self.assertEqual(stage_failure.id, 1) |
| self.assertEqual(stage_failure.exception_type, 'StepFailure') |
| self.assertEqual(stage_failure.exception_category, |
| constants.EXCEPTION_CATEGORY_UNKNOWN) |
| |
| |
| class FailureMessageHelper(object): |
| """Helper class to help create stage failure message instances for test.""" |
| |
| @classmethod |
| def GetStageFailureMessage( |
| cls, |
| failure_id=1, |
| build_stage_id=1, |
| outer_failure_id=None, |
| exception_type='StepFailure', |
| exception_message='exception msg', |
| exception_category=constants.EXCEPTION_CATEGORY_UNKNOWN, |
| extra_info=None, |
| stage_name='Paygen'): |
| stage_failure = StageFailureHelper.GetStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, stage_name) |
| |
| return failure_message_lib.StageFailureMessage(stage_failure) |
| |
| @classmethod |
| def GetBuildScriptFailureMessage( |
| cls, |
| failure_id=2, |
| build_stage_id=0, |
| outer_failure_id=None, |
| exception_type='BuildScriptFailure', |
| exception_message='exception msg', |
| exception_category=constants.EXCEPTION_CATEGORY_BUILD, |
| extra_info=DEFAULT_BUILD_SCRIPT_FAILURE_EXTRA_INFO, |
| stage_name='InitSDK',): |
| stage_failure = StageFailureHelper.GetStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, stage_name) |
| |
| return failure_message_lib.BuildScriptFailureMessage(stage_failure) |
| |
| @classmethod |
| def GetPackageBuildFailureMessage( |
| cls, |
| failure_id=3, |
| build_stage_id=0, |
| outer_failure_id=None, |
| exception_type='PackageBuildFailure', |
| exception_message='exception msg', |
| exception_category=constants.EXCEPTION_CATEGORY_BUILD, |
| extra_info=DEFAULT_PACKAGE_BUILD_FAILURE_EXTRA_INFO, |
| stage_name='BuildImage',): |
| stage_failure = StageFailureHelper.GetStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, stage_name) |
| |
| return failure_message_lib.PackageBuildFailureMessage(stage_failure) |
| |
| @classmethod |
| def GetCompoundFailureMessage( |
| cls, |
| failure_id=4, |
| build_stage_id=0, |
| outer_failure_id=None, |
| exception_type='BackgroundFailure', |
| exception_message='exception msg', |
| exception_category=constants.EXCEPTION_CATEGORY_UNKNOWN, |
| extra_info=None, |
| stage_name='Archive', |
| stage_prefix_name='Archive'): |
| stage_failure = StageFailureHelper.GetStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, stage_name) |
| |
| return failure_message_lib.CompoundFailureMessage( |
| stage_failure, extra_info=extra_info, |
| stage_prefix_name=stage_prefix_name) |
| |
| @classmethod |
| def GetTestFailureMessage( |
| cls, |
| failure_id=5, |
| build_stage_id=0, |
| outer_failure_id=None, |
| exception_type='TestFailure', |
| exception_message='** HWTest failed (code 1) **', |
| exception_category=constants.EXCEPTION_CATEGORY_TEST, |
| extra_info=None, |
| stage_name='HWTest'): |
| stage_failure = StageFailureHelper.GetStageFailure( |
| failure_id, build_stage_id, outer_failure_id, exception_type, |
| exception_message, exception_category, extra_info, stage_name) |
| |
| return failure_message_lib.StageFailureMessage(stage_failure) |
| |
| @classmethod |
| def GetBuildFailureMessageWithMixedMsgs(cls): |
| f_1 = cls.GetStageFailureMessage(failure_id=1, outer_failure_id=4) |
| f_2 = cls.GetBuildScriptFailureMessage(failure_id=2, outer_failure_id=4) |
| f_3 = cls.GetPackageBuildFailureMessage(failure_id=3, outer_failure_id=4) |
| f_4 = cls.GetStageFailureMessage(failure_id=4) |
| f_5 = cls.GetStageFailureMessage(failure_id=5) |
| f_6 = cls.GetBuildScriptFailureMessage(failure_id=6) |
| f_7 = cls.GetPackageBuildFailureMessage(failure_id=7) |
| |
| return (failure_message_lib.FailureMessageManager.ReconstructMessages( |
| [f_1, f_2, f_3, f_4, f_5, f_6, f_7])) |
| |
| |
| class StageFailureMessageTests(cros_test_lib.TestCase): |
| """Tests for StageFailureMessage.""" |
| |
| def testDecodeExtraInfoWithNoneExtraInfo(self): |
| """Test _DecodeExtraInfo with None extra_info.""" |
| failure_message = FailureMessageHelper.GetStageFailureMessage() |
| |
| self.assertEqual(failure_message.extra_info, {}) |
| |
| def testDecodeExtraInfoWithNoneValidExtraInfo(self): |
| """Test _DecodeExtraInfo with valid extra_info.""" |
| failure_message = FailureMessageHelper.GetStageFailureMessage( |
| extra_info=DEFAULT_BUILD_SCRIPT_FAILURE_EXTRA_INFO) |
| |
| self.assertEqual(failure_message.extra_info['shortname'], |
| 'security_test_image') |
| |
| def testDecodeExtraInfoWithNoneInvalidExtraInfo(self): |
| """Test _DecodeExtraInfo with invalid extra_info.""" |
| failure_message = FailureMessageHelper.GetStageFailureMessage( |
| extra_info='{\"shortname\": \"security_test_image\"') |
| |
| self.assertEqual(failure_message.extra_info, {}) |
| |
| def testExtractStagePrefixName(self): |
| """Test _ExtractStagePrefixName.""" |
| prefix_name_map = { |
| 'HWTest [bvt-arc]': 'HWTest', |
| 'HWTest': 'HWTest', |
| 'ImageTest': 'ImageTest', |
| 'ImageTest [amd64-generic]': 'ImageTest', |
| 'VMTest (attempt 1)': 'VMTest', |
| 'VMTest [amd64-generic] (attempt 1)': 'VMTest' |
| } |
| |
| for k, v in prefix_name_map.items(): |
| failure_message = FailureMessageHelper.GetStageFailureMessage( |
| stage_name=k) |
| self.assertEqual(failure_message.stage_prefix_name, v) |
| |
| |
| class BuildScriptFailureMessageTests(cros_test_lib.TestCase): |
| """Tests for BuildScriptFailureMessage.""" |
| |
| def testGetShortname(self): |
| """Test GetShortname.""" |
| failure_message = FailureMessageHelper.GetBuildScriptFailureMessage() |
| |
| self.assertEqual(failure_message.GetShortname(), 'security_test_image') |
| |
| |
| class PackageBuildFailureMessageTests(cros_test_lib.TestCase): |
| """Tests for PackageBuildFailureMessage.""" |
| |
| def testGetShortname(self): |
| """Test GetShortname.""" |
| failure_message = FailureMessageHelper.GetPackageBuildFailureMessage() |
| |
| self.assertEqual(failure_message.GetShortname(), |
| './build_image') |
| |
| def testGetFailedPackages(self): |
| """Test GetFailedPackages.""" |
| failure_message = FailureMessageHelper.GetPackageBuildFailureMessage() |
| |
| self.assertCountEqual( |
| ['chromeos-base/chromeos-chrome', 'chromeos-base/chromeos-init'], |
| failure_message.GetFailedPackages()) |
| |
| |
| class CompoundFailureMessageTests(cros_test_lib.TestCase): |
| """Tests for CompoundFailureMessage.""" |
| |
| def testGetCompoundFailureMessage(self): |
| """Test GetCompoundFailureMessage.""" |
| failure_message = FailureMessageHelper.GetPackageBuildFailureMessage() |
| new_failure_message = ( |
| failure_message_lib.CompoundFailureMessage.GetFailureMessage( |
| failure_message)) |
| self.assertEqual(new_failure_message.inner_failures, []) |
| |
| def testHasEmptyList(self): |
| """Test HasEmptyList.""" |
| failure_message = FailureMessageHelper.GetCompoundFailureMessage() |
| self.assertTrue(failure_message.HasEmptyList()) |
| |
| failure_message.inner_failures.append( |
| FailureMessageHelper.GetPackageBuildFailureMessage()) |
| self.assertFalse(failure_message.HasEmptyList()) |
| |
| def testHasExceptionCategories(self): |
| """Test HasExceptionCategories.""" |
| failure_message = FailureMessageHelper.GetCompoundFailureMessage() |
| self.assertFalse(failure_message.HasExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD})) |
| |
| failure_message.inner_failures.append( |
| FailureMessageHelper.GetPackageBuildFailureMessage()) |
| self.assertTrue(failure_message.HasExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD})) |
| self.assertFalse(failure_message.HasExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_LAB})) |
| self.assertTrue(failure_message.HasExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD, constants.EXCEPTION_CATEGORY_LAB})) |
| |
| def testMatchesExceptionCategories(self): |
| """Test MatchesExceptionCategories.""" |
| failure_message = FailureMessageHelper.GetCompoundFailureMessage() |
| self.assertFalse(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD})) |
| |
| failure_message.inner_failures.append( |
| FailureMessageHelper.GetPackageBuildFailureMessage()) |
| self.assertTrue(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD})) |
| self.assertFalse(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_LAB})) |
| self.assertTrue(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD, constants.EXCEPTION_CATEGORY_LAB})) |
| |
| failure_message.inner_failures.append( |
| FailureMessageHelper.GetStageFailureMessage()) |
| self.assertFalse(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_BUILD})) |
| self.assertFalse(failure_message.MatchesExceptionCategories( |
| {constants.EXCEPTION_CATEGORY_LAB})) |
| |
| |
| class FailureMessageManagerTests(cros_test_lib.TestCase): |
| """Tests for FailureMessageManager.""" |
| |
| def testCreateMessageForBuildScriptFailureMessage(self): |
| """Test CreateMessage for BuildScriptFailureMessage.""" |
| stage_failure = StageFailureHelper.GetStageFailure( |
| 1, 1, 0, 'BuildScriptFailure', 'exception msg', 'build', |
| '{\"shortname\": \"./security_test_image\"', 'InitSDK') |
| failure_message = failure_message_lib.FailureMessageManager.CreateMessage( |
| stage_failure) |
| |
| self.assertTrue(isinstance(failure_message, |
| failure_message_lib.BuildScriptFailureMessage)) |
| |
| def testCreateMessageForPackageBuildFailureMessage(self): |
| """Test CreateMessage for PackageBuildFailureMessage.""" |
| stage_failure = StageFailureHelper.GetStageFailure( |
| 1, 1, 0, 'PackageBuildFailure', 'exception msg', 'build', |
| DEFAULT_PACKAGE_BUILD_FAILURE_EXTRA_INFO, 'BuildImage') |
| failure_message = failure_message_lib.FailureMessageManager.CreateMessage( |
| stage_failure) |
| |
| self.assertTrue(isinstance(failure_message, |
| failure_message_lib.PackageBuildFailureMessage)) |
| |
| def testCreateMessageForStageFailureMessage(self): |
| """Test CreateMessage for StageFailureMessage.""" |
| stage_failure = StageFailureHelper.GetStageFailure( |
| 1, 1, None, 'TestFailure', 'exception msg', 'build', None, 'VMTest') |
| failure_message = failure_message_lib.FailureMessageManager.CreateMessage( |
| stage_failure) |
| |
| self.assertTrue(isinstance(failure_message, |
| failure_message_lib.StageFailureMessage)) |
| |
| def testReconstructMessagesOnMixedMsgs(self): |
| """Test ReconstructMessages on mixed messages.""" |
| failures = FailureMessageHelper.GetBuildFailureMessageWithMixedMsgs() |
| |
| self.assertEqual(len(failures), 4) |
| failure_ids = [f.failure_id for f in failures] |
| self.assertCountEqual(failure_ids, [4, 5, 6, 7]) |
| |
| for f in failures: |
| if f.failure_id == 4: |
| self.assertTrue(isinstance( |
| f, failure_message_lib.CompoundFailureMessage)) |
| inner_failures = f.inner_failures |
| inner_failure_ids = [n_f.failure_id for n_f in inner_failures] |
| self.assertCountEqual([1, 2, 3], inner_failure_ids) |
| |
| def testReconstructMessagesOnEmptyMsgs(self): |
| """Test ReconstructMessages on empty messages.""" |
| failures = failure_message_lib.FailureMessageManager.ReconstructMessages([]) |
| |
| self.assertEqual(failures, []) |
| |
| def testConstructStageFailureMessagesOnNonCompoundFailures(self): |
| """Test ConstructStageFailureMessages on non-compound failures.""" |
| entry_1 = StageFailureHelper.GetStageFailure(failure_id=1) |
| entry_2 = StageFailureHelper.GetStageFailure(failure_id=2) |
| entry_3 = StageFailureHelper.GetStageFailure(failure_id=3) |
| failure_entries = [entry_1, entry_2, entry_3] |
| |
| failures = ( |
| failure_message_lib.FailureMessageManager.ConstructStageFailureMessages( |
| failure_entries)) |
| |
| self.assertEqual(len(failures), 3) |
| failure_ids = [f.failure_id for f in failures] |
| self.assertCountEqual(failure_ids, [1, 2, 3]) |
| |
| def testConstructStageFailureMessagesOnCompoundFailures(self): |
| """Test ConstructStageFailureMessages on compound failures.""" |
| entry_1 = StageFailureHelper.GetStageFailure(failure_id=1) |
| entry_2 = StageFailureHelper.GetStageFailure( |
| failure_id=2, outer_failure_id=1) |
| entry_3 = StageFailureHelper.GetStageFailure( |
| failure_id=3, outer_failure_id=1) |
| failure_entries = [entry_1, entry_2, entry_3] |
| |
| failures = ( |
| failure_message_lib.FailureMessageManager.ConstructStageFailureMessages( |
| failure_entries)) |
| |
| self.assertEqual(len(failures), 1) |
| f = failures[0] |
| self.assertEqual(f.failure_id, 1) |
| self.assertTrue(isinstance(f, failure_message_lib.CompoundFailureMessage)) |
| inner_failure_ids = [n_f.failure_id for n_f in f.inner_failures] |
| self.assertCountEqual([2, 3], inner_failure_ids) |