blob: a57912605ea54ad99778b6f61c357749fa7fc5a5 [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Recipe for uploading build and test outputs."""
import textwrap
from recipe_engine.recipe_api import Property
from recipe_engine.config import List
DEPS = [
"fuchsia/build",
"fuchsia/checkout",
"fuchsia/tricium_analyze",
"recipe_engine/buildbucket",
"recipe_engine/context",
"recipe_engine/file",
"recipe_engine/json",
"recipe_engine/path",
"recipe_engine/properties",
"recipe_engine/raw_io",
]
PROPERTIES = {
"enabled_analyzers": Property(kind=List(str), help="Enabled analyzers", default=()),
"suggest_fx": Property(
kind=bool, help="Value for api.tricium_analyze.suggest_fx", default=True
),
}
def RunSteps(api, enabled_analyzers, suggest_fx):
checkout = api.checkout.fuchsia_with_options(
manifest="minimal",
path=api.path["start_dir"].join("checkout"),
remote="https://fuchsia.googlesource.com/fuchsia",
)
api.tricium_analyze.build_results = api.build.with_options(
checkout=checkout, fint_params_path="fint_params/tricium.textproto"
)
api.path.mock_add_paths(
checkout.root_dir.join("tools", "mdlint", "rules", "respectful_code_words.json")
)
api.tricium_analyze.suggest_fx = suggest_fx
api.tricium_analyze.checkout = checkout
api.tricium_analyze.check_commit_message()
exts = [
"cpp",
"go",
"gn",
"md",
"rs",
"py",
"o",
]
with api.context(cwd=checkout.root_dir):
filenames = [api.path.sep.join(["path", "to", f"file.{ext}"]) for ext in exts]
# Re-analyzing another C++ file shouldn't re-run the analysis.
api.tricium_analyze(
filenames,
enabled_analyzers=enabled_analyzers,
enabled_luci_analyzers=["Spellchecker"],
)
def GenTests(api):
def change_diff_data(filename):
# This data isn't very realistic, but we only care about the line
# numbers - the file contents don't matter.
diff = f"""\
diff --git a/{filename} b/{filename}
index e684c1e..a76a10e 100644
--- a/{filename}
+++ b/{filename}
@@ -25,0 +26,23 @@
+foo
+bar
@@ -336,0 +360,34 @@
+foo
+bar
@@ -358,105 +414,0 @@
-foo
-bar
"""
diff = textwrap.dedent(diff)
return api.step_data(
f"analyze {filename}.get change diff",
api.raw_io.stream_output_text(diff),
)
def formatted_diff_data(filename):
# This data isn't very realistic, but we only care about the line
# numbers - the file contents don't matter.
diff = f"""\
diff --git a/{filename} b/{filename}
index e684c1e..a76a10e 100644
--- a/{filename}
+++ b/{filename}
@@ -32 +31,0 @@
-foo
@@ -33,0 +33 @@
+foo
@@ -46 +46 @@
- ]
+ ]
@@ -369,2 +368,0 @@
-foo
-bar
@@ -374,0 +373,2 @@
+bar
+foo
"""
diff = textwrap.dedent(diff)
return api.step_data(
f"analyze {filename}.get formatted diff",
api.raw_io.stream_output_text(diff),
)
clang_tidy_errors_json = {
"Diagnostics": [
{
"DiagnosticMessage": {
"FileOffset": 12,
"Message": "error",
"FilePath": "../../path/to/file.cpp",
},
"DiagnosticName": "fuchsia-default",
},
{
"DiagnosticMessage": {
"FileOffset": 16,
"Message": "error",
"FilePath": "../../path/to/file.cpp",
},
"DiagnosticName": "fuchsia-default",
},
]
}
clang_tidy_errors_json_bad_offset = {
"Diagnostics": [
{
"DiagnosticMessage": {
"FileOffset": 65,
"Message": "error",
"FilePath": "../../path/to/file.cpp",
},
"DiagnosticName": "fuchsia-default",
}
]
}
go_vet_output = """go: finding github.com/julienschmidt/httprouter v1.2.0
go: extracting golang.org/x/text v0.3.2
# fuchsia.googlesource.com/tools/artifacts
{
"fuchsia.googlesource.com/tools/artifacts": {
"unreachable": [
{
"posn": "[START_DIR]/checkout/path/to/file.go:39:2",
"message": "unreachable code"
},
{
"posn": "[START_DIR]/checkout/path/to/other_file.go:10:2",
"message": "unreachable code"
}
]
}
}
"""
go_vet_output_no_warnings = """go: finding github.com/julienschmidt/httprouter v1.2.0
go: extracting golang.org/x/text v0.3.2
# fuchsia.googlesource.com/tools/artifacts
{}
"""
# These are sample response returned from a Gerrit API query request. They are
# equivalent to a response like:
# $ curl -b ~/.gitcookies https://fuchsia-review.googlesource.com/a/changes/407720/detail
# However, many fields are omitted.
no_commit_tag = {
"labels": {"Code-Review": {}, "Commit-Message-has-tags": {}},
"current_revision": "123abc",
"revisions": {
"123abc": {"_number": 3, "commit": {"message": "[foo] Add tests"}}
},
}
valid_commit_tag = {
"labels": {
"Code-Review": {},
"Commit-Message-has-tags": {"all": [{"_account_id": 37097}]},
},
"current_revision": "123abc",
"revisions": {
"123abc": {"_number": 3, "commit": {"message": "[foo] Add tests"}}
},
}
no_commit_message_label = {
"labels": {"Code-Review": {}},
"current_revision": "123abc",
"revisions": {
"123abc": {"_number": 3, "commit": {"message": "[foo] Add tests"}}
},
}
non_inclusive_commit_message = {
"labels": {
"Code-Review": {},
"Commit-Message-has-tags": {"all": [{"_account_id": 37097}]},
},
"current_revision": "123abc",
"revisions": {
"123abc": {
"_number": 3,
"commit": {"message": "[foo] master"}, # inclusive-language: ignore
},
},
}
try_build = api.buildbucket.try_build(
git_repo="https://fuchsia.googlesource.com/fuchsia", patch_set=3
)
yield (
api.test("no_analysis")
+ try_build
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
)
yield (
api.test("clang_tidy")
+ try_build
+ api.properties(enabled_analyzers=["CLANGTIdY"]) # check case-insensitivity
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data(
"analyze path/to/file.cpp.clang-tidy.load clang_tidy_fixes.yaml",
stdout=api.json.output(clang_tidy_errors_json),
)
)
yield (
api.test("clang_tidy_error")
+ try_build
+ api.properties(enabled_analyzers=["ClangTidy"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data(
"analyze path/to/file.cpp.clang-tidy.clang-tidy-diff.py",
retcode=1,
)
)
yield (
api.test("clang_tidy_bad_offset")
+ try_build
+ api.properties(enabled_analyzers=["ClangTidy"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data(
"analyze path/to/file.cpp.clang-tidy.load clang_tidy_fixes.yaml",
stdout=api.json.output(clang_tidy_errors_json_bad_offset),
)
)
yield (
api.test("go_vet")
+ try_build
+ api.properties(enabled_analyzers=["GoVet"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data(
"analyze path/to/file.go.go vet.run",
stderr=api.raw_io.output_text(go_vet_output),
)
)
yield (
api.test("go_vet_build_fails")
+ try_build
+ api.properties(enabled_analyzers=["GoVet"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data("analyze path/to/file.go.go vet.run", retcode=1)
)
yield (
api.test("go_vet_no_warnings")
+ try_build
+ api.properties(enabled_analyzers=["GoVet"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ api.step_data(
"analyze path/to/file.go.go vet.run",
stderr=api.raw_io.output_text(go_vet_output_no_warnings),
)
)
yield (
api.test("clang_format")
+ try_build
+ api.properties(enabled_analyzers=["ClangFormat"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ change_diff_data("path/to/file.cpp")
+ formatted_diff_data("path/to/file.cpp")
)
yield (
api.test("gofmt")
+ try_build
+ api.properties(enabled_analyzers=["GoFmt"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
+ change_diff_data("path/to/file.go")
+ formatted_diff_data("path/to/file.go")
)
yield (
api.test("no_commit_tag")
+ try_build
+ api.properties(enabled_analyzers=["CommitTag"])
+ api.step_data(
"check commit tags.get change details", api.json.output(no_commit_tag)
)
)
yield (
api.test("valid_commit_tag")
+ try_build
+ api.properties(enabled_analyzers=["CommitTag"])
+ api.step_data(
"check commit tags.get change details", api.json.output(valid_commit_tag)
)
)
yield (
api.test("no_commit_message")
+ try_build
+ api.properties(enabled_analyzers=["CommitTag"])
+ api.step_data(
"check commit tags.get change details",
api.json.output(no_commit_message_label),
)
)
# The following diff does not include the last two lines containing the word
# `master` so they should be excluded from the inclusivity check.
file_contents = """file contents:
non-inclusive word master
url http://master-url.com http://master-url2.com
master #inclusive-language:disable
notmaster
master # inclusive-language: enable
master
master # inclusive-language: ignore
master
master
master
"""
diff = """\
diff --git a/{0} b/{0}
index e684c1e..a76a10e 100644
--- a/{0}
+++ b/{0}
@@ -2,0 +2,8 @@
+master
+url http://master-url.com http://master-url2.com (urls should be excluded)
+master #inclusive-language:disable (should be ignored)
+notmaster
+master # inclusive-language: enable
+master
+master # inclusive-language: ignore (should be ignored)
+master
@@ -8,1 +9,0 @@
-slave
"""
diff = textwrap.dedent(diff)
yield (
api.test("non_inclusive_word")
+ try_build
+ api.step_data(
"check commit tags.get change details",
api.json.output(non_inclusive_commit_message),
)
+ api.step_data(
"check for inclusivity.get change diff for path/to/file.md",
api.raw_io.stream_output_text(diff.format("path/to/file.md")),
)
+ api.step_data(
"check for inclusivity.read path/to/file.md",
api.file.read_text(file_contents),
)
)