rules.bzl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. # Exceptions. See /LICENSE for license information.
  3. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. """Rules for building fuzz tests."""
  5. load("//bazel/cc_rules:defs.bzl", "cc_test")
  6. def _cc_fuzz_test(corpus, args, data, **kwargs):
  7. """Generates a single test target.
  8. Append the corpus files to the test arguments. When run on a list of
  9. files rather than a directory, libFuzzer-based fuzzers will perform a
  10. regression test against the corpus.
  11. """
  12. cc_test(
  13. data = data + corpus,
  14. args = args + ["$(location %s)" % file for file in corpus],
  15. **kwargs
  16. )
  17. def cc_fuzz_test(
  18. name,
  19. corpus,
  20. args = [],
  21. data = [],
  22. features = [],
  23. tags = [],
  24. deps = [],
  25. shard_count = 1,
  26. **kwargs):
  27. """Macro for C++ fuzzing test.
  28. In order to run tests on a single file, run the fuzzer binary under
  29. bazel-bin directly. That will avoid the args being passed by Bazel.
  30. Args:
  31. name: The main fuzz test rule name.
  32. corpus: List of files to use as a fuzzing corpus.
  33. args: Will have the locations of the corpus files added and passed down
  34. to the fuzz test.
  35. data: Will have the corpus added and passed down to the fuzz test.
  36. features: Will have the "fuzzer" feature added and passed down to the
  37. fuzz test.
  38. tags: Will have "fuzz_test" added and passed down to the fuzz test.
  39. deps: Will have "@llvm-project//compiler-rt:FuzzerMain" added and passed
  40. down to the fuzz test.
  41. shard_count: Provides sharding of the fuzz test.
  42. **kwargs: Remaining arguments passed down to the fuzz test.
  43. """
  44. # Add relevant tag and feature if necessary.
  45. if "fuzz_test" not in tags:
  46. tags = tags + ["fuzz_test"]
  47. if "fuzzer" not in features:
  48. features = features + ["fuzzer"]
  49. if "@llvm-project//compiler-rt:FuzzerMain" not in deps:
  50. deps = deps + ["@llvm-project//compiler-rt:FuzzerMain"]
  51. # The FuzzerMain library doesn't support sharding based on inputs, so we
  52. # general separate test targets in order to shard execution.
  53. if shard_count == 1:
  54. # When there's one shard, only one target is needed.
  55. _cc_fuzz_test(
  56. corpus,
  57. args,
  58. data,
  59. name = name,
  60. features = features,
  61. tags = tags,
  62. deps = deps,
  63. **kwargs
  64. )
  65. else:
  66. # Calculate the number of inputs per shard. This is equivalent to
  67. # ceiling division, so that the corpus subsetting doesn't miss odd
  68. # files.
  69. shard_size = len(corpus) // shard_count
  70. if shard_count * shard_size < len(corpus):
  71. shard_size += 1
  72. # Create separate targets for each shard.
  73. shards = []
  74. for shard in range(shard_count):
  75. shard_name = "{0}.shard{1}".format(name, shard)
  76. shards.append(shard_name)
  77. _cc_fuzz_test(
  78. corpus[shard * shard_size:(shard + 1) * shard_size],
  79. args,
  80. data,
  81. name = shard_name,
  82. features = features,
  83. tags = tags,
  84. deps = deps,
  85. **kwargs
  86. )
  87. # Create a suite containing all shards.
  88. native.test_suite(
  89. name = name,
  90. tests = shards,
  91. )
  92. # Create one target that includes the full corpus.
  93. _cc_fuzz_test(
  94. corpus,
  95. args,
  96. data,
  97. name = "{0}.full_corpus".format(name),
  98. features = features,
  99. tags = tags + ["manual"],
  100. deps = deps,
  101. **kwargs
  102. )