• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:new_sets.bzl", "sets")
16load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
17load(
18    "//build/bazel/rules/test_common:args.bzl",
19    "get_all_args_with_prefix",
20    "get_single_arg_with_prefix",
21)
22load("//build/bazel/rules/test_common:rules.bzl", "expect_failure_test")
23load(":cc_library_static.bzl", "cc_library_static")
24load(":clang_tidy.bzl", "generate_clang_tidy_actions")
25
26_PACKAGE_HEADER_FILTER = "^build/bazel/rules/cc/"
27_DEFAULT_GLOBAL_CHECKS = [
28    "android-*",
29    "bugprone-*",
30    "cert-*",
31    "clang-diagnostic-unused-command-line-argument",
32    "google-build-explicit-make-pair",
33    "google-build-namespaces",
34    "google-runtime-operator",
35    "google-upgrade-*",
36    "misc-*",
37    "performance-*",
38    "portability-*",
39    "-bugprone-assignment-in-if-condition",
40    "-bugprone-easily-swappable-parameters",
41    "-bugprone-narrowing-conversions",
42    "-misc-const-correctness",
43    "-misc-no-recursion",
44    "-misc-non-private-member-variables-in-classes",
45    "-misc-unused-parameters",
46    "-performance-no-int-to-ptr",
47    "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling",
48]
49_DEFAULT_CHECKS = [
50    "-misc-no-recursion",
51    "-readability-function-cognitive-complexity",
52    "-bugprone-unchecked-optional-access",
53    "-bugprone-reserved-identifier*",
54    "-cert-dcl51-cpp",
55    "-cert-dcl37-c",
56    "-readability-qualified-auto",
57    "-bugprone-implicit-widening-of-multiplication-result",
58    "-bugprone-easily-swappable-parameters",
59    "-cert-err33-c",
60    "-bugprone-unchecked-optional-access",
61    "-misc-use-anonymous-namespace",
62]
63_DEFAULT_CHECKS_AS_ERRORS = [
64    "-bugprone-assignment-in-if-condition",
65    "-bugprone-branch-clone",
66    "-bugprone-signed-char-misuse",
67    "-misc-const-correctness",
68]
69_EXTRA_ARGS_BEFORE = [
70    "-D__clang_analyzer__",
71    "-Xclang",
72    "-analyzer-config",
73    "-Xclang",
74    "c++-temp-dtor-inlining=false",
75]
76
77def _clang_tidy_impl(ctx):
78    tidy_outs = generate_clang_tidy_actions(
79        ctx,
80        ctx.attr.copts,
81        ctx.attr.deps,
82        ctx.files.srcs,
83        ctx.files.hdrs,
84        ctx.attr.language,
85        ctx.attr.tidy_flags,
86        ctx.attr.tidy_checks,
87        ctx.attr.tidy_checks_as_errors,
88        ctx.attr.tidy_timeout_srcs,
89    )
90    return [
91        DefaultInfo(files = depset(tidy_outs)),
92    ]
93
94_clang_tidy = rule(
95    implementation = _clang_tidy_impl,
96    attrs = {
97        "srcs": attr.label_list(allow_files = True),
98        "deps": attr.label_list(),
99        "copts": attr.string_list(),
100        "hdrs": attr.label_list(allow_files = True),
101        "language": attr.string(values = ["c++", "c"], default = "c++"),
102        "tidy_checks": attr.string_list(),
103        "tidy_checks_as_errors": attr.string_list(),
104        "tidy_flags": attr.string_list(),
105        "tidy_timeout_srcs": attr.label_list(allow_files = True),
106        "_clang_tidy_sh": attr.label(
107            default = Label("@//prebuilts/clang/host/linux-x86:clang-tidy.sh"),
108            allow_single_file = True,
109            executable = True,
110            cfg = "exec",
111            doc = "The clang tidy shell wrapper",
112        ),
113        "_clang_tidy": attr.label(
114            default = Label("@//prebuilts/clang/host/linux-x86:clang-tidy"),
115            allow_single_file = True,
116            executable = True,
117            cfg = "exec",
118            doc = "The clang tidy executable",
119        ),
120        "_clang_tidy_real": attr.label(
121            default = Label("@//prebuilts/clang/host/linux-x86:clang-tidy.real"),
122            allow_single_file = True,
123            executable = True,
124            cfg = "exec",
125        ),
126        "_with_tidy": attr.label(
127            default = "//build/bazel/flags/cc/tidy:with_tidy",
128        ),
129        "_allow_local_tidy_true": attr.label(
130            default = "//build/bazel/flags/cc/tidy:allow_local_tidy_true",
131        ),
132        "_with_tidy_flags": attr.label(
133            default = "//build/bazel/flags/cc/tidy:with_tidy_flags",
134        ),
135        "_default_tidy_header_dirs": attr.label(
136            default = "//build/bazel/flags/cc/tidy:default_tidy_header_dirs",
137        ),
138        "_tidy_timeout": attr.label(
139            default = "//build/bazel/flags/cc/tidy:tidy_timeout",
140        ),
141        "_product_variables": attr.label(
142            default = "//build/bazel/product_config:product_vars",
143        ),
144    },
145    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
146    fragments = ["cpp"],
147)
148
149def _get_all_arg(env, actions, argname):
150    args = get_all_args_with_prefix(actions[0].argv, argname)
151    asserts.false(env, args == [], "could not arguments that start with `{}`".format(argname))
152    return args
153
154def _get_single_arg(actions, argname):
155    return get_single_arg_with_prefix(actions[0].argv, argname)
156
157def _checks_test_impl(ctx):
158    env = analysistest.begin(ctx)
159    actions = analysistest.target_actions(env)
160
161    checks = _get_single_arg(actions, "-checks=").split(",")
162    asserts.set_equals(env, sets.make(ctx.attr.expected_checks), sets.make(checks))
163    if len(ctx.attr.unexpected_checks) > 0:
164        for c in ctx.attr.unexpected_checks:
165            asserts.false(env, c in checks, "found unexpected check in -checks flag: %s" % c)
166
167    checks_as_errors = _get_single_arg(actions, "-warnings-as-errors=").split(",")
168    asserts.set_equals(env, sets.make(ctx.attr.expected_checks_as_errors), sets.make(checks_as_errors))
169
170    return analysistest.end(env)
171
172_checks_test = analysistest.make(
173    _checks_test_impl,
174    attrs = {
175        "expected_checks": attr.string_list(mandatory = True),
176        "expected_checks_as_errors": attr.string_list(mandatory = True),
177        "unexpected_checks": attr.string_list(),
178    },
179)
180
181def _copts_test_impl(ctx):
182    env = analysistest.begin(ctx)
183    actions = analysistest.target_actions(env)
184
185    args = actions[0].argv
186    clang_flags = []
187    for i, a in enumerate(args):
188        if a == "--" and len(args) > i + 1:
189            clang_flags = args[i + 1:]
190            break
191    asserts.true(
192        env,
193        len(clang_flags) > 0,
194        "no flags passed to clang; all arguments: %s" % args,
195    )
196
197    for expected_arg in ctx.attr.expected_copts:
198        asserts.true(
199            env,
200            expected_arg in clang_flags,
201            "expected `%s` not present in clang flags" % expected_arg,
202        )
203
204    return analysistest.end(env)
205
206_copts_test = analysistest.make(
207    _copts_test_impl,
208    attrs = {
209        "expected_copts": attr.string_list(mandatory = True),
210    },
211)
212
213def _tidy_flags_test_impl(ctx):
214    env = analysistest.begin(ctx)
215    actions = analysistest.target_actions(env)
216
217    args = actions[0].argv
218    tidy_flags = []
219    for i, a in enumerate(args):
220        if a == "--" and len(args) > i + 1:
221            tidy_flags = args[:i]
222    asserts.true(
223        env,
224        len(tidy_flags) > 0,
225        "no tidy flags passed to clang-tidy; all arguments: %s" % args,
226    )
227
228    for expected_arg in ctx.attr.expected_tidy_flags:
229        asserts.true(
230            env,
231            expected_arg in tidy_flags,
232            "expected `%s` not present in flags to clang-tidy" % expected_arg,
233        )
234
235    header_filter = _get_single_arg(actions, "-header-filter=")
236    asserts.true(
237        env,
238        header_filter == ctx.attr.expected_header_filter,
239        (
240            "expected header-filter to have value `%s`; got `%s`" %
241            (ctx.attr.expected_header_filter, header_filter)
242        ),
243    )
244
245    extra_arg_before = _get_all_arg(env, actions, "-extra-arg-before=")
246    for expected_arg in ctx.attr.expected_extra_arg_before:
247        asserts.true(
248            env,
249            expected_arg in extra_arg_before,
250            "did not find expected flag `%s` in args to clang-tidy" % expected_arg,
251        )
252
253    return analysistest.end(env)
254
255_tidy_flags_test = analysistest.make(
256    _tidy_flags_test_impl,
257    attrs = {
258        "expected_tidy_flags": attr.string_list(),
259        "expected_header_filter": attr.string(mandatory = True),
260        "expected_extra_arg_before": attr.string_list(),
261    },
262)
263
264def _test_clang_tidy():
265    name = "checks"
266    test_name = name + "_test"
267    checks_test_name = test_name + "_checks"
268    copts_test_name = test_name + "_copts"
269    tidy_flags_test_name = test_name + "_tidy_flags"
270
271    _clang_tidy(
272        name = name,
273        # clang-tidy operates differently on generated and non-generated files
274        # use test_srcs so that the tidy rule doesn't think these are genearted
275        # files
276        srcs = ["//build/bazel/rules/cc/testing:test_srcs"],
277        copts = ["-asdf1", "-asdf2"],
278        tidy_flags = ["-tidy-flag1", "-tidy-flag2"],
279        tags = ["manual"],
280    )
281
282    _checks_test(
283        name = checks_test_name,
284        target_under_test = name,
285        expected_checks = _DEFAULT_CHECKS + _DEFAULT_GLOBAL_CHECKS,
286        expected_checks_as_errors = _DEFAULT_CHECKS_AS_ERRORS,
287    )
288
289    _copts_test(
290        name = copts_test_name,
291        target_under_test = name,
292        expected_copts = ["-asdf1", "-asdf2"],
293    )
294
295    _tidy_flags_test(
296        name = tidy_flags_test_name,
297        target_under_test = name,
298        expected_tidy_flags = ["-tidy-flag1", "-tidy-flag2"],
299        expected_header_filter = _PACKAGE_HEADER_FILTER,
300        expected_extra_arg_before = _EXTRA_ARGS_BEFORE,
301    )
302
303    return [
304        checks_test_name,
305        copts_test_name,
306        tidy_flags_test_name,
307    ]
308
309def _test_custom_header_dir():
310    name = "custom_header_dir"
311    test_name = name + "_test"
312
313    _clang_tidy(
314        name = name,
315        srcs = ["a.cpp"],
316        tidy_flags = ["-header-filter=dir1/"],
317        tags = ["manual"],
318    )
319
320    _tidy_flags_test(
321        name = test_name,
322        target_under_test = name,
323        expected_header_filter = "dir1/",
324    )
325
326    return [
327        test_name,
328    ]
329
330def _test_disabled_checks_are_removed():
331    name = "disabled_checks_are_removed"
332    test_name = name + "_test"
333
334    _clang_tidy(
335        name = name,
336        # clang-tidy operates differently on generated and non-generated files.
337        # use test_srcs so that the tidy rule doesn't think these are genearted
338        # files
339        srcs = ["//build/bazel/rules/cc/testing:test_srcs"],
340        tidy_checks = ["misc-no-recursion", "readability-function-cognitive-complexity"],
341        tags = ["manual"],
342    )
343
344    _checks_test(
345        name = test_name,
346        target_under_test = name,
347        expected_checks = _DEFAULT_CHECKS + _DEFAULT_GLOBAL_CHECKS,
348        expected_checks_as_errors = _DEFAULT_CHECKS_AS_ERRORS,
349        unexpected_checks = ["misc-no-recursion", "readability-function-cognitive-complexity"],
350    )
351
352    return [
353        test_name,
354    ]
355
356def _create_bad_tidy_checks_test(name, tidy_checks, failure_message):
357    name = "bad_tidy_checks_fail_" + name
358    test_name = name + "_test"
359
360    _clang_tidy(
361        name = name,
362        srcs = ["a.cpp"],
363        tidy_checks = tidy_checks,
364        tags = ["manual"],
365    )
366
367    expect_failure_test(
368        name = test_name,
369        target_under_test = name,
370        failure_message = failure_message,
371    )
372
373    return [
374        test_name,
375    ]
376
377def _test_bad_tidy_checks_fail():
378    return (
379        _create_bad_tidy_checks_test(
380            name = "with_spaces",
381            tidy_checks = ["check with spaces"],
382            failure_message = "Check `check with spaces` invalid, cannot contain spaces",
383        ) +
384        _create_bad_tidy_checks_test(
385            name = "with_commas",
386            tidy_checks = ["check,with,commas"],
387            failure_message = "Check `check,with,commas` invalid, cannot contain commas. Split each entry into its own string instead",
388        )
389    )
390
391def _create_bad_tidy_flags_test(name, tidy_flags, failure_message):
392    name = "bad_tidy_flags_fail_" + name
393    test_name = name + "_test"
394
395    _clang_tidy(
396        name = name,
397        srcs = ["a.cpp"],
398        tidy_flags = tidy_flags,
399        tags = ["manual"],
400    )
401
402    expect_failure_test(
403        name = test_name,
404        target_under_test = name,
405        failure_message = failure_message,
406    )
407
408    return [
409        test_name,
410    ]
411
412def _test_bad_tidy_flags_fail():
413    return (
414        _create_bad_tidy_flags_test(
415            name = "without_leading_dash",
416            tidy_flags = ["flag1"],
417            failure_message = "Flag `flag1` must start with `-`",
418        ) +
419        _create_bad_tidy_flags_test(
420            name = "fix_flags",
421            tidy_flags = ["-fix"],
422            failure_message = "Flag `%s` is not allowed, since it could cause multiple writes to the same source file",
423        ) +
424        _create_bad_tidy_flags_test(
425            name = "checks_in_flags",
426            tidy_flags = ["-checks=asdf"],
427            failure_message = "Flag `-checks=asdf` is not allowed, use `tidy_checks` property instead",
428        ) +
429        _create_bad_tidy_flags_test(
430            name = "warnings_as_errors_in_flags",
431            tidy_flags = ["-warnings-as-errors=asdf"],
432            failure_message = "Flag `-warnings-as-errors=asdf` is not allowed, use `tidy_checks_as_errors` property instead",
433        ) +
434        _create_bad_tidy_flags_test(
435            name = "space_in_flags",
436            tidy_flags = ["-flag with spaces"],
437            failure_message = "Bad flag: `-flag with spaces` is not an allowed multi-word flag. Should it be split into multiple flags",
438        )
439    )
440
441def _test_disable_global_checks():
442    name = "disable_global_checks"
443    test_name = name + "_test"
444
445    _clang_tidy(
446        name = name,
447        srcs = ["a.cpp"],
448        tidy_checks = ["-*"],
449        tags = ["manual"],
450    )
451
452    _checks_test(
453        name = test_name,
454        target_under_test = name,
455        expected_checks = ["-*"] + _DEFAULT_CHECKS,
456        expected_checks_as_errors = _DEFAULT_CHECKS_AS_ERRORS,
457    )
458
459    return [
460        test_name,
461    ]
462
463def _cc_library_static_generates_clang_tidy_actions_for_srcs_test_impl(ctx):
464    env = analysistest.begin(ctx)
465    actions = analysistest.target_actions(env)
466
467    clang_tidy_actions = [a for a in actions if a.mnemonic == "ClangTidy"]
468    asserts.equals(
469        env,
470        ctx.attr.expected_num_actions,
471        len(clang_tidy_actions),
472        "expected to have %s clang-tidy actions, but got %s; actions: %s" % (
473            ctx.attr.expected_num_actions,
474            len(clang_tidy_actions),
475            clang_tidy_actions,
476        ),
477    )
478
479    for a in clang_tidy_actions:
480        for input in a.inputs.to_list():
481            input_is_expected_header = input.short_path in [f.short_path for f in ctx.files.expected_headers]
482            if input in ctx.files._clang_tidy_tools or input_is_expected_header:
483                continue
484            asserts.true(
485                env,
486                input in ctx.files.srcs,
487                "clang-tidy operated on a file not in srcs: %s; all inputs: %s" % (input, a.inputs.to_list()),
488            )
489            asserts.true(
490                env,
491                input not in ctx.files.disabled_srcs,
492                "clang-tidy operated on a file in disabled_srcs: %s; all inputs: %s" % (input, a.inputs.to_list()),
493            )
494
495    return analysistest.end(env)
496
497_cc_library_static_generates_clang_tidy_actions_for_srcs_test = analysistest.make(
498    impl = _cc_library_static_generates_clang_tidy_actions_for_srcs_test_impl,
499    attrs = {
500        "expected_num_actions": attr.int(mandatory = True),
501        "srcs": attr.label_list(allow_files = True),
502        "disabled_srcs": attr.label_list(allow_files = True),
503        "expected_headers": attr.label_list(allow_files = True),
504        "_clang_tidy_tools": attr.label_list(
505            default = [
506                "@//prebuilts/clang/host/linux-x86:clang-tidy",
507                "@//prebuilts/clang/host/linux-x86:clang-tidy.real",
508                "@//prebuilts/clang/host/linux-x86:clang-tidy.sh",
509            ],
510            allow_files = True,
511        ),
512    },
513    config_settings = {
514        "@//build/bazel/flags/cc/tidy:allow_local_tidy_true": True,
515    },
516)
517
518def _create_cc_library_static_generates_clang_tidy_actions_for_srcs(
519        name,
520        srcs,
521        expected_num_actions,
522        disabled_srcs = None,
523        expected_headers = []):
524    name = "cc_library_static_generates_clang_tidy_actions_for_srcs_" + name
525    test_name = name + "_test"
526
527    cc_library_static(
528        name = name,
529        srcs = srcs,
530        tidy_disabled_srcs = disabled_srcs,
531        tidy = "local",
532        tags = ["manual"],
533    )
534
535    _cc_library_static_generates_clang_tidy_actions_for_srcs_test(
536        name = test_name,
537        target_under_test = name,
538        expected_num_actions = expected_num_actions,
539        srcs = srcs,
540        disabled_srcs = disabled_srcs,
541        expected_headers = expected_headers + select({
542            "//build/bazel/platforms/os:android": ["@//bionic/libc:generated_android_ids"],
543            "//conditions:default": [],
544        }),
545    )
546
547    return test_name
548
549def _test_cc_library_static_generates_clang_tidy_actions_for_srcs():
550    return [
551        _create_cc_library_static_generates_clang_tidy_actions_for_srcs(
552            name = "with_srcs",
553            srcs = ["a.cpp", "b.cpp"],
554            expected_num_actions = 2,
555        ),
556        _create_cc_library_static_generates_clang_tidy_actions_for_srcs(
557            name = "with_disabled_srcs",
558            srcs = ["a.cpp", "b.cpp"],
559            disabled_srcs = ["b.cpp", "c.cpp"],
560            expected_num_actions = 1,
561        ),
562    ]
563
564def _no_clang_analyzer_on_generated_files_test_impl(ctx):
565    env = analysistest.begin(ctx)
566    actions = analysistest.target_actions(env)
567
568    clang_tidy_actions = [a for a in actions if a.mnemonic == "ClangTidy"]
569    for a in clang_tidy_actions:
570        found_clang_analyzer = False
571        for arg in a.argv:
572            if "-clang-analyzer-*" in arg:
573                found_clang_analyzer = True
574        asserts.true(env, found_clang_analyzer)
575
576    return analysistest.end(env)
577
578_no_clang_analyzer_on_generated_files_test = analysistest.make(
579    impl = _no_clang_analyzer_on_generated_files_test_impl,
580    config_settings = {
581        "@//build/bazel/flags/cc/tidy:allow_local_tidy_true": True,
582    },
583)
584
585def _test_no_clang_analyzer_on_generated_files():
586    name = "no_clang_analyzer_on_generated_files"
587    gen_name = name + "_generated_files"
588    test_name = name + "_test"
589
590    native.genrule(
591        name = gen_name,
592        outs = ["aout.cpp", "bout.cpp"],
593        cmd = "touch $(OUTS)",
594        tags = ["manual"],
595    )
596
597    cc_library_static(
598        name = name,
599        srcs = [":" + gen_name],
600        tidy = "local",
601        tags = ["manual"],
602    )
603
604    _no_clang_analyzer_on_generated_files_test(
605        name = test_name,
606        target_under_test = name,
607    )
608
609    return [
610        test_name,
611    ]
612
613def _clang_tidy_actions_count_no_tidy_env_test_impl(ctx):
614    env = analysistest.begin(ctx)
615    actions = analysistest.target_actions(env)
616
617    clang_tidy_actions = [a for a in actions if a.mnemonic == "ClangTidy"]
618    asserts.equals(
619        env,
620        ctx.attr.expected_num_tidy_actions,
621        len(clang_tidy_actions),
622        "expected to find %d tidy actions, but found %d" % (
623            ctx.attr.expected_num_tidy_actions,
624            len(clang_tidy_actions),
625        ),
626    )
627
628    return analysistest.end(env)
629
630_clang_tidy_actions_count_no_tidy_env_test = analysistest.make(
631    impl = _clang_tidy_actions_count_no_tidy_env_test_impl,
632    attrs = {
633        "expected_num_tidy_actions": attr.int(),
634    },
635)
636
637_clang_tidy_actions_count_with_tidy_true_test = analysistest.make(
638    impl = _clang_tidy_actions_count_no_tidy_env_test_impl,
639    attrs = {
640        "expected_num_tidy_actions": attr.int(),
641    },
642    config_settings = {
643        "@//build/bazel/flags/cc/tidy:with_tidy": True,
644    },
645)
646
647_clang_tidy_actions_count_with_allow_local_tidy_true_test = analysistest.make(
648    impl = _clang_tidy_actions_count_no_tidy_env_test_impl,
649    attrs = {
650        "expected_num_tidy_actions": attr.int(),
651    },
652    config_settings = {
653        "@//build/bazel/flags/cc/tidy:allow_local_tidy_true": True,
654    },
655)
656
657def _test_clang_tidy_runs_if_tidy_true():
658    name = "clang_tidy_runs_if_tidy_true"
659    test_name = name + "_test"
660    with_tidy_test_name = test_name + "_with_tidy_true"
661    allow_local_tidy_true_test_name = test_name + "_allow_local_tidy_true"
662
663    cc_library_static(
664        name = name,
665        srcs = ["a.cpp"],
666        tidy = "local",
667        tags = ["manual"],
668    )
669    _clang_tidy_actions_count_no_tidy_env_test(
670        name = test_name,
671        target_under_test = name,
672        expected_num_tidy_actions = 0,
673    )
674    _clang_tidy_actions_count_with_tidy_true_test(
675        name = with_tidy_test_name,
676        target_under_test = name,
677        expected_num_tidy_actions = 1,
678    )
679    _clang_tidy_actions_count_with_allow_local_tidy_true_test(
680        name = allow_local_tidy_true_test_name,
681        target_under_test = name,
682        expected_num_tidy_actions = 1,
683    )
684    return [
685        test_name,
686        with_tidy_test_name,
687        allow_local_tidy_true_test_name,
688    ]
689
690def _test_clang_tidy_runs_if_attribute_unset():
691    name = "clang_tidy_runs_if_attribute_unset"
692    test_name = name + "_test"
693    with_tidy_test_name = test_name + "_with_tidy_true"
694    allow_local_tidy_true_test_name = test_name + "_allow_local_tidy_true"
695
696    cc_library_static(
697        name = name,
698        srcs = ["a.cpp"],
699        tags = ["manual"],
700    )
701    _clang_tidy_actions_count_no_tidy_env_test(
702        name = test_name,
703        target_under_test = name,
704        expected_num_tidy_actions = 0,
705    )
706    _clang_tidy_actions_count_with_tidy_true_test(
707        name = with_tidy_test_name,
708        target_under_test = name,
709        expected_num_tidy_actions = 1,
710    )
711    _clang_tidy_actions_count_with_allow_local_tidy_true_test(
712        name = allow_local_tidy_true_test_name,
713        target_under_test = name,
714        expected_num_tidy_actions = 0,
715    )
716    return [
717        test_name,
718        with_tidy_test_name,
719        allow_local_tidy_true_test_name,
720    ]
721
722def _test_no_clang_tidy_if_tidy_false():
723    name = "no_clang_tidy_if_tidy_false"
724    test_name = name + "_test"
725    with_tidy_test_name = test_name + "_with_tidy_true"
726    allow_local_tidy_true_test_name = test_name + "_allow_local_tidy_true"
727
728    cc_library_static(
729        name = name,
730        srcs = ["a.cpp"],
731        tidy = "never",
732        tags = ["manual"],
733    )
734    _clang_tidy_actions_count_no_tidy_env_test(
735        name = test_name,
736        target_under_test = name,
737        expected_num_tidy_actions = 0,
738    )
739    _clang_tidy_actions_count_with_tidy_true_test(
740        name = with_tidy_test_name,
741        target_under_test = name,
742        expected_num_tidy_actions = 0,
743    )
744    _clang_tidy_actions_count_with_allow_local_tidy_true_test(
745        name = allow_local_tidy_true_test_name,
746        target_under_test = name,
747        expected_num_tidy_actions = 0,
748    )
749    return [
750        test_name,
751        with_tidy_test_name,
752        allow_local_tidy_true_test_name,
753    ]
754
755def clang_tidy_test_suite(name):
756    native.test_suite(
757        name = name,
758        tests =
759            _test_clang_tidy() +
760            _test_custom_header_dir() +
761            _test_disabled_checks_are_removed() +
762            _test_bad_tidy_checks_fail() +
763            _test_bad_tidy_flags_fail() +
764            _test_disable_global_checks() +
765            _test_cc_library_static_generates_clang_tidy_actions_for_srcs() +
766            _test_no_clang_analyzer_on_generated_files() +
767            _test_no_clang_tidy_if_tidy_false() +
768            _test_clang_tidy_runs_if_tidy_true() +
769            _test_clang_tidy_runs_if_attribute_unset(),
770    )
771