blob: 42f1e4de23cb4c15a7cd7c73edac6f8ae13197f2 [file] [log] [blame]
# Copyright 2019 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.
from datetime import datetime
import json
from parameterized import parameterized
from go.chromium.org.luci.buildbucket.proto.build_pb2 import Build
from go.chromium.org.luci.buildbucket.proto.builder_pb2 import BuilderID
from go.chromium.org.luci.buildbucket.proto.step_pb2 import Step
from google.appengine.ext import ndb
from findit_v2.model.compile_failure import CompileFailure
from findit_v2.model.compile_failure import CompileFailureGroup
from findit_v2.model.luci_build import LuciFailedBuild
from findit_v2.model.test_failure import TestFailure
from findit_v2.services.chromeos_api import ChromeOSProjectAPI
from findit_v2.services.context import Context
from findit_v2.services.failure_type import StepTypeEnum
from waterfall.test import wf_testcase
class ChromeOSProjectAPITest(wf_testcase.TestCase):
def setUp(self):
super(ChromeOSProjectAPITest, self).setUp()
self.first_failed_commit_id = 'git_sha'
self.first_failed_commit_position = 65450
self.context = Context(
luci_project_name='chromeos',
gitiles_host='gitiles.host.com',
gitiles_project='project/name',
gitiles_ref='ref/heads/master',
gitiles_id='git_sha')
self.builder = BuilderID(
project='chromeos', bucket='postsubmit', builder='builder-postsubmit')
self.group_build_id = 8000000000189
self.group_build = LuciFailedBuild.Create(
luci_project=self.context.luci_project_name,
luci_bucket=self.builder.bucket,
luci_builder='builder2-postsubmit',
build_id=self.group_build_id,
legacy_build_number=12345,
gitiles_host=self.context.gitiles_host,
gitiles_project=self.context.gitiles_project,
gitiles_ref=self.context.gitiles_ref,
gitiles_id=self.context.gitiles_id,
commit_position=self.first_failed_commit_position,
status=20,
create_time=datetime(2019, 3, 28),
start_time=datetime(2019, 3, 28, 0, 1),
end_time=datetime(2019, 3, 28, 1),
build_failure_type=StepTypeEnum.COMPILE)
self.group_build.put()
self.output_target1 = json.dumps({
'category': 'chromeos-base',
'packageName': 'target1'
})
self.output_target2 = json.dumps({
'category': 'chromeos-base',
'packageName': 'target2'
})
self.output_targets = [self.output_target1, self.output_target2]
def _CreateBuildbucketBuild(self, build_id, build_number):
build = Build(id=build_id, number=build_number)
build.input.gitiles_commit.host = 'gitiles.host.com'
build.input.gitiles_commit.project = 'project/name'
build.input.gitiles_commit.ref = 'ref/heads/master'
build.input.gitiles_commit.id = 'git_sha'
return build
def _CreateBuildbucketBuildForCompile(
self,
build_id,
build_number,
output_targets=None,
step_name=None,
):
build = self._CreateBuildbucketBuild(build_id, build_number)
if output_targets:
build.output.properties['compile_failures'] = {
'failures': [{
'output_targets': output_targets,
'rule': 'emerge',
},],
'failed_step': step_name
}
return build
def _CreateBuildbucketBuildForTest(self,
build_id,
build_number,
step_name=None):
build = self._CreateBuildbucketBuild(build_id, build_number)
if step_name == 'no spec':
build.output.properties['test_failures'] = {
'xx_test_failures': [{
'failed_step': step_name,
},],
}
else:
build.output.properties['test_failures'] = {
'xx_test_failures': [{
'failed_step': step_name,
'test_spec': 'test_spec',
'suite': 'suite'
},],
'needs_bisection':
True
}
return build
def testCompileStep(self):
compile_step_name = 'install packages|installation results'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(
build_id,
build_number,
self.output_targets,
step_name=compile_step_name)
step = Step()
step.name = compile_step_name
log = step.logs.add()
log.name = 'stdout'
self.assertEqual(StepTypeEnum.COMPILE,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testInfraStepFromABuildWithCompileFailure(self):
compile_step_name = 'install packages|installation results'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(
build_id,
build_number,
self.output_targets,
step_name=compile_step_name)
step = Step()
step.name = 'Failure Reason'
log = step.logs.add()
log.name = 'reason'
self.assertEqual(StepTypeEnum.INFRA,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testInfraStepFromABuildWithCompileFailureNoFailedStep(self):
compile_step_name = 'install packages|installation results'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(build_id, build_number,
self.output_targets)
step = Step()
step.name = compile_step_name
log = step.logs.add()
log.name = 'reason'
self.assertEqual(StepTypeEnum.INFRA,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testTestStep(self):
step_name = 'results|xx test results|[FAILED] <suite1>'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForTest(
build_id, build_number, step_name=step_name)
step = Step()
step.name = step_name
self.assertEqual(StepTypeEnum.TEST,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testInfraStepFromABuildWithTestStep(self):
step_name = 'results|xx test results|[FAILED] <suite1>'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForTest(
build_id, build_number, step_name=step_name)
step = Step()
step.name = 'another_step'
self.assertEqual(StepTypeEnum.INFRA,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testInfraStepFromABuildWithoutCompileFailure(self):
step_name = 'test step'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(build_id, build_number)
step = Step()
step.name = step_name
log = step.logs.add()
log.name = 'reason'
self.assertEqual(StepTypeEnum.INFRA,
ChromeOSProjectAPI().ClassifyStepType(build, step))
def testGetCompileFailures(self):
step_name = 'install packages'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(
build_id, build_number, self.output_targets, step_name=step_name)
step = Step()
step.name = step_name
expected_failures = {
'install packages': {
'failures': {
frozenset(self.output_targets): {
'properties': {
'rule': 'emerge',
'needs_bisection': True,
},
'first_failed_build': {
'id': build_id,
'number': build_number,
'commit_id': 'git_sha',
},
'last_passed_build': None,
},
},
'first_failed_build': {
'id': build_id,
'number': build_number,
'commit_id': 'git_sha',
},
'last_passed_build': None,
},
}
self.assertEqual(expected_failures,
ChromeOSProjectAPI().GetCompileFailures(build, [step]))
def testGetCompileFailuresNoFailedStep(self):
step_name = 'install packages'
build_id = 8765432109123
build_number = 123
output_target = json.dumps({
'category': 'chromeos-base',
'packageName': 'target2',
})
build = self._CreateBuildbucketBuildForCompile(build_id, build_number,
[output_target])
step = Step()
step.name = step_name
self.assertEqual({}, ChromeOSProjectAPI().GetCompileFailures(build, [step]))
def testGetCompileFailuresNoFailure(self):
step_name = 'install packages'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForCompile(
build_id, build_number, [], step_name=step_name)
step = Step()
step.name = step_name
self.assertEqual({}, ChromeOSProjectAPI().GetCompileFailures(build, [step]))
def testGetRerunBuilderId(self):
build = Build(builder=self.builder)
build.output.properties['BISECT_BUILDER'] = 'builder-bisect'
self.assertEqual('chromeos/bisect/builder-bisect',
ChromeOSProjectAPI().GetRerunBuilderId(build))
def testGetCompileRerunBuildInputProperties(self):
targets = {'install packages': [self.output_target1]}
expected_prop = {
'$chromeos/cros_bisect': {
'compile': {
'targets': [self.output_target1],
},
},
}
self.assertEqual(
expected_prop,
ChromeOSProjectAPI().GetCompileRerunBuildInputProperties(
targets, 8000000000122))
def testGetCompileRerunBuildInputPropertiesOtherStep(self):
self.assertIsNone(ChromeOSProjectAPI().GetCompileRerunBuildInputProperties(
{}, 8000000000122))
def testGetFailuresWithMatchingCompileFailureGroupsNoExistingGroup(self):
build_id = 8000000000122
build = Build(builder=self.builder, number=122, id=build_id)
build.input.gitiles_commit.host = 'gitiles.host.com'
build.input.gitiles_commit.project = 'project/name'
build.input.gitiles_commit.ref = 'ref/heads/master'
build.input.gitiles_commit.id = 'git_sha'
last_passed_build_info = {
'id': 8000000000121,
'number': 121,
'commit_id': 'git_sha_121',
}
first_failures_in_current_build = {
'failures': {
'install packages': {
'atomic_failures': [frozenset([self.output_target1])],
'last_passed_build': last_passed_build_info,
},
},
'last_passed_build': last_passed_build_info,
}
self.assertEqual(
{},
ChromeOSProjectAPI().GetFailuresWithMatchingCompileFailureGroups(
self.context, build, first_failures_in_current_build))
def testGetFailuresWithMatchingCompileFailureGroupsFailureNotExactlySame(
self):
build_id = 8000000000122
build = Build(builder=self.builder, number=122, id=build_id)
build.input.gitiles_commit.host = 'gitiles.host.com'
build.input.gitiles_commit.project = 'project/name'
build.input.gitiles_commit.ref = 'ref/heads/master'
build.input.gitiles_commit.id = 'git_sha'
last_passed_build_info = {
'id': 8000000000121,
'number': 121,
'commit_id': 'git_sha_121',
}
first_failures_in_current_build = {
'failures': {
'install packages': {
'atomic_failures': [
frozenset([self.output_target1]),
frozenset([self.output_target2]),
],
'last_passed_build':
last_passed_build_info,
},
},
'last_passed_build': last_passed_build_info,
}
compile_failure = CompileFailure.Create(
self.group_build.key,
'install packages', [self.output_target1],
'CXX',
first_failed_build_id=self.group_build_id,
last_passed_build_id=8000000000160)
compile_failure.put()
CompileFailureGroup.Create(
luci_project=self.context.luci_project_name,
luci_bucket=build.builder.bucket,
build_id=self.group_build_id,
gitiles_host=self.context.gitiles_host,
gitiles_project=self.context.gitiles_project,
gitiles_ref=self.context.gitiles_ref,
last_passed_gitiles_id=last_passed_build_info['commit_id'],
last_passed_commit_position=654321,
first_failed_gitiles_id=self.first_failed_commit_id,
first_failed_commit_position=654340,
compile_failure_keys=[compile_failure.key]).put()
self.assertEqual(
{},
ChromeOSProjectAPI().GetFailuresWithMatchingCompileFailureGroups(
self.context, build, first_failures_in_current_build))
def testGetFailuresWithMatchingCompileFailureGroupsWithExistingGroup(self):
build_id = 8000000000122
build = Build(builder=self.builder, number=122, id=build_id)
build.input.gitiles_commit.host = 'gitiles.host.com'
build.input.gitiles_commit.project = 'project/name'
build.input.gitiles_commit.ref = 'ref/heads/master'
build.input.gitiles_commit.id = 'git_sha'
last_passed_build_info = {
'id': 8000000000121,
'number': 121,
'commit_id': 'git_sha_121',
}
first_failures_in_current_build = {
'failures': {
'install packages': {
'atomic_failures': [frozenset(['target1']),],
'last_passed_build': last_passed_build_info,
},
},
'last_passed_build': last_passed_build_info,
}
compile_failure = CompileFailure.Create(
self.group_build.key,
'install packages', ['target1'],
'CXX',
first_failed_build_id=self.group_build_id,
last_passed_build_id=8000000000160)
compile_failure.put()
CompileFailureGroup.Create(
luci_project=self.context.luci_project_name,
luci_bucket=build.builder.bucket,
build_id=self.group_build_id,
gitiles_host=self.context.gitiles_host,
gitiles_project=self.context.gitiles_project,
gitiles_ref=self.context.gitiles_ref,
last_passed_gitiles_id=last_passed_build_info['commit_id'],
last_passed_commit_position=654321,
first_failed_gitiles_id=self.first_failed_commit_id,
first_failed_commit_position=654340,
compile_failure_keys=[compile_failure.key]).put()
expected_failures_with_existing_group = {
'install packages': {
frozenset(['target1']): self.group_build_id,
}
}
self.assertEqual(
expected_failures_with_existing_group,
ChromeOSProjectAPI().GetFailuresWithMatchingCompileFailureGroups(
self.context, build, first_failures_in_current_build))
def testGetTestFailures(self):
step_name = 'results|xx test results|[FAILED] <suite1>'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForTest(
build_id, build_number, step_name=step_name)
step = Step()
step.name = step_name
expected_failures = {
step_name: {
'failures': {},
'first_failed_build': {
'id': build_id,
'number': build_number,
'commit_id': 'git_sha',
},
'last_passed_build': None,
'properties': {
'failure_type': 'xx_test_failures',
'test_spec': 'test_spec',
'suite': 'suite',
'needs_bisection': True,
}
},
}
self.assertEqual(expected_failures,
ChromeOSProjectAPI().GetTestFailures(build, [step]))
def testGetTestFailuresMalFormedOutput(self):
step_name = 'no spec'
build_id = 8765432109123
build_number = 123
build = self._CreateBuildbucketBuildForTest(
build_id, build_number, step_name=step_name)
step = Step()
step.name = step_name
expected_failures = {}
self.assertEqual(expected_failures,
ChromeOSProjectAPI().GetTestFailures(build, [step]))
def testGetFailuresWithMatchingTestFailureGroupsNoExistingGroup(self):
build_id = 8000000000122
build = Build(builder=self.builder, number=122, id=build_id)
build.input.gitiles_commit.host = 'gitiles.host.com'
build.input.gitiles_commit.project = 'project/name'
build.input.gitiles_commit.ref = 'ref/heads/master'
build.input.gitiles_commit.id = 'git_sha'
last_passed_build_info = {
'id': 8000000000121,
'number': 121,
'commit_id': 'git_sha_121'
}
first_failures_in_current_build = {
'failures': {
'step': {
'atomic_failures': [],
'last_passed_build': last_passed_build_info,
},
},
'last_passed_build': last_passed_build_info,
}
self.assertEqual(
{},
ChromeOSProjectAPI().GetFailuresWithMatchingTestFailureGroups(
self.context, build, first_failures_in_current_build))
def testGetTestRerunBuildInputProperties(self):
tests = {
'step1': {
'tests': [],
'properties': {
'failure_type': 'xx_test_failures',
'test_spec': 'test_spec1',
}
},
'step2': {
'tests': [],
'properties': {
'failure_type': 'xx_test_failures',
'test_spec': 'test_spec2',
}
}
}
expected_input = {
'$chromeos/cros_bisect': {
'test': {
'xx_test_failures': [{
'test_spec': 'test_spec2'
}, {
'test_spec': 'test_spec1'
}],
},
},
}
self.assertEqual(
expected_input,
ChromeOSProjectAPI().GetTestRerunBuildInputProperties(
tests, 8000000000122))
def testGetFailureKeysToAnalyzeTestFailures(self):
failure_entities = []
for i in xrange(2):
test_failure = TestFailure.Create(
failed_build_key=ndb.Key(LuciFailedBuild, 8000000000123),
step_ui_name='step%d.suite' % i,
test=None,
properties={'suite': 'suite'})
failure_entities.append(test_failure)
ndb.put_multi(failure_entities)
analyzed_failure_keys = ChromeOSProjectAPI(
).GetFailureKeysToAnalyzeTestFailures(failure_entities)
self.assertEqual(1, len(analyzed_failure_keys))
deduped_failure_keys = set([f.key for f in failure_entities
]) - set(analyzed_failure_keys)
self.assertEqual(analyzed_failure_keys[0],
deduped_failure_keys.pop().get().merged_failure_key)
def testGetFailureKeysToAnalyzeTestFailuresDifferentSuites(self):
failure_entities = []
for i in xrange(2):
test_failure = TestFailure.Create(
failed_build_key=ndb.Key(LuciFailedBuild, 8000000000123),
step_ui_name='step.suite%d' % i,
test=None,
properties={'suite': 'suite%d' % i})
failure_entities.append(test_failure)
ndb.put_multi(failure_entities)
analyzed_failure_keys = ChromeOSProjectAPI(
).GetFailureKeysToAnalyzeTestFailures(failure_entities)
self.assertEqual(2, len(analyzed_failure_keys))
def testGetFailureKeysToAnalyzeTestFailureNeedNoBisection(self):
failure_entities = []
for i in xrange(2):
test_failure = TestFailure.Create(
failed_build_key=ndb.Key(LuciFailedBuild, 8000000000123),
step_ui_name='step.suite%d' % i,
test=None,
properties={
'suite': 'suite%d' % i,
'needs_bisection': False,
})
failure_entities.append(test_failure)
ndb.put_multi(failure_entities)
analyzed_failure_keys = ChromeOSProjectAPI(
).GetFailureKeysToAnalyzeTestFailures(failure_entities)
self.assertEqual([], analyzed_failure_keys)
@parameterized.expand([
(TestFailure.Create(
failed_build_key=ndb.Key(LuciFailedBuild, 8000000000123),
step_ui_name='step.suite1',
test=None,
properties={
'needs_bisection': False,
}), False),
(TestFailure.Create(
failed_build_key=ndb.Key(LuciFailedBuild, 8000000000123),
step_ui_name='step.suite2',
test=None,
properties={
'needs_bisection': True,
}), True),
])
def testFailureShouldBeAnalyzed(self, failure_entity, result):
cros_api = ChromeOSProjectAPI()
self.assertEqual(result, cros_api.FailureShouldBeAnalyzed(failure_entity))
def testClearSkipFlag(self):
build_id = 8000000000598
failed_build_key = ndb.Key(LuciFailedBuild, build_id)
failure = TestFailure.Create(
failed_build_key=failed_build_key,
step_ui_name='step.suite8',
test=None,
properties={
'needs_bisection': False,
})
failure.put()
ChromeOSProjectAPI().ClearSkipFlag([failure])
failures = TestFailure.query(ancestor=failed_build_key).fetch()
self.assertTrue(failures[0].properties['needs_bisection'])