blob: 714955dab3dd544dce0dd3ce1d71c41a305780dc [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import datetime
import mock
from dto.flakiness import Flakiness
from dto.int_range import IntRange
from dto.step_metadata import StepMetadata
from dto.test_location import TestLocation
from gae_libs.pipeline_wrapper import pipeline_handlers
from infra_api_clients import crrev
from libs import analysis_status
from libs.list_of_basestring import ListOfBasestring
from model import result_status
from model.flake.analysis.flake_culprit import FlakeCulprit
from model.flake.analysis.data_point import DataPoint
from model.flake.analysis.master_flake_analysis import MasterFlakeAnalysis
from pipelines.delay_pipeline import DelayPipeline
from pipelines.flake_failure.analyze_flake_pipeline import AnalyzeFlakeInput
from pipelines.flake_failure.analyze_flake_pipeline import (
AnalyzeFlakePipeline)
from pipelines.flake_failure.analyze_flake_pipeline import (
RecursiveAnalyzeFlakePipeline)
from pipelines.flake_failure.create_and_submit_revert_pipeline import (
CreateAndSubmitRevertInput)
from pipelines.flake_failure.create_and_submit_revert_pipeline import (
CreateAndSubmitRevertPipeline)
from pipelines.flake_failure.create_bug_for_flake_pipeline import (
CreateBugForFlakeInput)
from pipelines.flake_failure.create_bug_for_flake_pipeline import (
CreateBugForFlakePipeline)
from pipelines.flake_failure.determine_approximate_pass_rate_pipeline import (
DetermineApproximatePassRateInput)
from pipelines.flake_failure.determine_approximate_pass_rate_pipeline import (
DetermineApproximatePassRatePipeline)
from pipelines.flake_failure.get_isolate_sha_pipeline import (
GetIsolateShaForCommitPositionParameters)
from pipelines.flake_failure.get_isolate_sha_pipeline import (
GetIsolateShaForCommitPositionPipeline)
from pipelines.flake_failure.get_isolate_sha_pipeline import (
GetIsolateShaOutput)
from pipelines.flake_failure.next_commit_position_pipeline import (
NextCommitPositionInput)
from pipelines.flake_failure.next_commit_position_pipeline import (
NextCommitPositionOutput)
from pipelines.flake_failure.next_commit_position_pipeline import (
NextCommitPositionPipeline)
from pipelines.flake_failure.notify_culprit_pipeline import NotifyCulpritInput
from pipelines.flake_failure.notify_culprit_pipeline import (
NotifyCulpritPipeline)
from pipelines.flake_failure.update_flake_analysis_data_points_pipeline import (
UpdateFlakeAnalysisDataPointsInput)
from pipelines.flake_failure.update_flake_analysis_data_points_pipeline import (
UpdateFlakeAnalysisDataPointsPipeline)
from pipelines.report_event_pipeline import ReportAnalysisEventPipeline
from pipelines.report_event_pipeline import ReportEventInput
from services import swarmed_test_util
from services.actions import flake_analysis_actions
from services.flake_failure import confidence_score_util
from services.flake_failure import flake_analysis_util
from waterfall.test.wf_testcase import WaterfallTestCase
class AnalyzeFlakePipelineTest(WaterfallTestCase):
app_module = pipeline_handlers._APP
@mock.patch.object(flake_analysis_actions, 'OnCulpritIdentified')
def testAnalyzeFlakePipelineAnalysisFinishedNoFindings(
self, mocked_culprit_identified):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
analysis.Save()
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=None, culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable([]),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=None)
expected_report_event_input = ReportEventInput(
analysis_urlsafe_key=analysis.key.urlsafe())
self.MockGeneratorPipeline(ReportAnalysisEventPipeline,
expected_report_event_input, None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()
self.assertIsNone(analysis.culprit_urlsafe_key)
self.assertEqual(analysis_status.COMPLETED, analysis.status)
self.assertEqual(result_status.NOT_FOUND_UNTRIAGED, analysis.result_status)
mocked_culprit_identified.assert_not_called()
@mock.patch.object(
flake_analysis_util, 'ShouldTakeAutoAction', return_value=True)
@mock.patch.object(crrev, 'RedirectByCommitPosition')
@mock.patch.object(flake_analysis_util, 'UpdateCulprit')
@mock.patch.object(confidence_score_util, 'CalculateCulpritConfidenceScore')
@mock.patch.object(swarmed_test_util, 'GetTestLocation')
@mock.patch.object(flake_analysis_actions, 'OnCulpritIdentified')
def testAnalyzeFlakePipelineAnalysisFinishedWithCulprit(
self, mocked_culprit_identified, mocked_test_location, mocked_confidence,
mocked_culprit, mocked_revision, _):
master_name = 'm'
builder_name = 'b'
build_number = 123
build_key = 'm/b/123'
step_name = 's'
test_name = 't'
culprit_commit_position = 999
analysis = MasterFlakeAnalysis.Create(master_name, builder_name,
build_number, step_name, test_name)
analysis.data_points = [
DataPoint.Create(commit_position=culprit_commit_position)
]
analysis.original_master_name = master_name
analysis.original_builder_name = builder_name
analysis.original_build_number = build_number
analysis.original_step_name = step_name
analysis.original_test_name = test_name
analysis.Save()
culprit_revision = 'r999'
confidence_score = 0.85
culprit = FlakeCulprit.Create('chromium', culprit_revision,
culprit_commit_position)
culprit.put()
test_location = TestLocation(file='f', line=123)
mocked_test_location.return_value = test_location
mocked_revision.return_value = {'git_sha': culprit_revision}
mocked_confidence.return_value = confidence_score
mocked_culprit.return_value = culprit
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=None,
culprit_commit_position=culprit_commit_position),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable([]),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=None)
expected_create_bug_input = CreateBugForFlakeInput(
analysis_urlsafe_key=unicode(analysis.key.urlsafe()),
step_metadata=None,
test_location=test_location)
expected_create_and_submit_revert_input = CreateAndSubmitRevertInput(
analysis_urlsafe_key=analysis.key.urlsafe(), build_key=build_key)
expected_notify_culprit_input = NotifyCulpritInput(
analysis_urlsafe_key=analysis.key.urlsafe())
expected_report_event_input = ReportEventInput(
analysis_urlsafe_key=analysis.key.urlsafe())
self.MockGeneratorPipeline(CreateBugForFlakePipeline,
expected_create_bug_input, None)
self.MockGeneratorPipeline(CreateAndSubmitRevertPipeline,
expected_create_and_submit_revert_input, True)
self.MockGeneratorPipeline(NotifyCulpritPipeline,
expected_notify_culprit_input, True)
self.MockGeneratorPipeline(ReportAnalysisEventPipeline,
expected_report_event_input, None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()
self.assertIsNotNone(analysis.culprit_urlsafe_key)
self.assertTrue(mocked_culprit.called)
self.assertEqual(confidence_score, analysis.confidence_in_culprit)
self.assertEqual(analysis_status.COMPLETED, analysis.status)
self.assertEqual(result_status.FOUND_UNTRIAGED, analysis.result_status)
mocked_revision.assert_called_once_with(mock.ANY, 999)
mocked_culprit_identified.assert_called_once_with(analysis.key.urlsafe())
@mock.patch.object(
flake_analysis_util, 'ShouldTakeAutoAction', return_value=False)
@mock.patch.object(crrev, 'RedirectByCommitPosition')
@mock.patch.object(flake_analysis_util, 'UpdateCulprit')
@mock.patch.object(confidence_score_util, 'CalculateCulpritConfidenceScore')
@mock.patch.object(swarmed_test_util, 'GetTestLocation')
@mock.patch.object(flake_analysis_actions, 'OnCulpritIdentified')
def testAnalyzeFlakePipelineAnalysisFinishedWithCulpritNoAutoAction(
self, mocked_culprit_identified, mocked_test_location, mocked_confidence,
mocked_culprit, mocked_revision, _):
master_name = 'm'
builder_name = 'b'
build_number = 123
step_name = 's'
test_name = 't'
culprit_commit_position = 999
analysis = MasterFlakeAnalysis.Create(master_name, builder_name,
build_number, step_name, test_name)
analysis.data_points = [
DataPoint.Create(commit_position=culprit_commit_position)
]
analysis.original_master_name = master_name
analysis.original_builder_name = builder_name
analysis.original_build_number = build_number
analysis.original_step_name = step_name
analysis.original_test_name = test_name
analysis.Save()
culprit_revision = 'r999'
confidence_score = 0.85
culprit = FlakeCulprit.Create('chromium', culprit_revision,
culprit_commit_position)
culprit.put()
test_location = TestLocation(file='f', line=123)
mocked_test_location.return_value = test_location
mocked_revision.return_value = {'git_sha': culprit_revision}
mocked_confidence.return_value = confidence_score
mocked_culprit.return_value = culprit
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=None,
culprit_commit_position=culprit_commit_position),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable([]),
manually_triggered=False,
rerun=True,
retries=0,
step_metadata=None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()
self.assertIsNotNone(analysis.culprit_urlsafe_key)
self.assertTrue(mocked_culprit.called)
self.assertEqual(confidence_score, analysis.confidence_in_culprit)
self.assertEqual(analysis_status.COMPLETED, analysis.status)
self.assertEqual(result_status.FOUND_UNTRIAGED, analysis.result_status)
mocked_culprit_identified.assert_called_once_with(analysis.key.urlsafe())
@mock.patch.object(
flake_analysis_util, 'CanStartAnalysisImmediately', return_value=True)
@mock.patch.object(crrev, 'RedirectByCommitPosition')
def testAnalyzeFlakePipelineCanStartAnalysisImmediately(
self, mocked_revision, _):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
analysis.Save()
start_commit_position = 1000
start_revision = 'r1000'
isolate_sha = 'sha1'
next_commit_position = 999
pass_rate = 0.5
build_url = 'url'
try_job_url = None
get_sha_output = GetIsolateShaOutput(
isolate_sha=isolate_sha, build_url=build_url, try_job_url=try_job_url)
step_metadata = StepMetadata(
canonical_step_name='s',
dimensions=None,
full_step_name='s',
patched=False,
swarm_task_ids=None,
waterfall_buildername='b',
waterfall_mastername='w',
isolate_target_name='s')
mocked_revision.return_value = {'git_sha': start_revision}
expected_flakiness = Flakiness(
build_url=build_url,
commit_position=start_commit_position,
revision=start_revision,
pass_rate=pass_rate)
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=start_commit_position,
culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=step_metadata)
expected_isolate_sha_input = GetIsolateShaForCommitPositionParameters(
analysis_urlsafe_key=analysis.key.urlsafe(),
commit_position=start_commit_position,
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
revision=start_revision,
step_metadata=step_metadata,
upper_bound_build_number=analysis.build_number)
expected_pass_rate_input = DetermineApproximatePassRateInput(
builder_name=analysis.builder_name,
commit_position=start_commit_position,
flakiness_thus_far=None,
get_isolate_sha_output=get_sha_output,
master_name=analysis.master_name,
previous_swarming_task_output=None,
reference_build_number=analysis.build_number,
revision=start_revision,
step_name=analysis.step_name,
test_name=analysis.test_name)
expected_update_data_points_input = UpdateFlakeAnalysisDataPointsInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
flakiness=expected_flakiness)
expected_next_commit_position_input = NextCommitPositionInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
commit_position_range=IntRange(lower=None, upper=None),
step_metadata=step_metadata)
expected_next_commit_position_output = NextCommitPositionOutput(
next_commit_position=next_commit_position, culprit_commit_position=None)
expected_recursive_analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=expected_next_commit_position_output,
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=step_metadata)
self.MockGeneratorPipeline(GetIsolateShaForCommitPositionPipeline,
expected_isolate_sha_input, get_sha_output)
self.MockGeneratorPipeline(DetermineApproximatePassRatePipeline,
expected_pass_rate_input, expected_flakiness)
self.MockSynchronousPipeline(UpdateFlakeAnalysisDataPointsPipeline,
expected_update_data_points_input, None)
self.MockSynchronousPipeline(NextCommitPositionPipeline,
expected_next_commit_position_input,
expected_next_commit_position_output)
self.MockGeneratorPipeline(RecursiveAnalyzeFlakePipeline,
expected_recursive_analyze_flake_input, None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()
mocked_revision.assert_called_once_with(mock.ANY, start_commit_position)
@mock.patch.object(
flake_analysis_util, 'CanStartAnalysisImmediately', return_value=False)
@mock.patch.object(crrev, 'RedirectByCommitPosition')
@mock.patch.object(flake_analysis_util, 'CalculateDelaySecondsBetweenRetries')
def testAnalyzeFlakePipelineStartTaskAfterDelay(self, mocked_delay,
mocked_revision, _):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
# Random date in the past, for coverage.
analysis.request_time = datetime.datetime(2015, 1, 1, 1, 1, 1)
analysis.Save()
start_commit_position = 1000
start_revision = 'r1000'
delay = 60
step_metadata = StepMetadata(
canonical_step_name='s',
dimensions=None,
full_step_name='s',
patched=False,
swarm_task_ids=None,
waterfall_buildername='b',
waterfall_mastername='w',
isolate_target_name='s')
mocked_revision.return_value = {'git_sha': start_revision}
mocked_delay.return_value = delay
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=start_commit_position,
culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=step_metadata)
expected_retried_analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=start_commit_position,
culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=1,
step_metadata=step_metadata)
self.MockAsynchronousPipeline(DelayPipeline, delay, delay)
self.MockGeneratorPipeline(RecursiveAnalyzeFlakePipeline,
expected_retried_analyze_flake_input, None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()
mocked_revision.assert_called_once_with(mock.ANY, 1000)
def testOnFinalizedNoError(self):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
analysis.Save()
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=1000, culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.OnFinalized(analyze_flake_input)
self.assertEqual(analysis_status.COMPLETED, analysis.status)
def testFinishAnalyzeFlakePipelineWithError(self):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
analysis.error = analysis.GetError()
analysis.Save()
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=1000, culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable(['os:testOS']),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=None)
pipeline_job = AnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.OnFinalized(analyze_flake_input)
self.assertEqual(analysis_status.ERROR, analysis.status)
def testRecursiveAnalyzeFlakePipeline(self):
analysis = MasterFlakeAnalysis.Create('m', 'b', 123, 's', 't')
analysis.Save()
analyze_flake_input = AnalyzeFlakeInput(
analysis_urlsafe_key=analysis.key.urlsafe(),
analyze_commit_position_parameters=NextCommitPositionOutput(
next_commit_position=1000, culprit_commit_position=None),
commit_position_range=IntRange(lower=None, upper=None),
dimensions=ListOfBasestring.FromSerializable([]),
manually_triggered=False,
rerun=False,
retries=0,
step_metadata=None)
self.MockGeneratorPipeline(AnalyzeFlakePipeline, analyze_flake_input, None)
pipeline_job = RecursiveAnalyzeFlakePipeline(analyze_flake_input)
pipeline_job.start()
self.execute_queued_tasks()