blob: 3b5149cf3e39f18f32005bb7f43b93db11e6b3d1 [file] [log] [blame]
# Copyright 2022 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
from google.protobuf import timestamp_pb2
from PB.go.chromium.org.luci.buildbucket.proto import (
build as build_pb2,
builder_common as builder_common_pb2,
common as common_pb2,
)
from PB.go.chromium.org.luci.led.job import job as job_pb2
from PB.recipe_modules.flutter.recipe_testing import options as options_pb2
from recipe_engine import recipe_test_api
ONE_DAY = int(datetime.timedelta(days=1).total_seconds())
MAX_BUILD_AGE_SECONDS = int(datetime.timedelta(days=28).total_seconds())
class RecipeTestingTestApi(recipe_test_api.RecipeTestApi):
def project(
self,
name="flutter",
include_unrestricted=True,
include_restricted=False,
cq_config_name="commit-queue.cfg",
excluded_buckets=(),
):
return options_pb2.Project(
name=name,
include_unrestricted=include_unrestricted,
include_restricted=include_restricted,
cq_config_name=cq_config_name,
excluded_buckets=excluded_buckets,
)
def options(self, projects=(), use_buildbucket=False, **kwargs):
if not projects:
projects = [self.project()]
return self.m.properties(
recipe_testing_options=options_pb2.Options(
projects=list(projects), use_buildbucket=use_buildbucket, **kwargs
)
)
def build_data(
self,
name,
recipe,
age_seconds=ONE_DAY,
cl_cached=False,
skip=False,
num_log_entries=1,
project="flutter",
bucket="try",
# used for both buildbucket build id and swarming task id.
fake_id=100,
using_led=True,
):
# This time is taken from the time recipe_engine module. I see no way
# of getting it programmatically.
curr_time = 1337000000
end_time = curr_time - age_seconds
orig_build = build_pb2.Build(id=fake_id, status=common_pb2.SUCCESS)
orig_build.end_time.seconds = end_time
orig_build.builder.project = project
orig_build.builder.bucket = bucket
orig_build.builder.builder = name
orig_build.input.properties["recipe"] = recipe
cl = orig_build.input.gerrit_changes.add()
cl.host = "flutter-review.googlesource.com"
cl.project = project
result = self.m.buildbucket.simulated_search_results(
[orig_build], "get builders.{}.buildbucket.search".format(name)
)
if skip or age_seconds > MAX_BUILD_AGE_SECONDS:
return result
job = job_pb2.Definition()
build = self.m.buildbucket.ci_build_message(
priority=34500, project=project, bucket=bucket, builder=name
)
build.input.properties["recipe"] = recipe
# Don't inject test data for led steps when not using led, i.e. using
# the Buildbucket scheduling codepath.
if not using_led:
return result
# It's unrealistic for the get-builder response to have a task ID set,
# but the only way of mocking the task ID returned by `led launch` is
# to set the task ID on the input to `led launch`, which, for recipe
# testing, is the `led get-builder` response.
build.infra.swarming.task_id = str(fake_id)
job.buildbucket.bbagent_args.build.CopyFrom(build)
result += self.m.led.mock_get_builder(
job,
project=project,
bucket=bucket,
builder=name,
)
if recipe != "recipes" and not cl_cached:
result += self.m.gitiles.log(
"get builders.{}.log {}".format(name, cl.project),
"A",
n=num_log_entries,
)
return result
def no_build(self, name):
return self.m.buildbucket.simulated_search_results(
[], "get builders.{}.buildbucket.search".format(name)
)
def affected_recipes_data(
self,
affected_recipes,
recipe_files=None,
changed_files=None,
error=None,
invalid_recipes=(),
step_name="get_affected_recipes.recipes-analyze",
):
if not recipe_files:
recipe_files = ["foo", "flutter.py", "recipes.py", "sdk.expected"]
res = self.step_data(
"get_affected_recipes.ls-recipes",
stdout=self.m.raw_io.output_text(
"".join("{}\n".format(x) for x in recipe_files)
),
)
if not changed_files:
changed_files = [
"recipes/flutter.py",
"recipes/foo",
"recipes/non_expected_json_file.json",
"recipe_modules/foo/examples/full.expected/bar.json",
"recipe_modules/foo/examples/full.py",
"recipe_modules/foo/test_api.py",
]
res += self.m.git.get_changed_files(
"get_affected_recipes.git diff-tree",
changed_files,
)
output = {
"recipes": list(affected_recipes),
"error": error or "",
"invalidRecipes": list(invalid_recipes),
}
retcode = -1 if error else 0
res += self.step_data(step_name, self.m.json.output(output), retcode=retcode)
return res
def task_result(self, task_id, name, failed=False):
return self.m.swarming.task_result(
id=task_id,
name="recipes-cq:%s" % name,
state=None if not name else self.m.swarming.TaskState.COMPLETED,
failure=failed,
)
def existing_green_tryjobs(self, tryjobs):
search_results = []
for builder_name in tryjobs:
project, bucket, builder = builder_name.split("/")
search_results.append(
build_pb2.Build(
builder=builder_common_pb2.BuilderID(
project=project,
bucket=bucket,
builder=builder,
),
create_time=timestamp_pb2.Timestamp(seconds=1527292217),
)
)
return self.m.buildbucket.simulated_search_results(
search_results,
step_name="get builders.get green tryjobs",
)