├── .clang-format ├── .dir-locals.el ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── appveyor.yml ├── danluu_example.cc ├── doc ├── Gen.md ├── Seq.md ├── Shrinkable.md ├── assertions.md ├── boost.md ├── boost_test.md ├── configuration.md ├── debugging.md ├── displaying.md ├── distribution.md ├── generators.md ├── generators_ref.md ├── gmock.md ├── gtest.md ├── properties.md ├── state.md ├── state_ref.md └── user_guide.md ├── encode.h ├── examples ├── CMakeLists.txt ├── boost_test │ ├── CMakeLists.txt │ └── main.cpp ├── classify │ ├── CMakeLists.txt │ └── main.cpp ├── counter │ ├── CMakeLists.txt │ └── main.cpp ├── database │ ├── CMakeLists.txt │ ├── Database.cpp │ ├── Database.h │ ├── DatabaseConnection.cpp │ ├── DatabaseConnection.h │ ├── Generators.h │ ├── User.cpp │ ├── User.h │ └── main.cpp ├── gtest │ ├── CMakeLists.txt │ └── main.cpp └── mapparser │ ├── CMakeLists.txt │ ├── MapParser.cpp │ ├── MapParser.h │ └── main.cpp ├── ext ├── CMakeLists.txt └── get_boost.sh ├── extras ├── CMakeLists.txt ├── boost │ ├── CMakeLists.txt │ ├── include │ │ └── rapidcheck │ │ │ ├── boost.h │ │ │ └── gen │ │ │ └── boost │ │ │ ├── Optional.h │ │ │ └── Optional.hpp │ └── test │ │ ├── CMakeLists.txt │ │ ├── OptionalTests.cpp │ │ └── main.cpp ├── boost_test │ ├── CMakeLists.txt │ └── include │ │ └── rapidcheck │ │ └── boost_test.h ├── catch │ ├── CMakeLists.txt │ └── include │ │ └── rapidcheck │ │ └── catch.h ├── gmock │ ├── CMakeLists.txt │ ├── include │ │ └── rapidcheck │ │ │ └── gmock.h │ └── test │ │ ├── CMakeLists.txt │ │ └── main.cpp └── gtest │ ├── CMakeLists.txt │ └── include │ └── rapidcheck │ └── gtest.h ├── fuzz_encoding.cc ├── include ├── rapidcheck.h └── rapidcheck │ ├── Assertions.h │ ├── Assertions.hpp │ ├── BeforeMinimalTestCase.h │ ├── Check.h │ ├── Check.hpp │ ├── Classify.h │ ├── Classify.hpp │ ├── Gen.h │ ├── Gen.hpp │ ├── GenerationFailure.h │ ├── Log.h │ ├── Log.hpp │ ├── Maybe.h │ ├── Maybe.hpp │ ├── Nothing.h │ ├── Random.h │ ├── Random.hpp │ ├── Seq.h │ ├── Seq.hpp │ ├── Show.h │ ├── Show.hpp │ ├── Shrinkable.h │ ├── Shrinkable.hpp │ ├── Traits.h │ ├── detail │ ├── AlignedUnion.h │ ├── Any.h │ ├── Any.hpp │ ├── ApplyTuple.h │ ├── BitStream.h │ ├── BitStream.hpp │ ├── Capture.h │ ├── Configuration.h │ ├── ExecFixture.h │ ├── FrequencyMap.h │ ├── FunctionTraits.h │ ├── FuzzData.h │ ├── ImplicitParam.h │ ├── ImplicitParam.hpp │ ├── IntSequence.h │ ├── Platform.h │ ├── Property.h │ ├── Property.hpp │ ├── PropertyContext.h │ ├── Results.h │ ├── Results.hpp │ ├── Serialization.h │ ├── Serialization.hpp │ ├── ShowType.h │ ├── ShowType.hpp │ ├── TestListener.h │ ├── TestListenerAdapter.h │ ├── TestMetadata.h │ ├── TestParams.h │ ├── Traits.h │ ├── TypeList.h │ ├── Utility.h │ ├── Variant.h │ └── Variant.hpp │ ├── fn │ ├── Common.h │ └── Common.hpp │ ├── gen │ ├── Arbitrary.h │ ├── Arbitrary.hpp │ ├── Build.h │ ├── Build.hpp │ ├── Chrono.h │ ├── Chrono.hpp │ ├── Container.h │ ├── Container.hpp │ ├── Create.h │ ├── Create.hpp │ ├── Exec.h │ ├── Exec.hpp │ ├── Maybe.h │ ├── Maybe.hpp │ ├── Numeric.h │ ├── Numeric.hpp │ ├── Predicate.h │ ├── Predicate.hpp │ ├── Select.h │ ├── Select.hpp │ ├── Text.h │ ├── Text.hpp │ ├── Transform.h │ ├── Transform.hpp │ ├── Tuple.h │ ├── Tuple.hpp │ └── detail │ │ ├── ExecHandler.h │ │ ├── ExecRaw.h │ │ ├── ExecRaw.hpp │ │ ├── GenerationHandler.h │ │ ├── Recipe.h │ │ ├── ScaleInteger.h │ │ ├── ShrinkValueIterator.h │ │ └── ShrinkValueIterator.hpp │ ├── seq │ ├── Create.h │ ├── Create.hpp │ ├── Operations.h │ ├── Operations.hpp │ ├── SeqIterator.h │ ├── SeqIterator.hpp │ ├── Transform.h │ └── Transform.hpp │ ├── shrink │ ├── Shrink.h │ └── Shrink.hpp │ ├── shrinkable │ ├── Create.h │ ├── Create.hpp │ ├── Operations.h │ ├── Operations.hpp │ ├── Transform.h │ └── Transform.hpp │ ├── state.h │ └── state │ ├── Command.h │ ├── Command.hpp │ ├── Commands.h │ ├── Commands.hpp │ ├── State.h │ ├── State.hpp │ └── gen │ ├── Commands.h │ ├── Commands.hpp │ ├── ExecCommands.h │ └── ExecCommands.hpp ├── main.cc ├── src ├── BeforeMinimalTestCase.cpp ├── Check.cpp ├── Classify.cpp ├── GenerationFailure.cpp ├── Log.cpp ├── Random.cpp ├── Show.cpp ├── detail │ ├── Any.cpp │ ├── Assertions.cpp │ ├── Base64.cpp │ ├── Base64.h │ ├── Configuration.cpp │ ├── DefaultTestListener.cpp │ ├── DefaultTestListener.h │ ├── FrequencyMap.cpp │ ├── FuzzData.cpp │ ├── ImplicitParam.cpp │ ├── LogTestListener.cpp │ ├── LogTestListener.h │ ├── MapParser.cpp │ ├── MapParser.h │ ├── MulticastTestListener.cpp │ ├── MulticastTestListener.h │ ├── ParseException.cpp │ ├── ParseException.h │ ├── Platform.cpp │ ├── Property.cpp │ ├── PropertyContext.cpp │ ├── ReproduceListener.cpp │ ├── ReproduceListener.h │ ├── Results.cpp │ ├── Serialization.cpp │ ├── StringSerialization.cpp │ ├── StringSerialization.h │ ├── TestMetadata.cpp │ ├── TestParams.cpp │ ├── Testing.cpp │ └── Testing.h └── gen │ ├── Numeric.cpp │ ├── Text.cpp │ └── detail │ ├── ExecHandler.cpp │ ├── GenerationHandler.cpp │ ├── Recipe.cpp │ └── ScaleInteger.cpp └── test ├── AssertionsTests.cpp ├── CMakeLists.txt ├── CheckTests.cpp ├── ClassifyTests.cpp ├── GenTests.cpp ├── LogTests.cpp ├── MaybeTests.cpp ├── RandomTests.cpp ├── SeqTests.cpp ├── ShowTests.cpp ├── ShrinkableTests.cpp ├── detail ├── AnyTests.cpp ├── ApplyTupleTests.cpp ├── Base64Tests.cpp ├── BitStreamTests.cpp ├── CaptureTests.cpp ├── ConfigurationTests.cpp ├── DefaultTestListenerTests.cpp ├── FrequencyMapTests.cpp ├── ImplicitParamTests.cpp ├── LogTestListenerTests.cpp ├── MapParserTests.cpp ├── MulticastTestListenerTests.cpp ├── PropertyTests.cpp ├── ReproduceListenerTests.cpp ├── ResultsTests.cpp ├── SerializationTests │ ├── CompactIntegers.cpp │ ├── CompactRanges.cpp │ ├── Integers.cpp │ └── Misc.cpp ├── ShowTypeTests.cpp ├── StringSerializationTests.cpp ├── TestMetadataTests.cpp ├── TestParamsTests.cpp ├── TestingTests.cpp └── VariantTests.cpp ├── fn └── CommonTests.cpp ├── gen ├── BuildTests.cpp ├── ChronoTests.cpp ├── ContainerTests │ ├── Common.h │ ├── Fixed.cpp │ ├── NonFixed.cpp │ └── Unique.cpp ├── CreateTests.cpp ├── ExecTests.cpp ├── MaybeTests.cpp ├── NumericTests.cpp ├── PredicateTests.cpp ├── SelectTests.cpp ├── TextTests.cpp ├── TransformTests.cpp ├── TupleTests.cpp └── detail │ ├── ExecRawTests.cpp │ ├── RecipeTests.cpp │ ├── ScaleIntegerTests.cpp │ └── ShrinkValueIteratorTests.cpp ├── main.cpp ├── seq ├── CreateTests.cpp ├── OperationsTests.cpp ├── SeqIteratorTests.cpp └── TransformTests.cpp ├── shrink └── ShrinkTests.cpp ├── shrinkable ├── CreateTests.cpp ├── OperationsTests.cpp └── TransformTests.cpp ├── state ├── CommandTests.cpp ├── CommandsTests.cpp ├── IntegrationTests.cpp ├── StateTests.cpp └── gen │ ├── CommandsTests.cpp │ └── ExecCommandsTests.cpp └── util ├── AppleOrange.h ├── ArbitraryRandom.cpp ├── ArbitraryRandom.h ├── Box.h ├── DestructNotifier.h ├── GenUtils.cpp ├── GenUtils.h ├── Generators.h ├── IntVec.h ├── Logger.h ├── Meta.h ├── MockTestListener.h ├── NonCopyableModel.h ├── Predictable.h ├── SeqUtils.h ├── Serialization.h ├── ShowTypeTestUtils.h ├── ShrinkableUtils.cpp ├── ShrinkableUtils.h ├── TemplateProps.h ├── ThrowOnCopy.h ├── TypeListMacros.h └── Util.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: true 5 | AlignEscapedNewlinesLeft: false 6 | AlignOperands: false 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: All 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AlwaysBreakAfterDefinitionReturnType: false 15 | AlwaysBreakBeforeMultilineStrings: true 16 | AlwaysBreakTemplateDeclarations: true 17 | BinPackArguments: false 18 | BinPackParameters: false 19 | BreakBeforeBinaryOperators: None 20 | BreakBeforeBraces: Attach 21 | BreakBeforeTernaryOperators: true 22 | BreakConstructorInitializersBeforeComma: true 23 | ColumnLimit: 80 24 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 25 | ConstructorInitializerIndentWidth: 4 26 | ContinuationIndentWidth: 4 27 | Cpp11BracedListStyle: true 28 | DerivePointerAlignment: false 29 | DisableFormat: false 30 | IndentCaseLabels: false 31 | IndentWidth: 2 32 | IndentWrappedFunctionNames: false 33 | KeepEmptyLinesAtTheStartOfBlocks: true 34 | Language: Cpp 35 | MaxEmptyLinesToKeep: 1 36 | NamespaceIndentation: None 37 | PointerAlignment: Right 38 | SpaceAfterCStyleCast: false 39 | SpaceBeforeAssignmentOperators: true 40 | SpaceBeforeParens: ControlStatements 41 | SpaceInEmptyParentheses: false 42 | SpacesBeforeTrailingComments: 1 43 | SpacesInAngles: false 44 | SpacesInCStyleCastParentheses: false 45 | SpacesInContainerLiterals: true 46 | SpacesInParentheses: false 47 | SpacesInSquareBrackets: false 48 | Standard: Cpp11 49 | TabWidth: 2 50 | UseTab: Never -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((nil . ((c-basic-offset . 2)))) 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | GPATH 3 | GRTAGS 4 | GTAGS 5 | TAGS 6 | ext/gmock 7 | ext/boost -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/catch"] 2 | path = ext/catch 3 | url = https://github.com/philsquared/Catch.git 4 | [submodule "ext/googletest"] 5 | path = ext/googletest 6 | url = https://github.com/google/googletest 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015, Emil Eriksson 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | os: Visual Studio 2015 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | environment: 9 | matrix: 10 | - CMAKE_BUILD_TYPE: Debug 11 | ARCH: amd64 12 | - CMAKE_BUILD_TYPE: Release 13 | ARCH: amd64 14 | - CMAKE_BUILD_TYPE: Debug 15 | ARCH: x86 16 | - CMAKE_BUILD_TYPE: Release 17 | ARCH: x86 18 | 19 | install: 20 | - git submodule update --init --recursive 21 | 22 | build_script: 23 | - md build 24 | - cd build 25 | - "\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" %ARCH%" 26 | - cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DRC_ENABLE_TESTS=ON -DRC_ENABLE_EXAMPLES=ON -DRC_ENABLE_GTEST=ON -DRC_ENABLE_GMOCK=ON -DRC_ENABLE_BOOST=ON -DRC_ENABLE_BOOST_TEST=ON -G "NMake Makefiles" .. 27 | - nmake 28 | 29 | test_script: 30 | - ctest --output-on-failure -------------------------------------------------------------------------------- /danluu_example.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "rapidcheck.h" 8 | 9 | /** C++ implementation of Go version from https://danluu.com/testing 10 | */ 11 | 12 | /// Checks that a number has its bottom bits set 13 | bool some_filter(int x) { 14 | static_assert(sizeof(int) == 4, "Sizeof(int) needs to be 4 bytes"); 15 | for (int i = 0; i < 32; i++) { 16 | if ((x & 1) != 1) { 17 | return false; 18 | } 19 | x >>= 1; 20 | } 21 | return true; 22 | } 23 | 24 | template 25 | std::ostream &print(std::ostream &os, const T &t) { 26 | for (const auto &x : t) { 27 | std::cout << ", " << x; 28 | } 29 | return os; 30 | } 31 | 32 | /// Takes an array and returns a non-zero int 33 | int dut(const std::vector &a) { 34 | if (a.size() != 4) { 35 | return 1; 36 | } 37 | 38 | if (some_filter(a[0]) && some_filter(a[1]) && some_filter(a[2]) && 39 | some_filter(a[3])) { 40 | return 0; // Intentional bug: documentation promises non-zero. 41 | } 42 | 43 | return 5; 44 | } 45 | 46 | int fud(int x) { return x == 0; } 47 | 48 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 49 | rc::check("Fuzz-fud", 50 | [](const std::vector &x) { assert(dut(x) != 0); }, 51 | Data, 52 | Size); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /doc/Gen.md: -------------------------------------------------------------------------------- 1 | `Gen` 2 | ======= 3 | _This section is incomplete._ 4 | -------------------------------------------------------------------------------- /doc/Seq.md: -------------------------------------------------------------------------------- 1 | `Seq` 2 | ======= 3 | _This section is incomplete._ 4 | 5 | `Seq` implements a lazy sequence (often called a "stream") of values of type `T`. It has a super simple interface for retrieving these values consisting only of the method `Maybe next()`. As can be seen, it returns a `Maybe` which is similar to `boost::optional`, Haskell's `Maybe` or other similar types that either contain a value or be empty. `next` successively returns the values in the sequence until the sequence is exhausted after which it will return an empty `Maybe` to signal that there are no more values. 6 | 7 | `Seq`s are mutable since calling `next` will modify the `Seq` by advancing it. They also have value semantics since you can copy them and pass them around like any other value. 8 | -------------------------------------------------------------------------------- /doc/Shrinkable.md: -------------------------------------------------------------------------------- 1 | `Shrinkable` 2 | ============== 3 | _This section is incomplete._ 4 | 5 | `Shrinkable` is a fundamental template class in RapidCheck. An instance of `Shrinkable` represents some value of type `T` and provides a way to access all the ways in which this value can be shrunk. It has value semantics which means that it can be copied and passed around just like any other value. It has two member functions: 6 | 7 | - `T value() const` returns the represented value of this `Shrinkable`. 8 | - `Seq> shrinks() const` returns a `Seq` (see [the documentation for `Seq`](Seq.md) for more information) of all the possible ways of shrinking the value from the smallest to the largest. 9 | 10 | There are two important things to note here: 11 | 12 | - `value()` is a member function, not a member variable. This means that the way that the `Shrinkable` obtains the value `T` that it returns is abstracted away. If this wasn't the case, we would have to impose the following restrictions on `T`: 13 | - `T` would need to have a copy constructor. Otherwise, we would only be able to retrieve `T` once. This means we couldn't generate, for example, `std::unique_ptr`s to objects. Non-copyable objects are very common in mainstream C++ code so this would be undesirable. 14 | - `T`'s copy constructor would have to produce copies that shared no state with the original value. Without this guarantee, we would not be able to repeatedly provide semantically equivalent objects since the consumer of `T` might modify the copy which could also modify the original. The obvious example of a type which certainly doesn't provide the non-shared-state guarantee is `std::shared_ptr` whose entire purpose is having a copy constructor that yields copies with shared state. 15 | - `shrinks()` returns a `Seq` of `Shrinkable` and not just `T`. This means that a `Shrinkable` is a value of `T` combined with a tree of possible ways of shrinking it, not just a list. This allows RapidCheck to _recursively_ search this for the smallest value value satisfying some condition (usually failing the property). 16 | -------------------------------------------------------------------------------- /doc/boost.md: -------------------------------------------------------------------------------- 1 | Boost type support 2 | ================== 3 | RapidCheck comes with support for (currently, a very limited set of) common Boost library types. This support is available through the `extras/boost` module. You can either directly add the `extras/boost/include` directory to your include path or link against the `rapidcheck_boost` target in your `CMakeLists.txt`. You can then simply `#include `. 4 | 5 | The Boost support is currently very limited so if you feel that you're missing some very essential type, please file an issue. Even better, submit a pull request complete with tests! 6 | 7 | ## Arbitrary ## 8 | The following types currently have `Arbitrary` specializations: 9 | - `boost::optional` 10 | 11 | ## Generators reference ## 12 | All the Boost generators are located in the `rc::gen::boost` namespace. 13 | 14 | #### `Gen> optional(Gen gen)` #### 15 | Equivalent to `gen::maybe` but for `boost::optional` instead. 16 | 17 | ``` 18 | // Example: 19 | const auto optionalSmallInt = *gen::boost::optional(gen::inRange(0, 100)); 20 | ``` 21 | -------------------------------------------------------------------------------- /doc/displaying.md: -------------------------------------------------------------------------------- 1 | Displaying values 2 | ================= 3 | RapidCheck often needs to display values as strings, such as when printing assertion messages or counterexamples. To do this, RapidCheck calls the `rc::show(const T &, std::ostream &)` template function. This template takes a value to display and a stream to output the string representation to. Given a value `v` and an output stream `os`, calling this template will do one of the following 4 | 5 | - If there is a valid overload for a call to `showValue(v, os)`, calls this overload. 6 | - If there is a valid stream insertion operator (i.e. `std::ostream &operator<<(std::ostream &, const T &)`) defined, this is used. 7 | - Otherwise, `` is printed. 8 | 9 | In essence, this means that you simply have to have a valid stream insertion operator available for RapidCheck to be able to display values of your type. If you don't want to define such an operator or simply want RapidCheck to display values differently than stream insertion would have done, you can provide an overload of `showValue` like this: 10 | 11 | ```C++ 12 | void showValue(const Person &person, std::ostream &os) { 13 | os << "First name: " << person.firstName << std::endl; 14 | os << "Last name: " << person.lastName << std::endl; 15 | os << "Age: " << person.age << std::endl; 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /doc/distribution.md: -------------------------------------------------------------------------------- 1 | Reporting distribution 2 | ====================== 3 | To have the confidence that your properties are testing the right thing, it can be important to know how the input data is distributed. RapidCheck provides functionality to collect this information and report it to the console. 4 | 5 | To do this, RapidCheck uses the concept of test case "tags". Tags are a sequence of strings associated with a test case and every test case with the same tags are grouped together. When RapidCheck has run all its tests, it prints the distribution of the different categories. To add tags to a test case, use the tagging macros described below. 6 | 7 | ## Tagging macros ## 8 | 9 | ### `RC_TAG(values...)` ### 10 | Tags the current test case with the given values. The values are converted to strings using `rc::toString`. 11 | 12 | Example: 13 | ```C++ 14 | rc::check([](const User &user) { 15 | RC_TAG(user.gender); 16 | }); 17 | ``` 18 | 19 | When run: 20 | ``` 21 | OK, passed 100 tests 22 | 54.00% - Female 23 | 46.00% - Male 24 | ``` 25 | 26 | ### `RC_CLASSIFY(condition, values...)` ### 27 | If `condition` is true, tags the current test case with the given values in the same manner as `RC_TAG`. If no tags are given, a stringified version of the condition will be used as the tag. 28 | 29 | Example: 30 | ```C++ 31 | rc::check([](const User &user) { 32 | RC_CLASSIFY(user.username.empty()); 33 | }); 34 | ``` 35 | 36 | When run: 37 | ``` 38 | OK, passed 100 tests 39 | 8.00% - user.username.empty() 40 | ``` 41 | 42 | ## Notes ## 43 | Tags are not treated as sets meaning that there can be duplicate tags and the order is significant. The following are not equivalent: 44 | ```C++ 45 | RC_TAG("foo", "bar"); // <- This... 46 | RC_TAG("bar", "foo"); // <- is not the same as this 47 | ``` 48 | 49 | Also, you can use multiple tagging macros, you do not have to add all tags in a single macro. The following are equivalent: 50 | ```C++ 51 | // This: 52 | RC_TAG("foo"); 53 | RC_TAG("bar"); 54 | 55 | // ...is equivalent to: 56 | RC_TAG("foo", "bar"); 57 | ``` 58 | -------------------------------------------------------------------------------- /doc/gmock.md: -------------------------------------------------------------------------------- 1 | Google Mock integration 2 | ======================= 3 | RapidCheck support integration with Google Mock by making it possible for mock failures to make the current RapidCheck test case fail. For RapidCheck, you can most likely not use the `throw_on_failure` flag. This flag causes throws from mock destructors, something which is likely to make the program crash. In traditional unit testing, this might be okay but this prevents RapidCheck from shrinking the test case once it has failed. 4 | 5 | ## Usage ## 6 | Google Mock integration is not part of RapidCheck core so you need to add `extras/gmock/include` to your include path. You then need to include the `rapidcheck/gmock.h` header and call `rc::gmock::RapidCheckListener::install()` in your main file after Google Mock initialization. 7 | 8 | **Example:** 9 | 10 | ```C++ 11 | #include 12 | 13 | // Here is where Google Mock integration lives 14 | #include 15 | 16 | int main(int argc, char **argv) { 17 | ::testing::InitGoogleMock(&argc, argv); 18 | 19 | // This installs the RapidCheck listener. 20 | rc::gmock::RapidCheckListener::install(); 21 | 22 | return RUN_ALL_TESTS(); 23 | } 24 | ``` 25 | 26 | This will install a Google Test test event listener which will translate mock failures in to RapidCheck failures. 27 | 28 | ## Caveats ## 29 | RapidCheck replaces the default test listener with its own and wraps it so that the original listener gets the callbacks as usual when not in a property. However, even when in a property, there is no way for RapidCheck to prevent mock failures and Google Test assertion failures from being reported as errors to Google Test. This is usually not a problem since a single mock failure or assertion failure in a property usually means an actual failure. However, if you see weird behavior when you use Google Test together with RapidCheck, this might be the cause. 30 | -------------------------------------------------------------------------------- /doc/user_guide.md: -------------------------------------------------------------------------------- 1 | RapidCheck User Guide 2 | ===================== 3 | The following document is a work in progress and some parts are still missing. There is more documentation to read in the form of source code comments if something is missing here. 4 | 5 | **Also, please note that the API has not stabilized yet and may change at any time.** 6 | 7 | ## Basics ## 8 | - [Properties](properties.md) 9 | - [Generators](generators.md) 10 | - [Displaying values](displaying.md) 11 | - [Assertions](assertions.md) 12 | - [Reporting distribution](distribution.md) 13 | - [Configuration](configuration.md) 14 | - [Debugging failures](debugging.md) 15 | - [Stateful testing](state.md) 16 | 17 | ## Extras ## 18 | - [Google Test integration](gtest.md) 19 | - [Google Mock integration](gmock.md) 20 | - [Boost support](boost.md) 21 | - [Boost Test integration](boost_test.md) 22 | 23 | ## Reference ## 24 | - [Generators](generators_ref.md) 25 | - [Stateful testing](state_ref.md) 26 | 27 | ## Advanced topics ## 28 | - [Gen](Gen.md) 29 | - [Shrinkable](Shrinkable.md) 30 | - [Seq](Seq.md) 31 | -------------------------------------------------------------------------------- /encode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // TODO(siedentop): One-Definition-Rule... Move into a '.cc' file. 9 | 10 | // Taken from here: https://hypothesis.readthedocs.io/en/latest/quickstart.html 11 | std::string decode(const std::vector> &lst) { 12 | std::stringstream ss; 13 | 14 | for (const auto &elem : lst) { 15 | ss << std::string(elem.first, elem.second); 16 | } 17 | 18 | return ss.str(); 19 | } 20 | 21 | std::vector> encode(const std::string &input_string) { 22 | int count = 0; 23 | char prev = '\0'; 24 | std::vector> lst = {}; 25 | 26 | // Hard to find bug. If prefix of input_string is 'magic', return encoding of 27 | // 'magic'. This will drop any chars after the prefix 28 | std::string prefix("ma"); 29 | if (input_string.compare(0, prefix.length(), prefix) == 0) { 30 | return {{1, 'm'}, {1, 'a'}, {1, 'g'}, {1, 'i'}, {1, 'c'}}; 31 | } 32 | 33 | for (const auto &character : input_string) { 34 | if (character != prev) { 35 | std::pair entry(count, prev); 36 | lst.push_back(entry); 37 | count = 1; 38 | prev = character; 39 | } else { 40 | count++; 41 | } 42 | } 43 | // Push final result back 44 | std::pair entry(count, input_string.back()); 45 | lst.push_back(entry); 46 | return lst; 47 | } 48 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(counter) 2 | add_subdirectory(mapparser) 3 | add_subdirectory(database) 4 | add_subdirectory(classify) 5 | 6 | if (RC_ENABLE_GTEST) 7 | add_subdirectory(gtest) 8 | endif() 9 | 10 | if (RC_ENABLE_BOOST_TEST) 11 | add_subdirectory(boost_test) 12 | endif() 13 | -------------------------------------------------------------------------------- /examples/boost_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(boost_test_integration main.cpp) 2 | target_link_libraries(boost_test_integration rapidcheck_boost_test boost) 3 | if (MSVC) 4 | target_compile_options(boost_test_integration PRIVATE "/EHa") 5 | else() 6 | # Boost uses auto_ptr which causes warnings on (at least) GCC 7 | target_compile_options(boost_test_integration PRIVATE "-Wno-deprecated-declarations") 8 | endif() 9 | -------------------------------------------------------------------------------- /examples/boost_test/main.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE main 2 | #include 3 | #include 4 | 5 | BOOST_AUTO_TEST_SUITE(RapidCheckExample) 6 | 7 | // Should succeed: 8 | RC_BOOST_PROP(copyOfStringIsIdenticalToOriginal, (const std::string &str)) { 9 | RC_CLASSIFY(str.empty()); 10 | const auto strCopy = str; 11 | RC_ASSERT(strCopy == str); 12 | } 13 | 14 | // Should obviously fail: 15 | RC_BOOST_PROP(dividingByTenMakesAllNumbersEqual, (int a, int b)) { 16 | RC_ASSERT((a / 10) == (b / 10)); 17 | } 18 | 19 | // If you don't have any arguments, you have to have an empty paren: 20 | RC_BOOST_PROP(inRangeValueIsInRange, ()) { 21 | const auto range = *rc::gen::arbitrary>(); 22 | const auto x = *rc::gen::inRange(range.first, range.second); 23 | RC_ASSERT(x >= range.first); 24 | RC_ASSERT(x < range.second); 25 | } 26 | 27 | // You can also create fixtures... 28 | class MyFixture { 29 | protected: 30 | MyFixture() 31 | : counter(0) {} 32 | 33 | void increment() { counter++; } 34 | 35 | std::size_t counter; 36 | }; 37 | 38 | // ...and use them like this: 39 | RC_BOOST_FIXTURE_PROP(shouldInstantiateFixtureOnEachRun, 40 | MyFixture, 41 | (const std::vector &ints)) { 42 | for (std::size_t i = 0; i < ints.size(); i++) { 43 | increment(); 44 | } 45 | 46 | RC_ASSERT(counter == ints.size()); 47 | } 48 | 49 | // A normal Boost test can use the same fixture: 50 | BOOST_FIXTURE_TEST_CASE(incrementIncrementsByOne, MyFixture) { 51 | BOOST_REQUIRE_EQUAL(0U, counter); 52 | increment(); 53 | BOOST_REQUIRE_EQUAL(1U, counter); 54 | } 55 | 56 | BOOST_AUTO_TEST_SUITE_END() 57 | -------------------------------------------------------------------------------- /examples/classify/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(classify main.cpp) 2 | target_link_libraries(classify rapidcheck) -------------------------------------------------------------------------------- /examples/classify/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace rc; 4 | 5 | enum class Gender { Male, Female }; 6 | 7 | std::ostream &operator<<(std::ostream &os, Gender gender) { 8 | os << ((gender == Gender::Male) ? "Male" : "Female"); 9 | return os; 10 | } 11 | 12 | struct User { 13 | std::string username; 14 | Gender gender; 15 | }; 16 | 17 | namespace rc { 18 | 19 | template <> 20 | struct Arbitrary { 21 | static Gen arbitrary() { 22 | return gen::build( 23 | gen::set(&User::username), 24 | gen::set(&User::gender, gen::element(Gender::Male, Gender::Female))); 25 | } 26 | }; 27 | 28 | } // namespace rc 29 | 30 | int main() { 31 | rc::check("RC_TAG", [](const User &user) { RC_TAG(user.gender); }, nullptr, 0); 32 | 33 | rc::check("RC_CLASSIFY", 34 | [](const User &user) { RC_CLASSIFY(user.username.empty()); }, nullptr, 0); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /examples/counter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(counter main.cpp) 2 | 3 | target_compile_options(counter PRIVATE -fsanitize=fuzzer) 4 | target_link_libraries(counter 5 | PRIVATE -fsanitize=fuzzer 6 | ) 7 | 8 | target_link_libraries(counter PUBLIC rapidcheck) 9 | #target_include_directories(counter PRIVATE ../../ext/catch/include) 10 | -------------------------------------------------------------------------------- /examples/counter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace rc; 5 | 6 | class Counter { 7 | public: 8 | void inc() { m_value++; } 9 | 10 | void dec() { 11 | assert(m_value > 0); 12 | // Broken! 13 | if (m_value != 10) { 14 | m_value--; 15 | } 16 | } 17 | 18 | int get() { return m_value; } 19 | 20 | private: 21 | int m_value = 0; 22 | }; 23 | 24 | struct CounterModel { 25 | int value = 0; 26 | }; 27 | 28 | struct Inc : public state::Command { 29 | void apply(CounterModel &s0) const override { s0.value++; } 30 | 31 | void run(const CounterModel &s0, Counter &counter) const override { 32 | counter.inc(); 33 | assert(counter.get() == (s0.value + 1)); 34 | } 35 | }; 36 | 37 | struct Dec : public state::Command { 38 | void checkPreconditions(const CounterModel &s0) const override { 39 | RC_PRE(s0.value > 0); 40 | assert(s0.value > 0); 41 | } 42 | 43 | void apply(CounterModel &s0) const override { s0.value--; } 44 | 45 | void run(const CounterModel &state, Counter &counter) const override { 46 | counter.dec(); 47 | if (counter.get() != (state.value - 1)) { 48 | std::cout << "Failing: " << counter.get() << ", " << state.value -1 << std::endl; } 49 | assert(counter.get() == (state.value - 1)); 50 | } 51 | }; 52 | 53 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 54 | //int main() { 55 | // const uint8_t* Data = nullptr; 56 | // size_t Size = 0; 57 | 58 | 59 | check([] { 60 | CounterModel state; 61 | Counter sut; 62 | state::check(state, sut, state::gen::execOneOfWithArgs()); 63 | }, Data, Size); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /examples/database/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(database main.cpp 2 | Database.cpp 3 | DatabaseConnection.cpp 4 | User.cpp) 5 | 6 | target_link_libraries(database rapidcheck) -------------------------------------------------------------------------------- /examples/database/Database.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "User.h" 9 | 10 | class IDatabaseConnection; 11 | 12 | class Database { 13 | public: 14 | explicit Database(std::unique_ptr connection); 15 | 16 | void open(); 17 | void close(); 18 | void beginWrite(); 19 | void executeWrite(); 20 | void put(User user); 21 | bool get(const std::string &username, User &user); 22 | 23 | private: 24 | bool m_open; 25 | std::unique_ptr m_connection; 26 | std::map m_cache; 27 | bool m_hasBlock; 28 | std::vector m_queue; 29 | std::size_t m_hash; 30 | }; 31 | -------------------------------------------------------------------------------- /examples/database/DatabaseConnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct Message { 8 | Message() = default; 9 | Message(std::string t, 10 | std::vector p = std::vector()); 11 | 12 | std::string type; 13 | std::vector params; 14 | }; 15 | 16 | class IDatabaseConnection { 17 | public: 18 | virtual Message sendMessage(const Message &msg) = 0; 19 | virtual ~IDatabaseConnection() = default; 20 | }; 21 | 22 | std::unique_ptr connectToDatabase(const std::string &id); 23 | -------------------------------------------------------------------------------- /examples/database/Generators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "User.h" 4 | 5 | rc::Gen genFirstName() { 6 | return rc::gen::weightedOneOf( 7 | {{4, rc::gen::arbitrary()}, 8 | {8, rc::gen::element("John", "Chuck", "Kevin", "Oscar")}, 9 | {8, 10 | rc::gen::element("Jane", "Sarah", "Kate", "Elizabeth")}}); 11 | } 12 | 13 | rc::Gen genLastName() { 14 | return rc::gen::weightedOneOf({ 15 | {4, rc::gen::arbitrary()}, 16 | {16, rc::gen::element("Johnson", "Smith", "Cook", "Jobs")}, 17 | }); 18 | } 19 | 20 | rc::Gen genTown() { 21 | return rc::gen::weightedOneOf({ 22 | {4, rc::gen::arbitrary()}, 23 | {16, 24 | rc::gen::element( 25 | "Stockholm", "New York", "San Francisco", "Gothenburg")}, 26 | }); 27 | } 28 | 29 | namespace rc { 30 | 31 | template <> 32 | struct Arbitrary { 33 | static Gen arbitrary() { 34 | return gen::build( 35 | gen::set(&User::username, gen::nonEmpty()), 36 | gen::set(&User::firstName, genFirstName()), 37 | gen::set(&User::lastName, genLastName()), 38 | gen::set(&User::age, gen::inRange(0, 100)), 39 | gen::set(&User::gender, gen::element(Gender::Male, Gender::Female)), 40 | gen::set(&User::hometown, genTown())); 41 | } 42 | }; 43 | 44 | } // namespace rc 45 | -------------------------------------------------------------------------------- /examples/database/User.cpp: -------------------------------------------------------------------------------- 1 | #include "User.h" 2 | 3 | #include 4 | 5 | bool operator==(const User &lhs, const User &rhs) { 6 | return (lhs.username == rhs.username) && (lhs.firstName == rhs.firstName) && 7 | (lhs.lastName == rhs.lastName) && (lhs.age == rhs.age) && 8 | (lhs.gender == rhs.gender) && (lhs.hometown == rhs.hometown); 9 | } 10 | 11 | bool operator!=(const User &lhs, const User &rhs) { 12 | return !(lhs == rhs); 13 | } 14 | 15 | bool operator!=(const User &lhs, const User &rhs); 16 | 17 | std::ostream &operator<<(std::ostream &os, const Gender &gender) { 18 | os << ((gender == Gender::Male) ? "male" : "female"); 19 | return os; 20 | } 21 | 22 | std::ostream &operator<<(std::ostream &os, const User &user) { 23 | os << "{username='" << user.username << "', firstName='" << user.firstName 24 | << "', lastName='" << user.lastName << "', age=" << user.age 25 | << ", gender=" << user.gender << ", hometown='" << user.hometown << "'}"; 26 | return os; 27 | } 28 | 29 | void serialize(const User &user, std::vector &out) { 30 | out.push_back(user.username); 31 | out.push_back(user.firstName); 32 | out.push_back(user.lastName); 33 | out.push_back(std::to_string(user.age)); 34 | out.push_back((user.gender == Gender::Male) ? "male" : "female"); 35 | out.push_back(user.hometown); 36 | } 37 | 38 | std::vector::const_iterator 39 | deserialize(std::vector::const_iterator start, 40 | std::vector::const_iterator end, 41 | User &user) { 42 | if ((end - start) < 6) { 43 | return start; 44 | } 45 | 46 | user.username = start[0]; 47 | user.firstName = start[1]; 48 | user.lastName = start[2]; 49 | try { 50 | user.age = std::stoi(start[3]); 51 | } catch (const std::logic_error &) { 52 | return start; 53 | } 54 | user.gender = (start[4] == "male") ? Gender::Male : Gender::Female; 55 | user.hometown = start[5]; 56 | 57 | return start + 6; 58 | } 59 | -------------------------------------------------------------------------------- /examples/database/User.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum class Gender { Male, Female }; 8 | 9 | struct User { 10 | std::string username; 11 | std::string firstName; 12 | std::string lastName; 13 | int age; 14 | Gender gender; 15 | std::string hometown; 16 | }; 17 | 18 | bool operator==(const User &lhs, const User &rhs); 19 | bool operator!=(const User &lhs, const User &rhs); 20 | std::ostream &operator<<(std::ostream &os, const Gender &gender); 21 | std::ostream &operator<<(std::ostream &os, const User &user); 22 | 23 | void serialize(const User &user, std::vector &out); 24 | 25 | std::vector::const_iterator 26 | deserialize(std::vector::const_iterator start, 27 | std::vector::const_iterator end, 28 | User &user); 29 | 30 | namespace std { 31 | 32 | template <> 33 | struct hash { 34 | typedef User argument_type; 35 | typedef std::size_t result_type; 36 | 37 | result_type operator()(const User &user) const { 38 | std::size_t h = 0; 39 | // Yeah I know, this is broken but it's just an example 40 | h ^= std::hash()(user.username); 41 | h ^= std::hash()(user.firstName); 42 | h ^= std::hash()(user.lastName); 43 | h ^= std::hash()(user.age); 44 | h ^= std::hash()((user.gender == Gender::Male) ? true : false); 45 | h ^= std::hash()(user.hometown); 46 | 47 | return h; 48 | } 49 | }; 50 | 51 | } // namespace std 52 | -------------------------------------------------------------------------------- /examples/gtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(gtest_integration main.cpp) 2 | target_link_libraries(gtest_integration rapidcheck_gtest gtest) 3 | target_include_directories(gtest_integration PRIVATE 4 | ${CMAKE_SOURCE_DIR}/ext/gmock/gtest/include) 5 | -------------------------------------------------------------------------------- /examples/gtest/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Should succeed: 5 | RC_GTEST_PROP(MyTestCase, 6 | copyOfStringIsIdenticalToOriginal, 7 | (const std::string &str)) { 8 | RC_CLASSIFY(str.empty()); 9 | const auto strCopy = str; 10 | RC_ASSERT(strCopy == str); 11 | } 12 | 13 | // Should obviously fail: 14 | RC_GTEST_PROP(MyTestCase, dividingByTenMakesAllNumbersEqual, (int a, int b)) { 15 | RC_ASSERT((a / 10) == (b / 10)); 16 | } 17 | 18 | // If you don't have any arguments, you have to have an empty paren: 19 | RC_GTEST_PROP(MyTestCase, inRangeValueIsInRange, ()) { 20 | const auto range = *rc::gen::arbitrary>(); 21 | const auto x = *rc::gen::inRange(range.first, range.second); 22 | RC_ASSERT(x >= range.first); 23 | RC_ASSERT(x < range.second); 24 | } 25 | 26 | // You can also create fixtures... 27 | class MyFixture : public ::testing::Test { 28 | protected: 29 | MyFixture() 30 | : counter(0) {} 31 | 32 | void SetUp() override { 33 | // SetUp works as usual... 34 | } 35 | 36 | void increment() { counter++; } 37 | 38 | void TearDown() override { 39 | // ...as does TearDown 40 | } 41 | 42 | std::size_t counter; 43 | }; 44 | 45 | // ...and use them like this: 46 | RC_GTEST_FIXTURE_PROP(MyFixture, 47 | shouldInstantiateFixtureOnEachRun, 48 | (const std::vector &ints)) { 49 | for (std::size_t i = 0; i < ints.size(); i++) { 50 | increment(); 51 | } 52 | 53 | RC_ASSERT(counter == ints.size()); 54 | } 55 | 56 | // A normal Google test can use the same fixture: 57 | TEST_F(MyFixture, incrementIncrementsByOne) { 58 | ASSERT_EQ(0U, counter); 59 | increment(); 60 | ASSERT_EQ(1U, counter); 61 | } 62 | 63 | int main(int argc, char **argv) { 64 | ::testing::InitGoogleTest(&argc, argv); 65 | return RUN_ALL_TESTS(); 66 | } 67 | -------------------------------------------------------------------------------- /examples/mapparser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(mapparser main.cpp MapParser.cpp) 2 | 3 | target_compile_options(mapparser PRIVATE -fsanitize=fuzzer,address) 4 | target_link_libraries(mapparser -fsanitize=fuzzer,address) 5 | 6 | target_link_libraries(mapparser rapidcheck) 7 | -------------------------------------------------------------------------------- /examples/mapparser/MapParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class ParseException : public std::exception { 8 | public: 9 | ParseException(std::string::size_type pos, const std::string &msg); 10 | std::string::size_type position() const; 11 | std::string message() const; 12 | const char *what() const noexcept override; 13 | 14 | private: 15 | std::string::size_type m_pos; 16 | std::string m_msg; 17 | std::string m_what; 18 | }; 19 | 20 | std::map parseMap(const std::string &str); 21 | 22 | std::string mapToString(const std::map &map, 23 | bool doubleQuote = false); 24 | -------------------------------------------------------------------------------- /examples/mapparser/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "MapParser.h" 4 | 5 | using namespace rc; 6 | 7 | //int main() { 8 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 9 | rc::check("serializing and then parsing should yield original map", 10 | [](const std::map &map) { 11 | 12 | // RC_PRE: handle by inverting condition and returning if true. 13 | if(map.find("") != map.end()) return true; 14 | assert(parseMap(mapToString(map)) == map); 15 | return true; 16 | }, Data, Size); 17 | return 0; 18 | } 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | //extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); 26 | __attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv); 27 | int main(int argc, char **argv) { 28 | fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); 29 | // if (LLVMFuzzerInitialize) 30 | // LLVMFuzzerInitialize(&argc, &argv); 31 | for (int i = 1; i < argc; i++) { 32 | fprintf(stderr, "Running: %s\n", argv[i]); 33 | FILE *f = fopen(argv[i], "r"); 34 | assert(f); 35 | fseek(f, 0, SEEK_END); 36 | size_t len = ftell(f); 37 | fseek(f, 0, SEEK_SET); 38 | unsigned char *buf = (unsigned char*)malloc(len); 39 | size_t n_read = fread(buf, 1, len, f); 40 | assert(n_read == len); 41 | LLVMFuzzerTestOneInput(buf, len); 42 | free(buf); 43 | fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (RC_ENABLE_TESTS OR RC_ENABLE_CATCH) 2 | add_library(catch INTERFACE) 3 | target_include_directories(catch INTERFACE catch/include) 4 | endif() 5 | 6 | if ((RC_ENABLE_GMOCK OR RC_ENABLE_GTEST) AND RC_ENABLE_TESTS) 7 | # On Windows, gmock/gtest defaults to static CRT which is not compatible 8 | # with the way RapidCheck is currently built 9 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 10 | add_subdirectory(googletest) 11 | endif() 12 | 13 | if (RC_ENABLE_BOOST AND RC_ENABLE_TESTS) 14 | if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/boost") 15 | execute_process( 16 | COMMAND "sh" "get_boost.sh" 17 | WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") 18 | endif() 19 | add_library(boost INTERFACE) 20 | target_include_directories(boost INTERFACE boost) 21 | endif() 22 | -------------------------------------------------------------------------------- /ext/get_boost.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -s -L -o boost_1_59_0.tar.bz2 "http://downloads.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.59.0%2Fboost_1_59_0.tar.bz2%2Fdownload&ts=1441133751&use_mirror=skylink" 3 | tar xjfp boost_1_59_0.tar.bz2 4 | mv boost_1_59_0 boost 5 | rm boost_1_59_0.tar.bz2 6 | -------------------------------------------------------------------------------- /extras/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(RC_ENABLE_CATCH "Build Catch.hpp support" OFF) 2 | if (RC_ENABLE_CATCH OR RC_ENABLE_TESTS) 3 | add_subdirectory(catch) 4 | endif() 5 | 6 | option(RC_ENABLE_GMOCK "Build Google Mock integration" OFF) 7 | if (RC_ENABLE_GMOCK) 8 | add_subdirectory(gmock) 9 | endif() 10 | 11 | option(RC_ENABLE_GTEST "Build Google Test integration" OFF) 12 | if (RC_ENABLE_GTEST) 13 | add_subdirectory(gtest) 14 | endif() 15 | 16 | option(RC_ENABLE_BOOST "Build Boost support" OFF) 17 | if (RC_ENABLE_BOOST) 18 | add_subdirectory(boost) 19 | endif() 20 | 21 | option(RC_ENABLE_BOOST_TEST "Build Boost Test support" OFF) 22 | if (RC_ENABLE_BOOST_TEST) 23 | add_subdirectory(boost_test) 24 | endif() 25 | -------------------------------------------------------------------------------- /extras/boost/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(rapidcheck_boost INTERFACE) 2 | target_link_libraries(rapidcheck_boost INTERFACE rapidcheck) 3 | target_include_directories(rapidcheck_boost INTERFACE include) 4 | 5 | if (RC_ENABLE_TESTS) 6 | add_subdirectory(test) 7 | endif() -------------------------------------------------------------------------------- /extras/boost/include/rapidcheck/boost.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "rapidcheck/gen/boost/Optional.h" 6 | -------------------------------------------------------------------------------- /extras/boost/include/rapidcheck/gen/boost/Optional.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rc { 6 | namespace gen { 7 | namespace boost { 8 | 9 | /// Generates a `boost::optional` of values generated by the given generator. 10 | template 11 | Gen<::boost::optional> optional(Gen gen); 12 | 13 | } // namespace boost 14 | } // namespace gen 15 | } // namespace rc 16 | 17 | #include "Optional.hpp" 18 | -------------------------------------------------------------------------------- /extras/boost/include/rapidcheck/gen/boost/Optional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rc { 4 | namespace gen { 5 | namespace boost { 6 | 7 | template 8 | Gen<::boost::optional> optional(Gen gen) { 9 | return gen::map>(gen::maybe(std::move(gen)), 10 | [](Maybe &&m) { 11 | return m ? ::boost::optional(std::move(*m)) 12 | : ::boost::optional(); 13 | }); 14 | } 15 | 16 | } // namespace boost 17 | } // namespace gen 18 | 19 | template 20 | struct Arbitrary> { 21 | static Gen> arbitrary() { 22 | return gen::boost::optional(gen::arbitrary()); 23 | } 24 | }; 25 | 26 | template 27 | void showValue(const boost::optional &x, std::ostream &os) { 28 | if (x) { 29 | show(*x, os); 30 | } else { 31 | os << "boost::none"; 32 | } 33 | } 34 | 35 | } // namespace rc 36 | -------------------------------------------------------------------------------- /extras/boost/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(rapidcheck_boost_tests 2 | main.cpp 3 | OptionalTests.cpp 4 | ) 5 | 6 | target_link_libraries(rapidcheck_boost_tests 7 | boost 8 | rapidcheck_boost 9 | rapidcheck_catch 10 | rapidcheck_test_utils 11 | catch) 12 | 13 | add_test( 14 | NAME rapidcheck_boost_tests 15 | COMMAND rapidcheck_boost_tests) -------------------------------------------------------------------------------- /extras/boost/test/OptionalTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "util/ShrinkableUtils.h" 6 | #include "util/GenUtils.h" 7 | #include "util/Predictable.h" 8 | #include "util/Box.h" 9 | 10 | using namespace rc; 11 | using namespace rc::test; 12 | 13 | TEST_CASE("gen::boost::optional") { 14 | prop("equivalent to gen::maybe", 15 | [](const GenParams ¶ms) { 16 | const auto gen = gen::arbitrary(); 17 | const auto maybeShrinkable = 18 | gen::maybe(gen)(params.random, params.size); 19 | const auto optionalShrinkable = shrinkable::map( 20 | gen::boost::optional(gen)(params.random, params.size), 21 | [](const boost::optional &x) -> Maybe { 22 | if (x) { 23 | return std::move(*x); 24 | } else { 25 | return Nothing; 26 | } 27 | }); 28 | 29 | assertEquivalent(maybeShrinkable, optionalShrinkable); 30 | }); 31 | 32 | prop("chooses correct Arbitrary instance", 33 | [](const GenParams ¶ms) { 34 | const auto gen = gen::arbitrary>(); 35 | const auto value = gen(params.random, params.size).value(); 36 | RC_PRE(value); 37 | RC_ASSERT(isArbitraryPredictable(*value)); 38 | }); 39 | } 40 | 41 | TEST_CASE("showValue(boost::optional)") { 42 | prop("shows present value using rc::show", 43 | [](const Box &x) { 44 | std::ostringstream actual; 45 | showValue(boost::make_optional(x), actual); 46 | std::ostringstream expected; 47 | showValue(x, expected); 48 | RC_ASSERT(actual.str() == expected.str()); 49 | }); 50 | 51 | SECTION("shows non-present value as 'boost::none'") { 52 | std::ostringstream os; 53 | showValue(boost::optional(), os); 54 | REQUIRE(os.str() == "boost::none"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /extras/boost/test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #if (__GLIBCXX__ / 10000) == 2014 3 | 4 | namespace std { 5 | 6 | inline bool uncaught_exception() noexcept(true) { 7 | return current_exception() != nullptr; 8 | } 9 | 10 | } // namespace std 11 | 12 | #endif 13 | 14 | #define CATCH_CONFIG_MAIN 15 | #include 16 | -------------------------------------------------------------------------------- /extras/boost_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(rapidcheck_boost_test INTERFACE) 2 | target_link_libraries(rapidcheck_boost_test INTERFACE rapidcheck) 3 | target_include_directories(rapidcheck_boost_test INTERFACE include) 4 | -------------------------------------------------------------------------------- /extras/catch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(rapidcheck_catch INTERFACE) 2 | target_link_libraries(rapidcheck_catch INTERFACE rapidcheck) 3 | target_include_directories(rapidcheck_catch INTERFACE include) -------------------------------------------------------------------------------- /extras/catch/include/rapidcheck/catch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace rc { 9 | 10 | /// For use with `catch.hpp`. Use this function wherever you would use a 11 | /// `SECTION` for convenient checking of properties. 12 | /// 13 | /// @param description A description of the property. 14 | /// @param testable The object that implements the property. 15 | template 16 | void prop(const std::string &description, Testable &&testable) { 17 | using namespace detail; 18 | 19 | #ifdef CATCH_CONFIG_PREFIX_ALL 20 | CATCH_SECTION(description) { 21 | #else 22 | SECTION(description) { 23 | #endif 24 | 25 | const auto result = checkTestable(std::forward(testable)); 26 | 27 | if (result.template is()) { 28 | const auto success = result.template get(); 29 | if (!success.distribution.empty()) { 30 | std::cout << "- " << description << std::endl; 31 | printResultMessage(result, std::cout); 32 | std::cout << std::endl; 33 | } 34 | } else { 35 | std::ostringstream ss; 36 | printResultMessage(result, ss); 37 | #ifdef CATCH_CONFIG_PREFIX_ALL 38 | CATCH_INFO(ss.str() << "\n"); 39 | CATCH_FAIL(); 40 | #else 41 | INFO(ss.str() << "\n"); 42 | FAIL(); 43 | #endif 44 | } 45 | } 46 | } 47 | 48 | } // namespace rc 49 | -------------------------------------------------------------------------------- /extras/gmock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(rapidcheck_gmock INTERFACE) 2 | target_link_libraries(rapidcheck_gmock INTERFACE rapidcheck) 3 | target_include_directories(rapidcheck_gmock INTERFACE include) 4 | 5 | if (RC_ENABLE_TESTS) 6 | add_subdirectory(test) 7 | endif() -------------------------------------------------------------------------------- /extras/gmock/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(rapidcheck_gmock_tests main.cpp) 2 | target_link_libraries(rapidcheck_gmock_tests 3 | rapidcheck_gmock 4 | rapidcheck_catch 5 | catch 6 | gmock) 7 | target_include_directories(rapidcheck_gmock_tests PRIVATE 8 | ${CMAKE_SOURCE_DIR}/ext/googletest/googletest/include 9 | ${CMAKE_SOURCE_DIR}/ext/googletest/googlemock/include) 10 | 11 | # Prevent collision between Gmock and Catch 12 | target_compile_definitions(rapidcheck_gmock_tests PRIVATE 13 | CATCH_CONFIG_PREFIX_ALL) 14 | 15 | add_test( 16 | NAME rapidcheck_gmock_tests 17 | COMMAND rapidcheck_gmock_tests) 18 | -------------------------------------------------------------------------------- /extras/gtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(rapidcheck_gtest INTERFACE) 2 | target_link_libraries(rapidcheck_gtest INTERFACE rapidcheck) 3 | target_include_directories(rapidcheck_gtest INTERFACE include) 4 | -------------------------------------------------------------------------------- /fuzz_encoding.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "encode.h" 8 | 9 | template 10 | void print(const T v, std::ostream &o) { 11 | for (const auto &x : v) { 12 | o << ", " << x; 13 | } 14 | o << std::endl; 15 | } 16 | 17 | template 18 | T sum_good(const std::vector &v) { 19 | return std::accumulate(v.begin(), v.end(), static_cast(0)); 20 | } 21 | 22 | template 23 | T sum_bad(const std::vector &v) { 24 | if (v.size() > 3) 25 | if (v[0] == 'm') 26 | if (v[1] == 'a') 27 | if (v[2] == 'g') { 28 | print(v, std::cout); 29 | return 3; 30 | } 31 | 32 | return sum_good(v); 33 | } 34 | 35 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 36 | rc::check("decode_inverts_encode", 37 | [](const std::string &s0) { assert(s0 == decode(encode(s0))); }, 38 | Data, 39 | Size); 40 | 41 | /// This shows that the output on failure that rapidcheck produces is 42 | /// completely incorrect on failure. Most certainly related to my changes. 43 | // rc::check( 44 | // "Output on Failure is Bogus", 45 | // [](const std::vector &v) { assert(sum_good(v) == sum_bad(v)); }, 46 | // Data, 47 | // Size); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /include/rapidcheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // MSVC HACK - Undefine stupid macros MS macros 4 | #undef min 5 | #undef max 6 | 7 | #include "rapidcheck/Seq.h" 8 | #include "rapidcheck/seq/Create.h" 9 | #include "rapidcheck/seq/Operations.h" 10 | #include "rapidcheck/seq/SeqIterator.h" 11 | #include "rapidcheck/seq/Transform.h" 12 | 13 | #include "rapidcheck/Shrinkable.h" 14 | #include "rapidcheck/shrinkable/Create.h" 15 | #include "rapidcheck/shrinkable/Operations.h" 16 | #include "rapidcheck/shrinkable/Transform.h" 17 | 18 | #include "rapidcheck/Gen.h" 19 | #include "rapidcheck/gen/Arbitrary.h" 20 | #include "rapidcheck/gen/Build.h" 21 | #include "rapidcheck/gen/Chrono.h" 22 | #include "rapidcheck/gen/Container.h" 23 | #include "rapidcheck/gen/Create.h" 24 | #include "rapidcheck/gen/Exec.h" 25 | #include "rapidcheck/gen/Maybe.h" 26 | #include "rapidcheck/gen/Numeric.h" 27 | #include "rapidcheck/gen/Predicate.h" 28 | #include "rapidcheck/gen/Select.h" 29 | #include "rapidcheck/gen/Text.h" 30 | #include "rapidcheck/gen/Transform.h" 31 | #include "rapidcheck/gen/Tuple.h" 32 | 33 | #include "rapidcheck/Assertions.h" 34 | #include "rapidcheck/Check.h" 35 | #include "rapidcheck/Classify.h" 36 | #include "rapidcheck/Log.h" 37 | #include "rapidcheck/Show.h" 38 | -------------------------------------------------------------------------------- /include/rapidcheck/Assertions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rc { 4 | namespace detail { 5 | 6 | std::string makeMessage(const std::string &file, 7 | int line, 8 | const std::string &assertion, 9 | const std::string &extra = ""); 10 | 11 | std::string makeExpressionMessage(const std::string &file, 12 | int line, 13 | const std::string &assertion, 14 | const std::string &expansion); 15 | 16 | std::string makeUnthrownExceptionMessage(const std::string &file, 17 | int line, 18 | const std::string &assertion); 19 | 20 | std::string makeWrongExceptionMessage(const std::string &file, 21 | int line, 22 | const std::string &assertion, 23 | const std::string &expected); 24 | 25 | template 26 | void doAssert(const Expression &expression, 27 | bool expectedResult, 28 | CaseResult::Type type, 29 | const std::string &file, 30 | int line, 31 | const std::string &assertion) { 32 | if (static_cast(expression.value()) != expectedResult) { 33 | std::ostringstream ss; 34 | expression.show(ss); 35 | throw CaseResult(type, 36 | makeExpressionMessage(file, line, assertion, ss.str())); 37 | } 38 | } 39 | 40 | } // namespace detail 41 | } // namespace rc 42 | -------------------------------------------------------------------------------- /include/rapidcheck/BeforeMinimalTestCase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rc { 4 | 5 | /// This is called before the final minimal test case is run when a property 6 | /// fails. Set a breakpoint here if you want to debug that test case. When the 7 | /// debugger breaks here, you can set up any further breakpoints or other tools 8 | /// before the test case is actually run. 9 | void beforeMinimalTestCase(); 10 | 11 | } // namespace rc 12 | -------------------------------------------------------------------------------- /include/rapidcheck/Check.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rc { 6 | 7 | /// Checks the given testable and returns `true` on success and `false` on 8 | /// failure. This method will also print information about the testing to 9 | /// stderr. 10 | template 11 | bool check(Testable &&testable, const uint8_t *Data, size_t Size); 12 | 13 | /// Same as `check(Testable &&)` but also takes a description of the property 14 | /// that is being tested as the first parameter. This will be used in the 15 | /// output. 16 | template 17 | bool check(const std::string &description, Testable &&testable, const uint8_t *Data, size_t Size); 18 | 19 | } // namespace rc 20 | 21 | #include "Check.hpp" 22 | -------------------------------------------------------------------------------- /include/rapidcheck/Classify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// Tags the current test case if the specified condition is true. If any tags 4 | /// are specified after the condition, those will be used. Otherwise, a string 5 | /// version of the condition itself will be used as the tag. 6 | #define RC_CLASSIFY(condition, ...) \ 7 | do { \ 8 | if (condition) { \ 9 | ::rc::detail::classify(#condition, {__VA_ARGS__}); \ 10 | } \ 11 | } while (false) 12 | 13 | /// Tags the current test case with the given values which will be converted to 14 | /// strings. 15 | #define RC_TAG(...) ::rc::detail::tag({__VA_ARGS__}) 16 | 17 | #include "Classify.hpp" 18 | -------------------------------------------------------------------------------- /include/rapidcheck/Classify.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rc { 6 | namespace detail { 7 | 8 | struct Stringified { 9 | template 10 | Stringified(const T &value) 11 | : str(toString(value)) {} 12 | std::string str; 13 | }; 14 | 15 | void tag(std::initializer_list tags); 16 | void classify(std::string condition, std::initializer_list tags); 17 | 18 | } // namespace detail 19 | } // namespace rc 20 | -------------------------------------------------------------------------------- /include/rapidcheck/GenerationFailure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rc { 6 | 7 | /// Thrown to indicate that an appropriate value couldn't be generated. 8 | class GenerationFailure : public std::runtime_error { 9 | public: 10 | explicit GenerationFailure(std::string msg); 11 | }; 12 | 13 | } // namespace rc 14 | -------------------------------------------------------------------------------- /include/rapidcheck/Log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /// Logs additional information about the run of a test case. Can be used either 6 | /// like a stream (`RC_LOG() << "foobar"`) or taking a string 7 | /// (`RC_LOG("foobar")`). When using the latter form, a newline will be appended 8 | /// automatically. 9 | #define RC_LOG(...) ::rc::detail::log(__VA_ARGS__) 10 | 11 | #include "Log.hpp" 12 | -------------------------------------------------------------------------------- /include/rapidcheck/Log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rc { 4 | namespace detail { 5 | 6 | /// Returns the current logging stream. 7 | std::ostream &log(); 8 | 9 | /// Logs the given message. 10 | void log(const std::string &msg); 11 | 12 | } // namespace detail 13 | } // namespace rc 14 | -------------------------------------------------------------------------------- /include/rapidcheck/Nothing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rc { 4 | 5 | /// Tag struct that can be used to construct different types to an uninitialized 6 | /// or empty state. 7 | struct NothingType { 8 | /// Explicit conversion to false. 9 | explicit operator bool() const { return false; } 10 | }; 11 | 12 | /// Singleton NothingType value. 13 | constexpr NothingType Nothing = NothingType(); 14 | 15 | } // namespace rc 16 | -------------------------------------------------------------------------------- /include/rapidcheck/Random.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rapidcheck/detail/Serialization.h" 4 | 5 | namespace rc { 6 | 7 | template 8 | Iterator serialize(const Random &random, Iterator output) { 9 | using namespace rc::detail; 10 | auto oit = output; 11 | return ++oit; 12 | } 13 | 14 | template 15 | Iterator deserialize(Iterator begin, Iterator end, Random &output) { 16 | using namespace rc::detail; 17 | auto iit = begin; 18 | 19 | Random::Counter counter; 20 | iit = deserializeCompact(iit, end, counter); 21 | // Normally, the block is calculated lazily if counter is divisible by 4 so 22 | // let's simulate this. 23 | if (counter != 0) { 24 | const auto blki = 25 | ((counter - 1) % std::tuple_size::value) + 1; 26 | if (blki != 0) { 27 | // Calculate the block as if counter % 4 == 0 28 | } 29 | } 30 | return ++iit; 31 | } 32 | 33 | } // namespace rc 34 | -------------------------------------------------------------------------------- /include/rapidcheck/Show.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rc { 7 | 8 | /// Outputs a human readable representation of the given value to the given 9 | /// output stream. To do this, it tries the following methods in order until one 10 | /// works: 11 | /// 12 | /// 1. Use a suitable overload of `void showValue(T, std::ostream)´ 13 | /// 2. Use a suitable overload of `std::ostream &operator<<(...)` 14 | /// 3. Output a placeholder value. 15 | template 16 | void show(const T &value, std::ostream &os); 17 | 18 | /// Uses show(...) to convert argument to a string. 19 | template 20 | std::string toString(const T &value); 21 | 22 | /// Helper function for showing collections of values. 23 | /// 24 | /// @param prefix The prefix to the collection, for example "[" 25 | /// @param suffix The suffix to the collection, for example "]" 26 | /// @param collection The collection type. Must support `begin()` and `end()`. 27 | /// @param os The stream to output to. 28 | template 29 | void showCollection(const std::string &prefix, 30 | const std::string &suffix, 31 | const Collection &collection, 32 | std::ostream &os); 33 | 34 | } // namespace rc 35 | 36 | #include "Show.hpp" 37 | -------------------------------------------------------------------------------- /include/rapidcheck/Shrinkable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rapidcheck/Seq.h" 4 | 5 | namespace rc { 6 | 7 | /// A `Shrinkable` describes a value in addition to all the possible ways of 8 | /// shrinking that value. 9 | /// 10 | /// `Shrinkable` is backed by a type erased implementation object which must 11 | /// have the following: 12 | /// - A method `T value() const` which returns the value. 13 | /// - A method `Seq> shrinks() const` which returns a `Seq` of 14 | /// the possible shrinks. If this method throws, it is treated as if it had 15 | /// returned an empty `Seq`. 16 | /// 17 | /// A Shrinkable is immutable and the implementation object is shared when the 18 | /// shrinkable is copied which is why the implementation object needs no copy 19 | /// constructor. 20 | template 21 | class Shrinkable { 22 | template 23 | friend Shrinkable().value())>> 24 | makeShrinkable(Args &&... args); 25 | 26 | public: 27 | /// The type of the value in this `Shrinkable`. 28 | using ValueType = T; 29 | 30 | /// Returns the value. 31 | T value() const; 32 | 33 | /// Returns a `Seq` of all the possible shrinks of this `Shrinkable`. 34 | Seq> shrinks() const noexcept; 35 | 36 | Shrinkable(const Shrinkable &other) noexcept; 37 | Shrinkable(Shrinkable &&other) noexcept; 38 | Shrinkable &operator=(const Shrinkable &other) noexcept; 39 | Shrinkable &operator=(Shrinkable &&other) noexcept; 40 | ~Shrinkable() noexcept; 41 | 42 | private: 43 | Shrinkable() = default; 44 | 45 | class IShrinkableImpl; 46 | 47 | template 48 | class ShrinkableImpl; 49 | 50 | IShrinkableImpl *m_impl = nullptr; 51 | }; 52 | 53 | /// Two `Shrinkable`s are equal if the have the same value and the same shrinks. 54 | template 55 | bool operator==(const Shrinkable &lhs, const Shrinkable &rhs); 56 | 57 | template 58 | bool operator!=(const Shrinkable &lhs, const Shrinkable &rhs); 59 | 60 | } // namespace rc 61 | 62 | #include "Shrinkable.hpp" 63 | -------------------------------------------------------------------------------- /include/rapidcheck/Traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "rapidcheck/detail/IntSequence.h" 6 | 7 | namespace rc { 8 | 9 | /// Convenience wrapper over std::decay, shorter to type. 10 | template 11 | using Decay = typename std::decay::type; 12 | 13 | /// Checks that all the parameters are true. 14 | template 15 | struct AllTrue 16 | : public std::is_same, 17 | detail::IntSequence> {}; 18 | 19 | /// Checks that all the types in `Ts` conforms to type trait `F`. 20 | template