From be1eeaf21e63f406be8b34017329a8bfed54ddb3 Mon Sep 17 00:00:00 2001 From: Stepland <10530295-Buggyroom@users.noreply.gitlab.com> Date: Mon, 11 Apr 2022 01:25:24 +0200 Subject: [PATCH] Tests with rapidcheck ! --- docs/Compiling.md | 10 ++- docs/Setup.md | 11 +-- meson_options.txt | 6 ++ src/better_beats.cpp | 2 +- src/better_note.cpp | 12 +-- src/better_note.hpp | 7 +- src/meson.build | 4 +- src/tests/rapidcheck/generators.hpp | 83 +++++++++++++++++++ src/tests/rapidcheck/main.cpp | 17 ++++ src/tests/rapidcheck/meson.build | 5 +- src/tests/rapidcheck/rapidcheck_main.cpp | 6 -- .../packagefiles/rapidcheck/meson.build | 71 ++++++++++++++++ .../packagefiles/rapidcheck/meson_options.txt | 20 +++++ .../rapidcheck/src/detail/meson.build | 23 +++++ .../rapidcheck/src/gen/detail/meson.build | 6 ++ .../rapidcheck/src/gen/meson.build | 6 ++ .../packagefiles/rapidcheck/src/meson.build | 13 +++ utils/save_meson_wrap.py | 24 ++++++ 18 files changed, 301 insertions(+), 25 deletions(-) create mode 100644 meson_options.txt create mode 100644 src/tests/rapidcheck/generators.hpp create mode 100644 src/tests/rapidcheck/main.cpp delete mode 100644 src/tests/rapidcheck/rapidcheck_main.cpp create mode 100644 subprojects/packagefiles/rapidcheck/meson.build create mode 100644 subprojects/packagefiles/rapidcheck/meson_options.txt create mode 100644 subprojects/packagefiles/rapidcheck/src/detail/meson.build create mode 100644 subprojects/packagefiles/rapidcheck/src/gen/detail/meson.build create mode 100644 subprojects/packagefiles/rapidcheck/src/gen/meson.build create mode 100644 subprojects/packagefiles/rapidcheck/src/meson.build create mode 100644 utils/save_meson_wrap.py diff --git a/docs/Compiling.md b/docs/Compiling.md index b008891..68d7e47 100644 --- a/docs/Compiling.md +++ b/docs/Compiling.md @@ -2,7 +2,8 @@ In other words, how to create a new F.E.I.S. executable from the source code. -0. (If not done already) Set up you work environment by following [this page](docs/Setup.md) +0. (If not done already) Set up you work environment by following + [the Setup Steps](docs/Setup.md) 0. `cd` into the root of your local copy of F.E.I.S.'s source code ```console @@ -15,6 +16,13 @@ In other words, how to create a new F.E.I.S. executable from the source code. $ meson setup build ``` + If you want to compile the unit tests as well, pass in `-D tests=true`. + You can also set this option later by doing : + + ```console + $ meson configure build -D tests=true + ``` + 0. Compile in that directory ```console diff --git a/docs/Setup.md b/docs/Setup.md index be53a32..a7c6d1f 100644 --- a/docs/Setup.md +++ b/docs/Setup.md @@ -17,7 +17,8 @@ get a more up-to-date version than what your distro packages might have $ pip install meson ``` -Unfortunately this also means meson will not come with ninja so we need to install it ourselves : +Unfortunately this also means meson will not come with ninja so we need to +install it ourselves : ```console $ sudo apt install ninja-build @@ -46,9 +47,9 @@ $ sudo apt install clang-format #### MSYS2 MSYS2 is not the *usual* way to compile things for windows but it's the only -thing I know for now. If you know better, you're very welcome to do better -(and to also shower me with some of your knowledge, I absolutely *suck* at -build systems and would be delighted to hear from you) +thing I know for now. If you know better, by all means, do what you think is +best (and also please share some of your knowledge with me, I absolutely *suck* +at build systems and would be delighted to learn from an expert) Installing MSYS2 is pretty simple. [Follow their instructions](https://www.msys2.org/) @@ -65,4 +66,4 @@ $ pacman -S \ ``` Once this is done, open a new `MSYS2 MinGW x64` terminal and follow the -[instructions on how to compile](docs/Compiling.md) +[compilation instructions](docs/Compiling.md) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..fa6fb03 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,6 @@ +option( + 'tests', + type: 'boolean', + value: false, + description: 'Build F.E.I.S\'s unit tests' +) \ No newline at end of file diff --git a/src/better_beats.cpp b/src/better_beats.cpp index 2c91808..4d71968 100644 --- a/src/better_beats.cpp +++ b/src/better_beats.cpp @@ -15,7 +15,7 @@ bool is_expressible_as_240th(const Fraction& beat) { nlohmann::ordered_json beat_to_best_form(const Fraction& beat) { if (is_expressible_as_240th(beat)) { return nlohmann::ordered_json( - (240 * convert_to_u64(beat.numerator()) / convert_to_u64(beat.denominator()) + (240 * convert_to_u64(beat.numerator())) / convert_to_u64(beat.denominator()) ); } else { return beat_to_fraction_tuple(beat); diff --git a/src/better_note.cpp b/src/better_note.cpp index 7c9cacc..80a18d9 100644 --- a/src/better_note.cpp +++ b/src/better_note.cpp @@ -2,6 +2,8 @@ #include +#include + #include "better_beats.hpp" namespace better { @@ -14,10 +16,10 @@ namespace better { }; Position::Position(std::uint64_t x, std::uint64_t y) : x(x), y(y) { - if (x > 3 or y > 3) { + if (x > 3 or y > 2) { std::stringstream ss; ss << "Attempted to create Position from invalid coordinates : "; - ss << this; + ss << *this; throw std::invalid_argument(ss.str()); } }; @@ -35,7 +37,7 @@ namespace better { }; std::ostream& operator<< (std::ostream& out, const Position& pos) { - out << "(x: " << pos.get_x() << ", y: " << pos.get_y() << ")"; + out << fmt::format("(x: {}, y: {})", pos.x, pos.y); return out; }; @@ -67,7 +69,7 @@ namespace better { if (duration < 0) { std::stringstream ss; ss << "Attempted to create a LongNote with negative duration : "; - ss << duration.get_str(); + ss << duration; throw std::invalid_argument(ss.str()); } if (tail_tip == position) { @@ -232,7 +234,7 @@ namespace better { return std::visit([](const auto& n){return n.dump_to_memon_1_0_0();}, this->note); } - Note Note::load_from_memon_0_1_0(const nlohmann::json& json, std::uint64_t resolution) { + Note Note::load_from_memon_1_0_0(const nlohmann::json& json, std::uint64_t resolution) { const auto position = Position{json["n"].get()}; const auto time = load_memon_1_0_0_beat(json["t"], resolution); if (not json.contains("l")) { diff --git a/src/better_note.hpp b/src/better_note.hpp index 6edc17b..3bf7dc2 100644 --- a/src/better_note.hpp +++ b/src/better_note.hpp @@ -32,14 +32,13 @@ namespace better { std::uint64_t get_y() const; auto operator<=>(const Position&) const = default; + friend std::ostream& operator<<(std::ostream& out, const Position& pos); private: std::uint64_t x; std::uint64_t y; }; - std::ostream& operator<<(std::ostream& out, const Position& pos); - class TapNote { public: TapNote(Fraction time, Position position); @@ -96,9 +95,9 @@ namespace better { nlohmann::ordered_json dump_to_memon_1_0_0() const; - static Note load_from_memon_0_1_0( + static Note load_from_memon_1_0_0( const nlohmann::json& json, - std::uint64_t resolution + std::uint64_t resolution = 240 ); static Note load_from_memon_legacy( diff --git a/src/meson.build b/src/meson.build index 329ff36..7045c2c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -32,4 +32,6 @@ sources += files( subdir('widgets') -subdir('tests') \ No newline at end of file +if get_option('tests') + subdir('tests') +endif \ No newline at end of file diff --git a/src/tests/rapidcheck/generators.hpp b/src/tests/rapidcheck/generators.hpp new file mode 100644 index 0000000..0f387c6 --- /dev/null +++ b/src/tests/rapidcheck/generators.hpp @@ -0,0 +1,83 @@ +#include + +#include "../../better_note.hpp" +#include "../../special_numeric_types.hpp" +#include "rapidcheck/gen/Arbitrary.h" +#include "rapidcheck/gen/Numeric.h" + +namespace rc { + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::construct( + gen::inRange(0, 16) + ); + } + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::apply([](const Fraction& a, const Fraction& b) { + return a + b; + }, + gen::cast( + gen::nonNegative() + ), + gen::construct( + gen::nonNegative(), + gen::positive() + ) + ); + } + + static Gen positive() { + return gen::apply([](const Fraction& a, const Fraction& b) { + return a + b; + }, + gen::cast( + gen::nonNegative() + ), + gen::construct( + gen::positive(), + gen::positive() + ) + ); + } + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::construct( + gen::arbitrary(), + gen::arbitrary() + ); + } + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + const auto pos = *gen::arbitrary(); + const auto tail_6_notation = *gen::inRange(0, 6); + const auto tail_pos = better::convert_6_notation_to_position(pos, tail_6_notation); + return gen::construct( + gen::arbitrary(), + gen::just(pos), + gen::positive(), + gen::just(tail_pos) + ); + } + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::oneOf( + gen::construct(gen::arbitrary()), + gen::construct(gen::arbitrary()) + ); + } + }; +} \ No newline at end of file diff --git a/src/tests/rapidcheck/main.cpp b/src/tests/rapidcheck/main.cpp new file mode 100644 index 0000000..a45c426 --- /dev/null +++ b/src/tests/rapidcheck/main.cpp @@ -0,0 +1,17 @@ +#include + +#include "generators.hpp" + +#include "../../better_note.hpp" + +int main() { + rc::check( + "Notes survive being converted to json and back", + [](const better::Note& n) { + const auto j = n.dump_to_memon_1_0_0(); + const auto n_recovered = better::Note::load_from_memon_1_0_0(j); + RC_ASSERT(n_recovered == n); + } + ); + return 0; +} \ No newline at end of file diff --git a/src/tests/rapidcheck/meson.build b/src/tests/rapidcheck/meson.build index aaf8866..32008ea 100644 --- a/src/tests/rapidcheck/meson.build +++ b/src/tests/rapidcheck/meson.build @@ -1,7 +1,8 @@ rapidcheck_tests = executable( 'rapidcheck_tests', - 'rapidcheck_main.cpp', - 'test_fractions.cpp', + 'main.cpp', + '../../better_note.cpp', + '../../better_beats.cpp', '../../special_numeric_types.cpp', include_sources['fmt'], dependencies: [ diff --git a/src/tests/rapidcheck/rapidcheck_main.cpp b/src/tests/rapidcheck/rapidcheck_main.cpp deleted file mode 100644 index c0f00a7..0000000 --- a/src/tests/rapidcheck/rapidcheck_main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - - return 0; -} \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/meson.build b/subprojects/packagefiles/rapidcheck/meson.build new file mode 100644 index 0000000..0e24dbb --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/meson.build @@ -0,0 +1,71 @@ +project( + 'rapidcheck', + 'cpp', + version: '0.1.0', + license: 'BSD-2-Clause', + default_options : [ + 'cpp_std=c++11', + 'warning_level=3', + ], + meson_version : '>=0.62.0', +) + +sources = [] + +subdir('src') # fills in 'sources' + +includes = include_directories('include') + +add_project_arguments( + '-Wno-missing-braces', + '-Wno-unused-command-line-argument', + language: 'cpp' +) + +# Random is used a LOT so it should preferably be really fast +rapidcheck_random_lib = static_library( + 'rapidcheck_random', + 'src/Random.cpp', + include_directories: includes, + cpp_args: '-O3', +) + +# On Windows under MinGW, random_device provides no entropy, +# so it will always return the same value. +# Seed using system time instead. +# See: https://stackoverflow.com/questions/18880654/why-do-i-get-the-same-sequence-for-every-run-with-stdrandom-device-with-mingw +if host_machine.system() == 'cygwin' + add_project_arguments('-DRC_SEED_SYSTEM_TIME', language: 'cpp') +endif + +if not get_option('rtti') + add_project_arguments('-DRC_DONT_USE_RTTI', language: 'cpp') +endif + +# if get_option('tests') +# subdir('ext') +# subdir('test') +# endif + +# if get_option('examples') +# ... more unfun stuff +# endif + +rapidcheck_lib = static_library( + 'rapidcheck', + sources, + include_directories: includes, + link_with: rapidcheck_random_lib, + cpp_args: [ + '-Wall', + '-Wno-missing-braces', + '-Wno-unused-command-line-argument', + ] +) + +rapidcheck_dep = declare_dependency( + include_directories: includes, + link_with: rapidcheck_lib, +) + +meson.override_dependency('rapidcheck', rapidcheck_dep) \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/meson_options.txt b/subprojects/packagefiles/rapidcheck/meson_options.txt new file mode 100644 index 0000000..eb16ce9 --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/meson_options.txt @@ -0,0 +1,20 @@ +option( + 'rtti', + type: 'boolean', + value: true, + description: 'Build RapidCheck with Run-Time Type Inspection' +) + +# option( +# 'tests', +# type: 'boolean', +# value: false, +# description: 'Build RapidCheck tests' +# ) + +# option( +# 'examples', +# type: 'boolean', +# value: true, +# description: 'Build RapidCheck examples' +# ) \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/src/detail/meson.build b/subprojects/packagefiles/rapidcheck/src/detail/meson.build new file mode 100644 index 0000000..01e36cb --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/src/detail/meson.build @@ -0,0 +1,23 @@ +sources += files( + 'Any.cpp', + 'Assertions.cpp', + 'Base64.cpp', + 'Configuration.cpp', + 'DefaultTestListener.cpp', + 'FrequencyMap.cpp', + 'ImplicitParam.cpp', + 'LogTestListener.cpp', + 'MapParser.cpp', + 'MulticastTestListener.cpp', + 'ParseException.cpp', + 'Platform.cpp', + 'Property.cpp', + 'PropertyContext.cpp', + 'ReproduceListener.cpp', + 'Results.cpp', + 'Serialization.cpp', + 'StringSerialization.cpp', + 'TestMetadata.cpp', + 'TestParams.cpp', + 'Testing.cpp', +) \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/src/gen/detail/meson.build b/subprojects/packagefiles/rapidcheck/src/gen/detail/meson.build new file mode 100644 index 0000000..465ef76 --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/src/gen/detail/meson.build @@ -0,0 +1,6 @@ +sources += files( + 'ExecHandler.cpp', + 'GenerationHandler.cpp', + 'Recipe.cpp', + 'ScaleInteger.cpp', +) \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/src/gen/meson.build b/subprojects/packagefiles/rapidcheck/src/gen/meson.build new file mode 100644 index 0000000..088f43f --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/src/gen/meson.build @@ -0,0 +1,6 @@ +sources += files( + 'Numeric.cpp', + 'Text.cpp', +) + +subdir('detail') \ No newline at end of file diff --git a/subprojects/packagefiles/rapidcheck/src/meson.build b/subprojects/packagefiles/rapidcheck/src/meson.build new file mode 100644 index 0000000..ee3b03b --- /dev/null +++ b/subprojects/packagefiles/rapidcheck/src/meson.build @@ -0,0 +1,13 @@ +sources += files( + 'BeforeMinimalTestCase.cpp', + 'Check.cpp', + 'Classify.cpp', + 'GenerationFailure.cpp', + 'Log.cpp', + # Leave this one out because it needs to be compiled spearately + # 'Random.cpp', + 'Show.cpp', +) + +subdir('detail') +subdir('gen') \ No newline at end of file diff --git a/utils/save_meson_wrap.py b/utils/save_meson_wrap.py new file mode 100644 index 0000000..af474c3 --- /dev/null +++ b/utils/save_meson_wrap.py @@ -0,0 +1,24 @@ +"""implement the missing --save option of meson subprojects packagefiles +for wrap-git packages""" + +from argparse import ArgumentParser +from path import Path + +parser = ArgumentParser() +parser.add_argument("wrap", type=Path) +args = parser.parse_args() + +subprojects = Path("subprojects") +if not subprojects.exists(): + print(f"{subprojects} folder doesn't exist, are you in the right directory ?") + +subproject = subprojects / args.wrap +patch = subprojects / "packagefiles" / args.wrap +if not patch.exists(): + print(f"subproject folder doesn't exist ({subproject}), is the name correct ?") + +for absolute in subproject.walkfiles("*meson*"): + relative = absolute.relpath(subproject) + (patch / relative).parent.makedirs_p() + absolute.copy(patch / relative) +