|
| 1 | +# Mutate++ - A C++ Mutation Test Environment |
| 2 | + |
| 3 | +[Mutation testing](https://en.wikipedia.org/wiki/Mutation_testing) is a technique to detect bugs in a program by |
| 4 | +changing its soure code (called "mutation") and checking whether the program's test suite detects this mutation. The |
| 5 | +mutations are designed to mimick typical programming errors (e.g., off-by-one errors). If such errors are not noticed |
| 6 | +by the test suite, the "survived" mutation can be used to create an additional test that would detect it. |
| 7 | + |
| 8 | +- [Overview](#overview) |
| 9 | +- [Installation](#installation) |
| 10 | +- [Example](#example) |
| 11 | +- [Further features](#further-features) |
| 12 | +- [Help!](#help) |
| 13 | +- [Used third-party tools](#used-third-party-tools) |
| 14 | +- [License](#license) |
| 15 | + |
| 16 | + |
| 17 | +## Overview |
| 18 | + |
| 19 | +Mutate++ is a mutation test environment for C++ programs. It supports you with the following tasks: |
| 20 | + |
| 21 | +- create mutations of the source code |
| 22 | +- execute the test suite for each mutation |
| 23 | +- evaluate the outcome of the tests |
| 24 | + |
| 25 | +Mutate++ is a web app that runs locally on your machine. All computation is locally, and no data is shared with anyone. |
| 26 | + |
| 27 | + |
| 28 | +## Installation |
| 29 | + |
| 30 | +You need Python 3 which can be [downloaded here](https://www.python.org/downloads/) or installed by your operating |
| 31 | +systems's package manager. |
| 32 | + |
| 33 | +After checking out the sources from this repository, you need to create a |
| 34 | +[virtual environment](https://docs.python.org/3/tutorial/venv.html) and install the required packages: |
| 35 | + |
| 36 | +```bash |
| 37 | +virtualenv -p python3 venv |
| 38 | +venv/bin/pip install -r requirements |
| 39 | +``` |
| 40 | + |
| 41 | +Next, we need to create a database where mutations and the results are stored: |
| 42 | + |
| 43 | +```bash |
| 44 | +venv/bin/python3 db_create.py |
| 45 | +``` |
| 46 | + |
| 47 | +The database file is an SQLite3 database `app/app.db`. You can then run the app with |
| 48 | + |
| 49 | +```bash |
| 50 | +venv/bin/python3 run.py |
| 51 | +``` |
| 52 | + |
| 53 | +and open it in your browser with the URL <http://127.0.0.1:5000>. |
| 54 | + |
| 55 | + |
| 56 | +## Example |
| 57 | + |
| 58 | +We use a small [example project](https://github.com/bast/cmake-example) to demonstrate the required steps to set up a |
| 59 | +project in Mutate++. We assume that you have git, CMake, make, and a C++ compiler installed. |
| 60 | + |
| 61 | + |
| 62 | +### 1. Prepare the project |
| 63 | + |
| 64 | +Mutate++ will run the following (simplified) workflow: |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +We now set up the project so that we can build and test the project ourselves. |
| 69 | + |
| 70 | +```bash |
| 71 | +cd /tmp |
| 72 | +git clone https://github.com/bast/cmake-example.git |
| 73 | +cd cmake-example |
| 74 | +mkdir build |
| 75 | +cd build |
| 76 | +cmake .. |
| 77 | +``` |
| 78 | + |
| 79 | +We now have three directories we will be refering to later: |
| 80 | + |
| 81 | +- the working directory `/tmp/cmake-example/build`: this is the directory where we will execute the build and test |
| 82 | + commands |
| 83 | +- the source directory `/tmp/cmake-example/src`: this is the directory with the source code |
| 84 | + |
| 85 | +Let's try building and testing: |
| 86 | + |
| 87 | +```bash |
| 88 | +cd /tmp/cmake-example/build |
| 89 | +make |
| 90 | +ctest |
| 91 | +``` |
| 92 | + |
| 93 | +We see that 100% of the tests passed. |
| 94 | + |
| 95 | +In the following, we shall refer to these commands as: |
| 96 | + |
| 97 | +- the build command `make`: this is the command that builds the project from source |
| 98 | +- the test command `ctest`: this is the command that executes the test suite |
| 99 | + |
| 100 | + |
| 101 | +### 2. Create project in Mutate++ |
| 102 | + |
| 103 | + |
| 104 | + |
| 105 | +- Open <http://127.0.0.1:5000> in your browser. |
| 106 | +- Click on ["Projects"](http://127.0.0.1:5000/projects). |
| 107 | +- Click on ["New Project](http://127.0.0.1:5000/projects/create). |
| 108 | +- Enter `Example project` as project name. |
| 109 | +- Enter `/tmp/cmake-example/build` as working directory. |
| 110 | +- Enter `make` as build command. |
| 111 | +- Leave "Quickcheck command" and "Quickcheck timeout" empty. |
| 112 | +- Enter `ctest` as test command. |
| 113 | +- Leave `Test timeout` and `Clean command` empty. |
| 114 | +- Click on "Create project". |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | +Note that we assume the build and test command to succeed if they end with exit code `0`. This is the default for most |
| 119 | +tools like CMake, CTest, Ninja, etc. |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | +We are now in the [project overview](http://127.0.0.1:5000/projects/1) and see the workflow Mutate++ will execute, |
| 124 | +consisting of the steps "build" and "test". We see that no files have been added yet, and also the patches overview is |
| 125 | +empty. |
| 126 | + |
| 127 | + |
| 128 | +### 3. Add a file |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | +- Click on ["Add file"](http://127.0.0.1:5000/projects/1/files/add). |
| 133 | +- In the "Add file to project" dialog, enter the filename `/private/tmp/cmake-example/src/example.cpp`. |
| 134 | +- Click "Add file". |
| 135 | + |
| 136 | +We are back in the [project overview](http://127.0.0.1:5000/projects/1), but see `example.cpp` added to the files. When |
| 137 | +we click on [example.cpp](http://127.0.0.1:5000/projects/1/files/1), we see the source code. Go back to the |
| 138 | +[project overview](http://127.0.0.1:5000/projects/1). |
| 139 | + |
| 140 | + |
| 141 | +### 4. Generate patches |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | +- Next to "example.cpp", click on ["generate patches"](http://127.0.0.1:5000/projects/1/files/1/generate). |
| 146 | +- Leave "first line" and "last line" empty to mutate the whole file. |
| 147 | +- Right now, none of the check boxes are actually implemented, so you can ignore all of them - all mutations will be |
| 148 | + used by default. |
| 149 | +- Click on "Generate patches". |
| 150 | + |
| 151 | +Back in the [project overview](http://127.0.0.1:5000/projects/1), you see that 14 patches have been generated. You can |
| 152 | +inspect them back clicking on ["14 patches"](http://127.0.0.1:5000/projects/1/patches) in the "Patches" section. In |
| 153 | +this overview, you see the individual patches with their line, kind, state and confirmation: |
| 154 | + |
| 155 | +- The kind describes the nature of the patch, e.g. "lineDeletion" or "arithmeticOperator". If you click on a kind, the |
| 156 | + list of patches will be filteted accordingly. |
| 157 | +- The state describes whether the patch was analyzed so far; that is, whether the code has been mutated accordingly and |
| 158 | + the test suite has been executed. So far, all patches are incomplete. |
| 159 | +- The confirmation describes whether you have been evaluated the results so far. All patches are "unknown" so far. |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | +Let's now click on ["4"](http://127.0.0.1:5000/projects/1/patches/4) to have a look at the details of a patch: |
| 164 | + |
| 165 | +- In the top, you see the actual patch: We see that the patch replaces `return f1 + f2;` by `return f1 * f2;`. |
| 166 | +- The description provides a summary of the patch. |
| 167 | +- You could set the confirmation, but this would not make sense as we have not executed the patch so far. |
| 168 | +- Finally, we see that no runs have been executed so far. |
| 169 | + |
| 170 | +Go back to the [project overview](http://127.0.0.1:5000/projects/1). |
| 171 | + |
| 172 | + |
| 173 | +### 5. Execute patches |
| 174 | + |
| 175 | + |
| 176 | + |
| 177 | +Now it is time to actually apply the patches: |
| 178 | + |
| 179 | +- In the top navigation, click on ["Queue"](http://127.0.0.1:5000/queue). We see the 14 patches from before. |
| 180 | +- Click on ["Resume"](http://127.0.0.1:5000/queue/start) to start the execution. |
| 181 | +- When you reload the page, you see that after a few seconds, the queue is empty. |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | +What happened? Mutate++ executed the workflow described above for each patch. That is, it applied the patch to the |
| 186 | +source file `/private/tmp/cmake-example/src/example.cpp`, executed the build command `make` in the working directory |
| 187 | +`/tmp/cmake-example/build`, and then executed the test command `ctest` in the same directory. As we have a trivial |
| 188 | +project with just 14 patches, this is just a matter of seconds. |
| 189 | + |
| 190 | +Go back to the [project overview](http://127.0.0.1:5000/projects/1) by clicking on |
| 191 | +["Projects"](http://127.0.0.1:5000/projects) and ["Example project"](http://127.0.0.1:5000/projects/1). |
| 192 | + |
| 193 | + |
| 194 | +### 6. Evaluate results |
| 195 | + |
| 196 | + |
| 197 | + |
| 198 | +The Patches second got more colorful now. We see a graph that describes the breakdown of the patches: |
| 199 | + |
| 200 | +- 14 patches were genereated in total. |
| 201 | +- 13 patches were killed; meaning they have been detected by the test suite or the code did not compile. |
| 202 | +- 13 of these patches were killed due to a failure. There are other reasons a patch could have been killed which we |
| 203 | + describe later. |
| 204 | +- 1 patch survived, meaning the mutated source code could be compiled and tested without problems. |
| 205 | + |
| 206 | +Let's investigate this by clicking on ["1 survived"](http://127.0.0.1:5000/projects/1/patches?patch_state=survived) to |
| 207 | +see the list of survived patches. We see that patch 14 survived. Click on |
| 208 | +["14"](http://127.0.0.1:5000/projects/1/patches/14). |
| 209 | + |
| 210 | +In the patch overview, we see what happened: |
| 211 | + |
| 212 | +- The patch deleted line 16 of file `/private/tmp/cmake-example/src/example.cpp`, resulting in function |
| 213 | + `multiply_numbers` not returning anything. |
| 214 | +- In the run overview, we see the individual steps of the workflow: |
| 215 | + - [Run 22](http://127.0.0.1:5000/projects/1/patches/14/runs/22) shows the output of the build command. Maybe some |
| 216 | + stricter compiler settings could have warned about the mutation, but instead the code was built successfully. |
| 217 | + - [Run 23](http://127.0.0.1:5000/projects/1/patches/14/runs/23) shows that also the test suite was executed |
| 218 | + successfully. |
| 219 | + |
| 220 | +Why is this an issue? Because no one noticed that we deleted the multiplication! The easiest way to fix this is by |
| 221 | +adding a respective test case. In larger projects, we do not want to switch back and forth between evaluating patches |
| 222 | +and fixing the test suite, so we set the patch "confirm" and press "Submit". |
| 223 | + |
| 224 | +In some cases, the patch would change the source code in ways that it is natural that the test suite would not |
| 225 | +detect it, e.g. in parts of the code that are skipped by preprocessor directives (e.g. with `#ifdef` commands) or when, |
| 226 | +the patch results in equivalent code. In that cases, set the patch to "ignore". |
| 227 | + |
| 228 | +Later, you can filter to show only the |
| 229 | +[confirmed](http://127.0.0.1:5000/projects/1/patches?patch_state=survived&confirmation_state=confirmed) patches and |
| 230 | +adjust your test suite accordingly. |
| 231 | + |
| 232 | +In the [project overview](http://127.0.0.1:5000/projects/1), you also get some statistics on the execution: We see that |
| 233 | +5 patches failed during the build, whereas 9 built successfully. From these 9, 8 failed the tests, and |
| 234 | +[1 patch](http://127.0.0.1:5000/projects/1/patches/14) succeeded. |
| 235 | + |
| 236 | + |
| 237 | +## Further features |
| 238 | + |
| 239 | +- **Timeouts**. Mutating the code can create infinite loops. Therefore, it is wise to set a timeout for the tests in |
| 240 | + the "Create Project" dialog. Tests that take longer than this timeout are treated as if the test failed. |
| 241 | +- **Quickchecks**. A lot of test suites can be split into tests that run very quickly and the full test suite which |
| 242 | + may take many minutes to execute. In the "Create Project" dialog, you can define a "Quickcheck command" to execute |
| 243 | + this quicker test suite first. A lot of patches may be detected quicker this way. |
| 244 | +- **Cleaning up**. Some test suites may required cleaning up afterward. You can provide a "Clean command" in the |
| 245 | + "Create Project" dialog. It will be executed after processing each patch. Note that the example project implements |
| 246 | + a `make clean` command, but adding this as clean command would only increase the build times, because all source |
| 247 | + files would be re-compiled even if only one was changed. |
| 248 | +- **Hashing binaries**. Optimizing compilers may create exactly the same binary for programs that differ syntactically, |
| 249 | + but have the same semantics. Therefore, it can be helpful to calculate a hash of the generated binaries and compare |
| 250 | + it to reference values. If the hashes are the same, then you know the test suite will create the same result. To |
| 251 | + avoid waiting for such a false positive, Mutate++ can stop the evaluation of such patches if the test command or |
| 252 | + quickcheck command return exit code `77`. |
| 253 | + |
| 254 | + |
| 255 | +## Help! |
| 256 | + |
| 257 | +Mutate++ is in a very early stage, and there is a lot to do. In particular, we are aware of severy limitations: |
| 258 | + |
| 259 | +- Mutations are created very naively on a purely syntactial level and often result in code that fails compilation. |
| 260 | + Using, for instance, LLVM-based tools could help to use more type information to create mutations which are more |
| 261 | + likely to result in valid C++ programs. |
| 262 | +- The web app has a terrible UX, and should be overworked by someone who knows more about this... |
| 263 | + |
| 264 | +That said, pull requests and issues are more than welcome! |
| 265 | + |
| 266 | + |
| 267 | +## Used third-party tools |
| 268 | + |
| 269 | +Mutate++ contains the following libraries for the frontend: |
| 270 | + |
| 271 | +- [Bootstrap](http://getbootstrap.com) for the frontend components |
| 272 | +- [Font Awesome](http://fontawesome.io) for the icons |
| 273 | +- [highlight.js](https://highlightjs.org) for source code syntax highlighting |
| 274 | +- [jQuery](https://jquery.com) for some custom JavaScript code |
| 275 | + |
| 276 | +The web app uses [Flask](http://flask.pocoo.org) and [SQLAlchemy](https://www.sqlalchemy.org), and a lot of |
| 277 | +[other packages](requirements.txt). |
| 278 | + |
| 279 | + |
| 280 | +## License |
| 281 | + |
| 282 | +<img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png"> |
| 283 | + |
| 284 | +Mutate++ is licensed under the [MIT License](http://opensource.org/licenses/MIT): |
| 285 | + |
| 286 | +Copyright © 2017 [Niels Lohmann](http://nlohmann.me) |
| 287 | + |
| 288 | +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
| 289 | +documentation files (the “Software”), to deal in the Software without restriction, including without limitation the |
| 290 | +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit |
| 291 | +persons to whom the Software is furnished to do so, subject to the following conditions: |
| 292 | + |
| 293 | +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the |
| 294 | +Software. |
| 295 | + |
| 296 | +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 297 | +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| 298 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| 299 | +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
0 commit comments