Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 1 | # Quickstart with Bazel |
| 2 | |
| 3 | This tutorial describes how to set up your development environment with Bazel as |
| 4 | the build system, and how to get a simple fuzz test up and running. We recommend |
| 5 | this tutorial if you're new to FuzzTest or if you need a quick refresher. For a |
| 6 | more extensive showcase of the FuzzTest framework, consider doing the |
| 7 | [Codelab](tutorial.md). |
| 8 | |
| 9 | ## Prerequisites |
| 10 | |
| 11 | To use FuzzTest, you'll need: |
| 12 | |
| 13 | * A Linux-based operating system |
| 14 | * [Clang](https://clang.llvm.org/) |
| 15 | * [Bazel](https://bazel.build/) |
| 16 | |
| 17 | ## Set up a Bazel workspace |
| 18 | |
| 19 | First, create a directory where your project will reside: |
| 20 | |
| 21 | ```sh |
| 22 | $ mkdir -p my_workspace && cd my_workspace |
| 23 | ``` |
| 24 | |
| 25 | In this directory, create a file named `WORKSPACE` to define a |
| 26 | [Bazel workspace](https://bazel.build/concepts/build-ref#workspace). The file |
| 27 | will configure FuzzTest along with its transitive dependencies as Bazel external |
| 28 | dependencies: |
| 29 | |
| 30 | ``` |
Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 31 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| 32 | |
| 33 | ################################################################################ |
| 34 | # Direct dependencies |
| 35 | ################################################################################ |
| 36 | |
László Szekeres | 052999a | 2022-10-03 09:02:43 -0700 | [diff] [blame] | 37 | # To use the latest version of FuzzTest, update this regularly to the latest |
| 38 | # commit in the main branch: https://github.com/google/fuzztest/commits/main |
| 39 | FUZZTEST_COMMIT = "62cf00c7341eb05d128d0a3cbce79ac31dbda032" |
| 40 | |
| 41 | http_archive( |
Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 42 | name = "com_google_fuzztest", |
László Szekeres | 052999a | 2022-10-03 09:02:43 -0700 | [diff] [blame] | 43 | strip_prefix = "fuzztest-" + FUZZTEST_COMMIT, |
| 44 | url = "https://github.com/google/fuzztest/archive/" + FUZZTEST_COMMIT + ".zip", |
Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 45 | ) |
| 46 | |
| 47 | http_archive( |
| 48 | name = "com_google_googletest", |
| 49 | sha256 = "81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2", |
| 50 | strip_prefix = "googletest-release-1.12.1", |
| 51 | url = "https://github.com/google/googletest/archive/refs/tags/release-1.12.1.tar.gz", |
| 52 | ) |
| 53 | |
| 54 | ################################################################################ |
| 55 | # Transitive dependencies |
| 56 | ################################################################################ |
| 57 | |
| 58 | # Required by com_google_fuzztest. |
| 59 | http_archive( |
| 60 | name = "com_googlesource_code_re2", |
| 61 | sha256 = "f89c61410a072e5cbcf8c27e3a778da7d6fd2f2b5b1445cd4f4508bee946ab0f", |
| 62 | strip_prefix = "re2-2022-06-01", |
| 63 | url = "https://github.com/google/re2/archive/refs/tags/2022-06-01.tar.gz", |
| 64 | ) |
| 65 | |
| 66 | # Required by com_google_fuzztest. |
| 67 | http_archive( |
| 68 | name = "com_google_absl", |
Xinhao Yuan | 3c2a14c | 2023-04-17 20:25:03 -0700 | [diff] [blame] | 69 | sha256 = "3ea49a7d97421b88a8c48a0de16c16048e17725c7ec0f1d3ea2683a2a75adc21", |
| 70 | strip_prefix = "abseil-cpp-20230125.0", |
| 71 | url = "https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.0.tar.gz", |
Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 72 | ) |
| 73 | |
| 74 | # Required by com_google_absl. |
| 75 | http_archive( |
| 76 | name = "bazel_skylib", |
| 77 | sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", |
| 78 | urls = [ |
| 79 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", |
| 80 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", |
| 81 | ], |
| 82 | ) |
| 83 | ``` |
| 84 | |
| 85 | NOTE: We recommend the users to "live at head," that is, to often update the |
| 86 | `com_google_fuzztest` repository to the latest commit from the main branch. |
| 87 | |
| 88 | NOTE: It is possible to use FuzzTest without GoogleTest, and thus without |
| 89 | depending on `com_google_googletest`, but this is beyond the scope of this |
| 90 | tutorial. |
| 91 | |
| 92 | Next, create a [Bazel configuration file](https://bazel.build/run/bazelrc) named |
| 93 | `.bazelrc` to configure the build flags: |
| 94 | |
| 95 | ``` |
| 96 | # Force the use of Clang for all builds. FuzzTest relies on Clang for sanitizer |
| 97 | # coverage (https://clang.llvm.org/docs/SanitizerCoverage.html). |
| 98 | build --action_env=CC=clang |
| 99 | build --action_env=CXX=clang++ |
| 100 | |
| 101 | # Use the C++17 standard. |
| 102 | build --cxxopt=-std=c++17 |
| 103 | |
Filip Niksic | a63c5f1 | 2022-09-28 14:38:55 -0700 | [diff] [blame] | 104 | # Show everything when running tests. |
| 105 | test --test_output=streamed |
| 106 | |
| 107 | # To create this file, please run: |
| 108 | # |
| 109 | # bazel run @com_google_fuzztest//bazel:setup_configs > fuzztest.bazelrc |
| 110 | # |
| 111 | try-import %workspace%/fuzztest.bazelrc |
| 112 | ``` |
| 113 | |
| 114 | As suggested by the comments, generate a file named `fuzztest.bazelrc` with |
| 115 | additional build configurations. We generate the file using a script from the |
| 116 | FuzzTest repo to make sure it contains the correct and recent settings: |
| 117 | |
| 118 | ```sh |
| 119 | $ bazel run @com_google_fuzztest//bazel:setup_configs > fuzztest.bazelrc |
| 120 | ``` |
| 121 | |
| 122 | ## Create and run a fuzz test |
| 123 | |
| 124 | With the Bazel workspace set up, you can start using FuzzTest. Let's create a |
| 125 | trivial example to make sure everything runs correctly. |
| 126 | |
| 127 | Create a file named `first_fuzz_test.cc` in the directory `my_workspace` with |
| 128 | the following contents: |
| 129 | |
| 130 | ```c++ |
| 131 | #include "fuzztest/fuzztest.h" |
| 132 | #include "gtest/gtest.h" |
| 133 | |
| 134 | TEST(MyTestSuite, OnePlustTwoIsTwoPlusOne) { |
| 135 | EXPECT_EQ(1 + 2, 2 + 1); |
| 136 | } |
| 137 | |
| 138 | void IntegerAdditionCommutes(int a, int b) { |
| 139 | EXPECT_EQ(a + b, b + a); |
| 140 | } |
| 141 | FUZZ_TEST(MyTestSuite, IntegerAdditionCommutes); |
| 142 | ``` |
| 143 | |
| 144 | The file contains two tests in the test suite named `MyTestSuite`. The first one |
| 145 | is a unit test named `OnePlustTwoIsTwoPlusOne` asserting that integer addition |
| 146 | commutes for two specific values. |
| 147 | |
| 148 | The second test is a fuzz test that captures the commutative property of integer |
| 149 | addition more generally. The test consists of a property function with a |
| 150 | suggestive name `IntegerAdditionCommutes`, and the test registration using the |
| 151 | macro `FUZZ_TEST`. The property function asserts the commutative property for |
| 152 | two generic integer parameters. |
| 153 | |
| 154 | To build and run the tests, create a file named `BUILD` with the following |
| 155 | contents: |
| 156 | |
| 157 | ``` |
| 158 | cc_test( |
| 159 | name = "first_fuzz_test", |
| 160 | srcs = ["first_fuzz_test.cc"], |
| 161 | deps = [ |
| 162 | "@com_google_fuzztest//fuzztest", |
| 163 | "@com_google_fuzztest//fuzztest:fuzztest_gtest_main", |
| 164 | "@com_google_googletest//:gtest" |
| 165 | ], |
| 166 | ) |
| 167 | ``` |
| 168 | |
| 169 | This defines a C++ test binary and links it with the FuzzTest and GoogleTest |
| 170 | libraries, as well as the version of the default GoogleTest main that supports |
| 171 | FuzzTest. |
| 172 | |
| 173 | There are two ways to run the tests: in the unit test mode and in the fuzzing |
| 174 | mode. The unit test mode runs both test for a short time without sanitizer and |
| 175 | coverage instrumentation: |
| 176 | |
| 177 | ``` |
| 178 | $ bazel test :first_fuzz_test |
| 179 | |
| 180 | WARNING: Streamed test output requested. All tests will be run locally, without sharding, one at a time |
| 181 | INFO: Analyzed target //:first_fuzz_test (71 packages loaded, 1231 targets configured). |
| 182 | INFO: Found 1 test target... |
| 183 | [==========] Running 2 tests from 1 test suite. |
| 184 | [----------] Global test environment set-up. |
| 185 | [----------] 2 tests from MyTestSuite |
| 186 | [ RUN ] MyTestSuite.OnePlustTwoIsTwoPlusOne |
| 187 | [ OK ] MyTestSuite.OnePlustTwoIsTwoPlusOne (0 ms) |
| 188 | [ RUN ] MyTestSuite.IntegerAdditionCommutes |
| 189 | [ OK ] MyTestSuite.IntegerAdditionCommutes (13 ms) |
| 190 | [----------] 2 tests from MyTestSuite (13 ms total) |
| 191 | |
| 192 | [----------] Global test environment tear-down |
| 193 | [==========] 2 tests from 1 test suite ran. (13 ms total) |
| 194 | [ PASSED ] 2 tests. |
| 195 | Target //:first_fuzz_test up-to-date: |
| 196 | bazel-bin/first_fuzz_test |
| 197 | INFO: Elapsed time: 14.089s, Critical Path: 4.54s |
| 198 | INFO: 488 processes: 262 internal, 226 linux-sandbox. |
| 199 | INFO: Build completed successfully, 488 total actions |
| 200 | //:first_fuzz_test PASSED in 0.1s |
| 201 | |
| 202 | Executed 1 out of 1 test: 1 test passes. |
| 203 | INFO: Build completed successfully, 488 total actions |
| 204 | ``` |
| 205 | |
| 206 | The fuzzing mode runs a single specified fuzz test with sanitizer and coverage |
| 207 | instrumentation. It keeps running the test with different input values until it |
| 208 | finds a crash or it is explicitly terminated by the user: |
| 209 | |
| 210 | ``` |
| 211 | $ bazel run --config=fuzztest :first_fuzz_test -- \ |
| 212 | --fuzz=MyTestSuite.IntegerAdditionCommutes |
| 213 | |
| 214 | INFO: Build options --copt, --dynamic_mode, --linkopt, and 1 more have changed, discarding analysis cache. |
| 215 | INFO: Analyzed target //:first_fuzz_test (0 packages loaded, 1231 targets configured). |
| 216 | INFO: Found 1 target... |
| 217 | Target //:first_fuzz_test up-to-date: |
| 218 | bazel-bin/first_fuzz_test |
| 219 | INFO: Elapsed time: 14.570s, Critical Path: 5.11s |
| 220 | INFO: 161 processes: 4 internal, 157 linux-sandbox. |
| 221 | INFO: Build completed successfully, 161 total actions |
| 222 | INFO: Build completed successfully, 161 total actions |
| 223 | exec ${PAGER:-/usr/bin/less} "$0" || exit 1 |
| 224 | Executing tests from //:first_fuzz_test |
| 225 | ----------------------------------------------------------------------------- |
| 226 | [.] Sanitizer coverage enabled. Counter map size: 21290, Cmp map size: 262144 |
| 227 | Note: Google Test filter = MyTestSuite.IntegerAdditionCommutes |
| 228 | [==========] Running 1 test from 1 test suite. |
| 229 | [----------] Global test environment set-up. |
| 230 | [----------] 1 test from MyTestSuite |
| 231 | [ RUN ] MyTestSuite.IntegerAdditionCommutes |
| 232 | [*] Corpus size: 1 | Edges covered: 131 | Fuzzing time: 504.798us | Total runs: 1.00e+00 | Runs/secs: 1 |
| 233 | [*] Corpus size: 2 | Edges covered: 133 | Fuzzing time: 934.176us | Total runs: 3.00e+00 | Runs/secs: 3 |
| 234 | [*] Corpus size: 3 | Edges covered: 134 | Fuzzing time: 2.384383ms | Total runs: 5.30e+01 | Runs/secs: 53 |
| 235 | [*] Corpus size: 4 | Edges covered: 137 | Fuzzing time: 2.732274ms | Total runs: 5.40e+01 | Runs/secs: 54 |
| 236 | [*] Corpus size: 5 | Edges covered: 137 | Fuzzing time: 7.275553ms | Total runs: 2.48e+02 | Runs/secs: 248 |
| 237 | [*] Corpus size: 6 | Edges covered: 137 | Fuzzing time: 21.97155ms | Total runs: 9.12e+02 | Runs/secs: 912 |
| 238 | [*] Corpus size: 7 | Edges covered: 137 | Fuzzing time: 166.721522ms | Total runs: 8.49e+03 | Runs/secs: 8491 |
| 239 | [*] Corpus size: 8 | Edges covered: 146 | Fuzzing time: 500.398497ms | Total runs: 2.77e+04 | Runs/secs: 27741 |
| 240 | [*] Corpus size: 9 | Edges covered: 146 | Fuzzing time: 500.821386ms | Total runs: 2.77e+04 | Runs/secs: 27742 |
| 241 | [*] Corpus size: 10 | Edges covered: 147 | Fuzzing time: 2.597553524s | Total runs: 1.75e+05 | Runs/secs: 87476 |
| 242 | ^C |
| 243 | ``` |
| 244 | |
| 245 | Congratulations! You're now all set for fuzzing with FuzzTest. |
| 246 | |
| 247 | ## Next steps |
| 248 | |
| 249 | * This tutorial covered the basic setup of the build environment with Bazel. |
| 250 | To learn more about the FuzzTest framework and how to use it, check out the |
| 251 | [Codelab](tutorial.md). |
| 252 | * Explore the rest of the [documentation](./). |