From 5233799361778b6d50ca2800313b239441970c6a Mon Sep 17 00:00:00 2001 From: Rob Date: Mon, 28 Aug 2017 14:38:41 -0700 Subject: [PATCH 01/39] Initial commit --- LICENSE | 21 +++++++++++++++++++++ README.md | 1 + 2 files changed, 22 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b9f7a8d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 PolySync Technologies + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d7a15d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# roscco \ No newline at end of file From 606ae04498ff93dbf014827b3bbb353de2726c75 Mon Sep 17 00:00:00 2001 From: Robert Brown Date: Fri, 1 Sep 2017 16:14:49 -0700 Subject: [PATCH 02/39] Add initial OSCC and ROS interface Prior to this commit this repository was bare. This commit adds the initial interface for OSCC and ROS called roscco. --- .gitmodules | 3 + CMakeLists.txt | 359 ++++++++++++ Jenkinsfile | 46 ++ README.md | 24 +- include/roscco/oscc_to_ros.h | 49 ++ include/roscco/ros_to_oscc.h | 42 ++ msg/BrakeCommand.msg | 2 + msg/BrakeReport.msg | 2 + msg/BrakeReportData.msg | 5 + msg/CanFrame.msg | 2 + msg/CanFrameData.msg | 6 + msg/EnableDisable.msg | 2 + msg/FaultOriginId.msg | 3 + msg/FaultReport.msg | 2 + msg/FaultReportData.msg | 3 + msg/SteeringCommand.msg | 2 + msg/SteeringReport.msg | 2 + msg/SteeringReportData.msg | 5 + msg/ThrottleCommand.msg | 2 + msg/ThrottleReport.msg | 2 + msg/ThrottleReportData.msg | 5 + oscc | 1 + package.xml | 54 ++ src/oscc_to_ros.cpp | 98 ++++ src/ros_to_oscc.cpp | 84 +++ src/roscco_node.cpp | 39 ++ test/include/oscc.h | 58 ++ test/oscc_fixture.c | 84 +++ test/rapidcheck/include/rapidcheck.h | 37 ++ .../include/rapidcheck/Assertions.h | 97 ++++ .../include/rapidcheck/Assertions.hpp | 41 ++ .../rapidcheck/BeforeMinimalTestCase.h | 11 + test/rapidcheck/include/rapidcheck/Check.h | 21 + test/rapidcheck/include/rapidcheck/Check.hpp | 72 +++ test/rapidcheck/include/rapidcheck/Classify.h | 17 + .../include/rapidcheck/Classify.hpp | 19 + test/rapidcheck/include/rapidcheck/Gen.h | 74 +++ test/rapidcheck/include/rapidcheck/Gen.hpp | 142 +++++ .../include/rapidcheck/GenerationFailure.h | 13 + test/rapidcheck/include/rapidcheck/Log.h | 11 + test/rapidcheck/include/rapidcheck/Log.hpp | 13 + test/rapidcheck/include/rapidcheck/Maybe.h | 100 ++++ test/rapidcheck/include/rapidcheck/Maybe.hpp | 174 ++++++ test/rapidcheck/include/rapidcheck/Nothing.h | 15 + test/rapidcheck/include/rapidcheck/Random.h | 84 +++ test/rapidcheck/include/rapidcheck/Random.hpp | 46 ++ test/rapidcheck/include/rapidcheck/Seq.h | 86 +++ test/rapidcheck/include/rapidcheck/Seq.hpp | 118 ++++ test/rapidcheck/include/rapidcheck/Show.h | 36 ++ test/rapidcheck/include/rapidcheck/Show.hpp | 239 ++++++++ .../include/rapidcheck/Shrinkable.h | 62 +++ .../include/rapidcheck/Shrinkable.hpp | 115 ++++ test/rapidcheck/include/rapidcheck/Traits.h | 23 + .../include/rapidcheck/detail/AlignedUnion.h | 25 + .../include/rapidcheck/detail/Any.h | 60 ++ .../include/rapidcheck/detail/Any.hpp | 64 +++ .../include/rapidcheck/detail/ApplyTuple.h | 54 ++ .../include/rapidcheck/detail/BitStream.h | 52 ++ .../include/rapidcheck/detail/BitStream.hpp | 101 ++++ .../include/rapidcheck/detail/Capture.h | 118 ++++ .../include/rapidcheck/detail/Configuration.h | 60 ++ .../include/rapidcheck/detail/ExecFixture.h | 23 + .../include/rapidcheck/detail/FrequencyMap.h | 24 + .../rapidcheck/detail/FunctionTraits.h | 57 ++ .../include/rapidcheck/detail/ImplicitParam.h | 104 ++++ .../rapidcheck/detail/ImplicitParam.hpp | 44 ++ .../include/rapidcheck/detail/IntSequence.h | 42 ++ .../include/rapidcheck/detail/Platform.h | 25 + .../include/rapidcheck/detail/Property.h | 31 ++ .../include/rapidcheck/detail/Property.hpp | 109 ++++ .../rapidcheck/detail/PropertyContext.h | 41 ++ .../include/rapidcheck/detail/Results.h | 137 +++++ .../include/rapidcheck/detail/Results.hpp | 32 ++ .../include/rapidcheck/detail/Serialization.h | 131 +++++ .../rapidcheck/detail/Serialization.hpp | 232 ++++++++ .../include/rapidcheck/detail/ShowType.h | 21 + .../include/rapidcheck/detail/ShowType.hpp | 323 +++++++++++ .../include/rapidcheck/detail/TestListener.h | 35 ++ .../rapidcheck/detail/TestListenerAdapter.h | 17 + .../include/rapidcheck/detail/TestMetadata.h | 22 + .../include/rapidcheck/detail/TestParams.h | 29 + .../include/rapidcheck/detail/Traits.h | 22 + .../include/rapidcheck/detail/TypeList.h | 39 ++ .../include/rapidcheck/detail/Utility.h | 114 ++++ .../include/rapidcheck/detail/Variant.h | 111 ++++ .../include/rapidcheck/detail/Variant.hpp | 300 ++++++++++ .../rapidcheck/include/rapidcheck/fn/Common.h | 18 + .../include/rapidcheck/fn/Common.hpp | 30 + .../include/rapidcheck/gen/Arbitrary.h | 22 + .../include/rapidcheck/gen/Arbitrary.hpp | 29 + .../rapidcheck/include/rapidcheck/gen/Build.h | 74 +++ .../include/rapidcheck/gen/Build.hpp | 196 +++++++ .../include/rapidcheck/gen/Chrono.h | 11 + .../include/rapidcheck/gen/Chrono.hpp | 26 + .../include/rapidcheck/gen/Container.h | 30 + .../include/rapidcheck/gen/Container.hpp | 523 ++++++++++++++++++ .../include/rapidcheck/gen/Create.h | 22 + .../include/rapidcheck/gen/Create.hpp | 22 + test/rapidcheck/include/rapidcheck/gen/Exec.h | 21 + .../include/rapidcheck/gen/Exec.hpp | 19 + .../rapidcheck/include/rapidcheck/gen/Maybe.h | 14 + .../include/rapidcheck/gen/Maybe.hpp | 51 ++ .../include/rapidcheck/gen/Numeric.h | 18 + .../include/rapidcheck/gen/Numeric.hpp | 114 ++++ .../include/rapidcheck/gen/Predicate.h | 56 ++ .../include/rapidcheck/gen/Predicate.hpp | 114 ++++ .../include/rapidcheck/gen/Select.h | 59 ++ .../include/rapidcheck/gen/Select.hpp | 195 +++++++ test/rapidcheck/include/rapidcheck/gen/Text.h | 23 + .../include/rapidcheck/gen/Text.hpp | 78 +++ .../include/rapidcheck/gen/Transform.h | 73 +++ .../include/rapidcheck/gen/Transform.hpp | 150 +++++ .../rapidcheck/include/rapidcheck/gen/Tuple.h | 21 + .../include/rapidcheck/gen/Tuple.hpp | 152 +++++ .../rapidcheck/gen/detail/ExecHandler.h | 25 + .../include/rapidcheck/gen/detail/ExecRaw.h | 20 + .../include/rapidcheck/gen/detail/ExecRaw.hpp | 69 +++ .../rapidcheck/gen/detail/GenerationHandler.h | 45 ++ .../include/rapidcheck/gen/detail/Recipe.h | 49 ++ .../rapidcheck/gen/detail/ScaleInteger.h | 15 + .../gen/detail/ShrinkValueIterator.h | 19 + .../gen/detail/ShrinkValueIterator.hpp | 55 ++ test/rapidcheck/include/rapidcheck/gmock.h | 147 +++++ test/rapidcheck/include/rapidcheck/gtest.h | 62 +++ .../include/rapidcheck/seq/Create.h | 53 ++ .../include/rapidcheck/seq/Create.hpp | 199 +++++++ .../include/rapidcheck/seq/Operations.h | 43 ++ .../include/rapidcheck/seq/Operations.hpp | 88 +++ .../include/rapidcheck/seq/SeqIterator.h | 40 ++ .../include/rapidcheck/seq/SeqIterator.hpp | 56 ++ .../include/rapidcheck/seq/Transform.h | 76 +++ .../include/rapidcheck/seq/Transform.hpp | 387 +++++++++++++ .../include/rapidcheck/shrink/Shrink.h | 56 ++ .../include/rapidcheck/shrink/Shrink.hpp | 202 +++++++ .../include/rapidcheck/shrinkable/Create.h | 46 ++ .../include/rapidcheck/shrinkable/Create.hpp | 99 ++++ .../rapidcheck/shrinkable/Operations.h | 39 ++ .../rapidcheck/shrinkable/Operations.hpp | 60 ++ .../include/rapidcheck/shrinkable/Transform.h | 46 ++ .../rapidcheck/shrinkable/Transform.hpp | 152 +++++ test/rapidcheck/include/rapidcheck/state.h | 9 + .../include/rapidcheck/state/Command.h | 48 ++ .../include/rapidcheck/state/Command.hpp | 48 ++ .../include/rapidcheck/state/Commands.h | 60 ++ .../include/rapidcheck/state/Commands.hpp | 68 +++ .../include/rapidcheck/state/State.h | 38 ++ .../include/rapidcheck/state/State.hpp | 41 ++ .../include/rapidcheck/state/gen/Commands.h | 34 ++ .../include/rapidcheck/state/gen/Commands.hpp | 269 +++++++++ .../rapidcheck/state/gen/ExecCommands.h | 46 ++ .../rapidcheck/state/gen/ExecCommands.hpp | 95 ++++ test/rapidcheck/librapidcheck.a | Bin 0 -> 7074862 bytes test/test_oscc_to_ros.cpp | 210 +++++++ test/test_oscc_to_ros.launch | 7 + test/test_ros_to_oscc.cpp | 111 ++++ test/test_ros_to_oscc.launch | 7 + test/test_roscco_mem.launch | 9 + 157 files changed, 10659 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 Jenkinsfile create mode 100644 include/roscco/oscc_to_ros.h create mode 100644 include/roscco/ros_to_oscc.h create mode 100644 msg/BrakeCommand.msg create mode 100644 msg/BrakeReport.msg create mode 100644 msg/BrakeReportData.msg create mode 100644 msg/CanFrame.msg create mode 100644 msg/CanFrameData.msg create mode 100644 msg/EnableDisable.msg create mode 100644 msg/FaultOriginId.msg create mode 100644 msg/FaultReport.msg create mode 100644 msg/FaultReportData.msg create mode 100644 msg/SteeringCommand.msg create mode 100644 msg/SteeringReport.msg create mode 100644 msg/SteeringReportData.msg create mode 100644 msg/ThrottleCommand.msg create mode 100644 msg/ThrottleReport.msg create mode 100644 msg/ThrottleReportData.msg create mode 160000 oscc create mode 100644 package.xml create mode 100644 src/oscc_to_ros.cpp create mode 100644 src/ros_to_oscc.cpp create mode 100644 src/roscco_node.cpp create mode 100644 test/include/oscc.h create mode 100644 test/oscc_fixture.c create mode 100644 test/rapidcheck/include/rapidcheck.h create mode 100644 test/rapidcheck/include/rapidcheck/Assertions.h create mode 100644 test/rapidcheck/include/rapidcheck/Assertions.hpp create mode 100644 test/rapidcheck/include/rapidcheck/BeforeMinimalTestCase.h create mode 100644 test/rapidcheck/include/rapidcheck/Check.h create mode 100644 test/rapidcheck/include/rapidcheck/Check.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Classify.h create mode 100644 test/rapidcheck/include/rapidcheck/Classify.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Gen.h create mode 100644 test/rapidcheck/include/rapidcheck/Gen.hpp create mode 100644 test/rapidcheck/include/rapidcheck/GenerationFailure.h create mode 100644 test/rapidcheck/include/rapidcheck/Log.h create mode 100644 test/rapidcheck/include/rapidcheck/Log.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Maybe.h create mode 100644 test/rapidcheck/include/rapidcheck/Maybe.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Nothing.h create mode 100644 test/rapidcheck/include/rapidcheck/Random.h create mode 100644 test/rapidcheck/include/rapidcheck/Random.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Seq.h create mode 100644 test/rapidcheck/include/rapidcheck/Seq.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Show.h create mode 100644 test/rapidcheck/include/rapidcheck/Show.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Shrinkable.h create mode 100644 test/rapidcheck/include/rapidcheck/Shrinkable.hpp create mode 100644 test/rapidcheck/include/rapidcheck/Traits.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/AlignedUnion.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Any.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Any.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/ApplyTuple.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/BitStream.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/BitStream.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/Capture.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Configuration.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/ExecFixture.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/FrequencyMap.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/FunctionTraits.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/ImplicitParam.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/ImplicitParam.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/IntSequence.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Platform.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Property.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Property.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/PropertyContext.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Results.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Results.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/Serialization.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Serialization.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/ShowType.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/ShowType.hpp create mode 100644 test/rapidcheck/include/rapidcheck/detail/TestListener.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/TestListenerAdapter.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/TestMetadata.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/TestParams.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Traits.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/TypeList.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Utility.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Variant.h create mode 100644 test/rapidcheck/include/rapidcheck/detail/Variant.hpp create mode 100644 test/rapidcheck/include/rapidcheck/fn/Common.h create mode 100644 test/rapidcheck/include/rapidcheck/fn/Common.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Arbitrary.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Arbitrary.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Build.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Build.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Chrono.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Chrono.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Container.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Container.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Create.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Create.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Exec.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Exec.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Maybe.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Maybe.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Numeric.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Numeric.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Predicate.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Predicate.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Select.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Select.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Text.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Text.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Transform.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Transform.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/Tuple.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/Tuple.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ExecHandler.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ExecRaw.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ExecRaw.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/GenerationHandler.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/Recipe.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ScaleInteger.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ShrinkValueIterator.h create mode 100644 test/rapidcheck/include/rapidcheck/gen/detail/ShrinkValueIterator.hpp create mode 100644 test/rapidcheck/include/rapidcheck/gmock.h create mode 100644 test/rapidcheck/include/rapidcheck/gtest.h create mode 100644 test/rapidcheck/include/rapidcheck/seq/Create.h create mode 100644 test/rapidcheck/include/rapidcheck/seq/Create.hpp create mode 100644 test/rapidcheck/include/rapidcheck/seq/Operations.h create mode 100644 test/rapidcheck/include/rapidcheck/seq/Operations.hpp create mode 100644 test/rapidcheck/include/rapidcheck/seq/SeqIterator.h create mode 100644 test/rapidcheck/include/rapidcheck/seq/SeqIterator.hpp create mode 100644 test/rapidcheck/include/rapidcheck/seq/Transform.h create mode 100644 test/rapidcheck/include/rapidcheck/seq/Transform.hpp create mode 100644 test/rapidcheck/include/rapidcheck/shrink/Shrink.h create mode 100644 test/rapidcheck/include/rapidcheck/shrink/Shrink.hpp create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Create.h create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Create.hpp create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Operations.h create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Operations.hpp create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Transform.h create mode 100644 test/rapidcheck/include/rapidcheck/shrinkable/Transform.hpp create mode 100644 test/rapidcheck/include/rapidcheck/state.h create mode 100644 test/rapidcheck/include/rapidcheck/state/Command.h create mode 100644 test/rapidcheck/include/rapidcheck/state/Command.hpp create mode 100644 test/rapidcheck/include/rapidcheck/state/Commands.h create mode 100644 test/rapidcheck/include/rapidcheck/state/Commands.hpp create mode 100644 test/rapidcheck/include/rapidcheck/state/State.h create mode 100644 test/rapidcheck/include/rapidcheck/state/State.hpp create mode 100644 test/rapidcheck/include/rapidcheck/state/gen/Commands.h create mode 100644 test/rapidcheck/include/rapidcheck/state/gen/Commands.hpp create mode 100644 test/rapidcheck/include/rapidcheck/state/gen/ExecCommands.h create mode 100644 test/rapidcheck/include/rapidcheck/state/gen/ExecCommands.hpp create mode 100644 test/rapidcheck/librapidcheck.a create mode 100644 test/test_oscc_to_ros.cpp create mode 100644 test/test_oscc_to_ros.launch create mode 100644 test/test_ros_to_oscc.cpp create mode 100644 test/test_ros_to_oscc.launch create mode 100644 test/test_roscco_mem.launch diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..34cc7a8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "oscc"] + path = oscc + url = https://github.com/PolySync/oscc.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ebe9201 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,359 @@ +cmake_minimum_required(VERSION 2.8.3) +project(roscco) + +## Compile as C++11, supported in ROS Kinetic and newer +## C++11 is required for rapidcheck +if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (CMAKE_CXX_FLAGS "-std=gnu++11 ${CMAKE_CXX_FLAGS}") + endif () +else () + set (CMAKE_CXX_STANDARD 11) +endif () + +if(KIA_SOUL) + add_definitions(-DKIA_SOUL) +elseif(KIA_SOUL_EV) + add_definitions(-DKIA_SOUL_EV) +endif() + +## Find catkin macros and libraries +## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) +## is used, also find other catkin packages +find_package(catkin REQUIRED COMPONENTS + message_generation + roscpp + std_msgs +) + +## System dependencies are found with CMake's conventions +# find_package(Boost REQUIRED COMPONENTS system) + + +## Uncomment this if the package has a setup.py. This macro ensures +## modules and global scripts declared therein get installed +## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html +# catkin_python_setup() + +################################################ +## Declare ROS messages, services and actions ## +################################################ + +add_message_files( + FILES + BrakeReportData.msg + BrakeReport.msg + BrakeCommand.msg + SteeringReportData.msg + SteeringReport.msg + SteeringCommand.msg + ThrottleReportData.msg + ThrottleReport.msg + ThrottleCommand.msg + FaultReportData.msg + FaultReport.msg + CanFrame.msg + CanFrameData.msg + EnableDisable.msg +) + +generate_messages( + DEPENDENCIES + std_msgs +) + +## To declare and build messages, services or actions from within this +## package, follow these steps: +## * Let MSG_DEP_SET be the set of packages whose message types you use in +## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). +## * In the file package.xml: +## * add a build_depend tag for "message_generation" +## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET +## * If MSG_DEP_SET isn't empty the following dependency has been pulled in +## but can be declared for certainty nonetheless: +## * add a run_depend tag for "message_runtime" +## * In this file (CMakeLists.txt): +## * add "message_generation" and every package in MSG_DEP_SET to +## find_package(catkin REQUIRED COMPONENTS ...) +## * add "message_runtime" and every package in MSG_DEP_SET to +## catkin_package(CATKIN_DEPENDS ...) +## * uncomment the add_*_files sections below as needed +## and list every .msg/.srv/.action file to be processed +## * uncomment the generate_messages entry below +## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) + +## Generate messages in the 'msg' folder +# add_message_files( +# FILES +# Message1.msg +# Message2.msg +# ) + +## Generate services in the 'srv' folder +# add_service_files( +# FILES +# Service1.srv +# Service2.srv +# ) + +## Generate actions in the 'action' folder +# add_action_files( +# FILES +# Action1.action +# Action2.action +# ) + +## Generate added messages and services with any dependencies listed here +# generate_messages( +# DEPENDENCIES +# std_msgs +# ) + +################################################ +## Declare ROS dynamic reconfigure parameters ## +################################################ + +## To declare and build dynamic reconfigure parameters within this +## package, follow these steps: +## * In the file package.xml: +## * add a build_depend and a run_depend tag for "dynamic_reconfigure" +## * In this file (CMakeLists.txt): +## * add "dynamic_reconfigure" to +## find_package(catkin REQUIRED COMPONENTS ...) +## * uncomment the "generate_dynamic_reconfigure_options" section below +## and list every .cfg file to be processed + +## Generate dynamic reconfigure parameters in the 'cfg' folder +# generate_dynamic_reconfigure_options( +# cfg/DynReconf1.cfg +# cfg/DynReconf2.cfg +# ) + +################################### +## catkin specific configuration ## +################################### +## The catkin_package macro generates cmake config files for your package +## Declare things to be passed to dependent projects +## INCLUDE_DIRS: uncomment this if you package contains header files +## LIBRARIES: libraries you create in this project that dependent projects also need +## CATKIN_DEPENDS: catkin_packages dependent projects also need +## DEPENDS: system dependencies of this project that dependent projects also need +catkin_package( +# INCLUDE_DIRS include +# LIBRARIES roscco +# CATKIN_DEPENDS message_generation roscpp std_msgs +# DEPENDS system_lib +) + +########### +## Build ## +########### + +## Specify additional locations of header files +## Your package locations should be listed before other locations +#include_directories( +# include +# oscc/api/include +# ${catkin_INCLUDE_DIRS} +#) + +add_library( + oscc_api + oscc/api/src/oscc.c +) + +target_include_directories( + oscc_api PUBLIC + include + oscc/api/include +) + +add_library( + ${PROJECT_NAME}_ros_to_oscc + src/ros_to_oscc.cpp +) + +target_include_directories( + ${PROJECT_NAME}_ros_to_oscc PUBLIC + include + oscc/api/include + ${catkin_INCLUDE_DIRS} +) + +add_library( + ${PROJECT_NAME}_oscc_to_ros + src/oscc_to_ros.cpp +) + +target_include_directories( + ${PROJECT_NAME}_oscc_to_ros PUBLIC + include + oscc/api/include + ${catkin_INCLUDE_DIRS} +) + +target_link_libraries( + ${PROJECT_NAME}_oscc_to_ros + oscc_api +) + +## Declare a C++ library +# add_library(${PROJECT_NAME} +# src/${PROJECT_NAME}/roscco.cpp +# ) + +## Add cmake target dependencies of the library +## as an example, code may need to be generated before libraries +## either from message generation or dynamic reconfigure +add_dependencies( + ${PROJECT_NAME}_ros_to_oscc + ${${PROJECT_NAME}_EXPORTED_TARGETS} + ${catkin_EXPORTED_TARGETS} +) + +add_dependencies( + ${PROJECT_NAME}_oscc_to_ros + ${${PROJECT_NAME}_EXPORTED_TARGETS} + ${catkin_EXPORTED_TARGETS} +) + +## Declare a C++ executable +## With catkin_make all packages are built within a single CMake context +## The recommended prefix ensures that target names across packages don't collide +add_executable(${PROJECT_NAME}_node src/roscco_node.cpp) + +target_include_directories( + ${PROJECT_NAME}_node PUBLIC + include + oscc/api/include + ${catkin_INCLUDE_DIRS} +) + +## Rename C++ executable without prefix +## The above recommended prefix causes long target names, the following renames the +## target back to the shorter version for ease of user use +## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" +# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") + +## Add cmake target dependencies of the executable +## same as for the library above +add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + +## Specify libraries to link a library or executable target against +target_link_libraries(${PROJECT_NAME}_node + oscc_api + ${PROJECT_NAME}_ros_to_oscc + ${PROJECT_NAME}_oscc_to_ros + ${catkin_LIBRARIES} +) + +############# +## Install ## +############# + +# all install targets should use catkin DESTINATION variables +# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html + +## Mark executable scripts (Python etc.) for installation +## in contrast to setup.py, you can choose the destination +# install(PROGRAMS +# scripts/my_python_script +# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark executables and/or libraries for installation +# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark cpp header files for installation +# install(DIRECTORY include/${PROJECT_NAME}/ +# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +# FILES_MATCHING PATTERN "*.h" +# PATTERN ".svn" EXCLUDE +# ) + +## Mark other files for installation (e.g. launch and bag files, etc.) +# install(FILES +# # myfile1 +# # myfile2 +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +# ) + +############# +## Testing ## +############# + +if(CATKIN_ENABLE_TESTING) + + find_package(rostest REQUIRED) + + add_library( + oscc_fixture + test/oscc_fixture.c + ) + + + target_include_directories( + oscc_fixture PUBLIC + test/include + oscc/api/include/can_protocols + ${catkin_INCLUDE_DIRS} + ) + + add_rostest_gtest( + test_ros_oscc_api + test/test_ros_to_oscc.launch + test/test_ros_to_oscc.cpp + ) + + target_include_directories( + test_ros_oscc_api PUBLIC + test/include + test/rapidcheck/include + ${catkin_INCLUDE_DIRS} + ) + + target_link_libraries( + test_ros_oscc_api + ${CMAKE_CURRENT_SOURCE_DIR}/test/rapidcheck/librapidcheck.a + ${PROJECT_NAME}_ros_to_oscc + oscc_fixture + ${catkin_LIBRARIES} + ) + + add_rostest_gtest( + test_oscc_ros_api + test/test_oscc_to_ros.launch + test/test_oscc_to_ros.cpp + ) + + target_include_directories( + test_oscc_ros_api PUBLIC + include + test/include + test/rapidcheck/include + oscc/api/include/can_protocols + ${catkin_INCLUDE_DIRS} + ) + + target_link_libraries( + test_oscc_ros_api + ${CMAKE_CURRENT_SOURCE_DIR}/test/rapidcheck/librapidcheck.a + ${PROJECT_NAME}_oscc_to_ros + oscc_fixture + ${catkin_LIBRARIES} + ) + +endif() + +## Add gtest based cpp test target and link libraries +# catkin_add_gtest(${PROJECT_NAME}-test test/test_roscco.cpp) +# if(TARGET ${PROJECT_NAME}-test) +# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) +# endif() + +## Add folders to be run by python nosetests +# catkin_add_nosetests(test) diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..9c1406f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,46 @@ +#!groovy +node('xenial') { + try { + stage('Checkout') { + sh 'mkdir -p catkin_ws/src/roscco' + dir('catkin_ws/src/roscco') + { + checkout([ + $class: 'GitSCM', + branches: scm.branches, + doGenerateSubmoduleConfigurations: false, + extensions: scm.extensions + [[$class: 'CleanBeforeCheckout'], + [$class: 'SubmoduleOption', + disableSubmodules: false, + parentCredentials: true, + recursiveSubmodules: true, + reference: '', + trackingSubmodules: false]], + submoduleCfg: [], + userRemoteConfigs: scm.userRemoteConfigs + ]) + } + } + stage('Build') { + parallel 'kia soul firmware': { + sh '. /opt/ros/kinetic/setup.sh && cd catkin_ws && catkin_make -DKIA_SOUL=ON' + } + echo 'Build Complete!' + } + stage('Test') { + parallel 'kia soul tests': { + sh '. /opt/ros/kinetic/setup.sh && cd catkin_ws && catkin_make run_tests -DKIA_SOUL=ON' + echo 'ROS Tests Complete!' + } + } + stage('Release') { + echo 'Release Package Created!' + } + } + catch(Exception e) { + throw e; + } + finally { + deleteDir() + } +} diff --git a/README.md b/README.md index 7d7a15d..a9f55d6 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -# roscco \ No newline at end of file +# roscco + +## Building roscco in a catkin project + +``` +mkdir -p catkin_ws/src && cd catkin_ws/src +git clone --recursive https://github.com/PolySync/roscco.git +cd .. +catkin_make -DKIA_SOUL=ON +``` + +## Running roscco + +roscco defaults to can channel but can be specified as a node parameter +``` +rosrun roscco roscco_node _can_channel=0 +``` + +## Testing roscco + +``` +catkin_make run_tests -DKIA_SOUL=ON +``` diff --git a/include/roscco/oscc_to_ros.h b/include/roscco/oscc_to_ros.h new file mode 100644 index 0000000..93ee18c --- /dev/null +++ b/include/roscco/oscc_to_ros.h @@ -0,0 +1,49 @@ +#ifndef OSCC_TO_ROS_H +#define OSCC_TO_ROS_H + +extern "C" { +#include +} + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ros::Publisher topic_brake_report_; + +static ros::Publisher topic_steering_report_; + +static ros::Publisher topic_throttle_report_; + +static ros::Publisher topic_fault_report_; + +static ros::Publisher topic_obd_messages_; + +class OsccToRos +{ +public: + OsccToRos(ros::NodeHandle* public_nh, ros::NodeHandle* private_nh); + + template + static void cast_callback(OSCCTYPE* report, ros::Publisher* pub); + + static void steering_callback(oscc_steering_report_s* report); + + static void brake_callback(oscc_brake_report_s* report); + + static void throttle_callback(oscc_throttle_report_s* report); + + static void fault_callback(oscc_fault_report_s* report); + + static void obd_callback(can_frame* frame); +}; + +#endif // OSCC_TO_ROS_H diff --git a/include/roscco/ros_to_oscc.h b/include/roscco/ros_to_oscc.h new file mode 100644 index 0000000..1d28449 --- /dev/null +++ b/include/roscco/ros_to_oscc.h @@ -0,0 +1,42 @@ +#ifndef ROS_TO_OSCC_H +#define ROS_TO_OSCC_H + +extern "C" { +#include +} + +#include + +#include +#include +#include +#include + +class RosToOscc +{ +public: + RosToOscc(ros::NodeHandle* public_nh, ros::NodeHandle* private_nh); + + void brakeCommandCallback( + const roscco::BrakeCommand::ConstPtr& msg); + + void steeringCommandCallback( + const roscco::SteeringCommand::ConstPtr& msg); + + void throttleCommandCallback( + const roscco::ThrottleCommand::ConstPtr& msg); + + void enableDisableCallback( + const roscco::EnableDisable::ConstPtr& msg); + +private: + ros::Subscriber topic_brake_command_; + + ros::Subscriber topic_steering_command_; + + ros::Subscriber topic_throttle_command_; + + ros::Subscriber topic_enable_disable_command_; +}; + +#endif // ROS_TO_OSCC_H diff --git a/msg/BrakeCommand.msg b/msg/BrakeCommand.msg new file mode 100644 index 0000000..49a0fea --- /dev/null +++ b/msg/BrakeCommand.msg @@ -0,0 +1,2 @@ +Header header +float64 brake_position diff --git a/msg/BrakeReport.msg b/msg/BrakeReport.msg new file mode 100644 index 0000000..caf25dd --- /dev/null +++ b/msg/BrakeReport.msg @@ -0,0 +1,2 @@ +Header header +BrakeReportData data diff --git a/msg/BrakeReportData.msg b/msg/BrakeReportData.msg new file mode 100644 index 0000000..982ec37 --- /dev/null +++ b/msg/BrakeReportData.msg @@ -0,0 +1,5 @@ +uint8[2] magic +uint8 enabled +uint8 operator_override +uint8 dtcs +uint8[3] reserved diff --git a/msg/CanFrame.msg b/msg/CanFrame.msg new file mode 100644 index 0000000..93e96a4 --- /dev/null +++ b/msg/CanFrame.msg @@ -0,0 +1,2 @@ +Header header +CanFrameData frame diff --git a/msg/CanFrameData.msg b/msg/CanFrameData.msg new file mode 100644 index 0000000..1db4983 --- /dev/null +++ b/msg/CanFrameData.msg @@ -0,0 +1,6 @@ +uint32 can_id +uint8 can_dlc +uint8 pad +uint8 res0 +uint8 res1 +uint8[8] data diff --git a/msg/EnableDisable.msg b/msg/EnableDisable.msg new file mode 100644 index 0000000..da3829f --- /dev/null +++ b/msg/EnableDisable.msg @@ -0,0 +1,2 @@ +Header header +bool enable_control diff --git a/msg/FaultOriginId.msg b/msg/FaultOriginId.msg new file mode 100644 index 0000000..385298d --- /dev/null +++ b/msg/FaultOriginId.msg @@ -0,0 +1,3 @@ +int32 FaultOriginBrake=0 +int32 FaultOriginSteering=1 +int32 FaultOriginThrottle=2 diff --git a/msg/FaultReport.msg b/msg/FaultReport.msg new file mode 100644 index 0000000..9186941 --- /dev/null +++ b/msg/FaultReport.msg @@ -0,0 +1,2 @@ +Header header +FaultReportData data diff --git a/msg/FaultReportData.msg b/msg/FaultReportData.msg new file mode 100644 index 0000000..619a8b4 --- /dev/null +++ b/msg/FaultReportData.msg @@ -0,0 +1,3 @@ +uint8[2] magic +uint32 fault_origin_id +uint8[2] reserved diff --git a/msg/SteeringCommand.msg b/msg/SteeringCommand.msg new file mode 100644 index 0000000..7c99f8c --- /dev/null +++ b/msg/SteeringCommand.msg @@ -0,0 +1,2 @@ +Header header +float64 steering_torque diff --git a/msg/SteeringReport.msg b/msg/SteeringReport.msg new file mode 100644 index 0000000..2d4b07e --- /dev/null +++ b/msg/SteeringReport.msg @@ -0,0 +1,2 @@ +Header header +SteeringReportData data diff --git a/msg/SteeringReportData.msg b/msg/SteeringReportData.msg new file mode 100644 index 0000000..982ec37 --- /dev/null +++ b/msg/SteeringReportData.msg @@ -0,0 +1,5 @@ +uint8[2] magic +uint8 enabled +uint8 operator_override +uint8 dtcs +uint8[3] reserved diff --git a/msg/ThrottleCommand.msg b/msg/ThrottleCommand.msg new file mode 100644 index 0000000..2440579 --- /dev/null +++ b/msg/ThrottleCommand.msg @@ -0,0 +1,2 @@ +Header header +float64 throttle_position diff --git a/msg/ThrottleReport.msg b/msg/ThrottleReport.msg new file mode 100644 index 0000000..eb0b2ed --- /dev/null +++ b/msg/ThrottleReport.msg @@ -0,0 +1,2 @@ +Header header +ThrottleReportData data diff --git a/msg/ThrottleReportData.msg b/msg/ThrottleReportData.msg new file mode 100644 index 0000000..982ec37 --- /dev/null +++ b/msg/ThrottleReportData.msg @@ -0,0 +1,5 @@ +uint8[2] magic +uint8 enabled +uint8 operator_override +uint8 dtcs +uint8[3] reserved diff --git a/oscc b/oscc new file mode 160000 index 0000000..9107166 --- /dev/null +++ b/oscc @@ -0,0 +1 @@ +Subproject commit 910716600a1dbf5aa99f14db9a225e2611a6fd54 diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..905382c --- /dev/null +++ b/package.xml @@ -0,0 +1,54 @@ + + + roscco + 0.0.1 + A OSCC to ROS interface + + + + + PolySync + + + + + + MIT + + + + + + + + + + + Robert Brown + + + + + + + + + + + + + + catkin + message_generation + roscpp + std_msgs + roscpp + std_msgs + + + + + + + + diff --git a/src/oscc_to_ros.cpp b/src/oscc_to_ros.cpp new file mode 100644 index 0000000..a628019 --- /dev/null +++ b/src/oscc_to_ros.cpp @@ -0,0 +1,98 @@ +#include + +OsccToRos::OsccToRos(ros::NodeHandle* public_nh, ros::NodeHandle* private_nh) +{ + topic_brake_report_ = + public_nh->advertise("BrakeReport", 10); + + topic_steering_report_ = + public_nh->advertise("SteeringReport", 10); + + topic_throttle_report_ = + public_nh->advertise("ThrottleReport", 10); + + topic_fault_report_ = + public_nh->advertise("FaultReport", 10); + + topic_obd_messages_ = + public_nh->advertise("CanFrame", 10); + + oscc_subscribe_to_brake_reports(brake_callback); + + oscc_subscribe_to_steering_reports(steering_callback); + + oscc_subscribe_to_throttle_reports(throttle_callback); + + oscc_subscribe_to_fault_reports(fault_callback); + + oscc_subscribe_to_obd_messages(obd_callback); +} + +void OsccToRos::steering_callback(oscc_steering_report_s* report) +{ + cast_callback(report, &topic_steering_report_); +} + +void OsccToRos::brake_callback(oscc_brake_report_s* report) +{ + cast_callback(report, &topic_brake_report_); +} + +void OsccToRos::throttle_callback(oscc_throttle_report_s* report) +{ + cast_callback(report, &topic_throttle_report_); +} + +template +void OsccToRos::cast_callback(OSCCTYPE* report, ros::Publisher* pub) +{ + ROSMSGTYPE* ros_message(new ROSMSGTYPE); + + ROSDATATYPE* data = (ROSDATATYPE*)report; + + ros_message->data = *data; + + ros_message->header.stamp = ros::Time::now(); + + pub->publish(*ros_message); + + delete ros_message; +} + +void OsccToRos::fault_callback(oscc_fault_report_s* report) +{ + roscco::FaultReport* ros_message(new roscco::FaultReport); + + // ROS does not pack the structs so individual assignment is required over + // cast + ros_message->data.magic[0] = report->magic[0]; + ros_message->data.magic[1] = report->magic[1]; + ros_message->data.fault_origin_id = report->fault_origin_id; + ros_message->data.reserved[0] = report->reserved[0]; + ros_message->data.reserved[1] = report->reserved[1]; + + ros_message->header.stamp = ros::Time::now(); + + topic_fault_report_.publish(*ros_message); +} + +void OsccToRos::obd_callback(can_frame* frame) +{ + roscco::CanFrame* ros_message(new roscco::CanFrame); + + roscco::CanFrameData* data = (roscco::CanFrameData*)frame; + + ros_message->frame = *data; + + ros_message->header.stamp = ros::Time::now(); + + topic_obd_messages_.publish(*ros_message); + + delete ros_message; +} diff --git a/src/ros_to_oscc.cpp b/src/ros_to_oscc.cpp new file mode 100644 index 0000000..daa127f --- /dev/null +++ b/src/ros_to_oscc.cpp @@ -0,0 +1,84 @@ +#include + +RosToOscc::RosToOscc(ros::NodeHandle* public_nh, ros::NodeHandle* private_nh) +{ + topic_brake_command_ = public_nh->subscribe( + "BrakeCommand", 10, &RosToOscc::brakeCommandCallback, this); + + topic_steering_command_ = public_nh->subscribe( + "SteeringCommand", 10, &RosToOscc::steeringCommandCallback, this); + + topic_throttle_command_ = public_nh->subscribe( + "ThrottleCommand", 10, &RosToOscc::throttleCommandCallback, this); + + topic_enable_disable_command_ = public_nh->subscribe( + "EnableDisable", 10, &RosToOscc::enableDisableCallback, this); +}; + +void RosToOscc::brakeCommandCallback( + const roscco::BrakeCommand::ConstPtr& msg) +{ + oscc_result_t ret = OSCC_ERROR; + + ret = oscc_publish_brake_position( msg->brake_position ); + + if( ret == OSCC_ERROR ) + { + ROS_ERROR("OSCC_ERROR occured while trying send the brake position."); + } + else if( ret == OSCC_WARNING ) + { + ROS_WARN("OSCC_WARNING occured while trying send the brake position."); + } +}; + +void RosToOscc::steeringCommandCallback( + const roscco::SteeringCommand::ConstPtr& msg) +{ + oscc_result_t ret = OSCC_ERROR; + + ret = oscc_publish_steering_torque( msg->steering_torque ); + + if( ret == OSCC_ERROR ) + { + ROS_ERROR("OSCC_ERROR occured while trying send the steering torque."); + } + else if( ret == OSCC_WARNING ) + { + ROS_WARN("OSCC_WARNING occured while trying send the steering torque."); + } +}; + +void RosToOscc::throttleCommandCallback( + const roscco::ThrottleCommand::ConstPtr& msg) +{ + oscc_result_t ret = OSCC_ERROR; + + ret = oscc_publish_throttle_position( msg->throttle_position ); + + if( ret == OSCC_ERROR ) + { + ROS_ERROR("OSCC_ERROR occured while trying send the throttle position."); + } + else if( ret == OSCC_WARNING ) + { + ROS_WARN("OSCC_WARNING occured while trying send the throttle position."); + } +}; + +void RosToOscc::enableDisableCallback( + const roscco::EnableDisable::ConstPtr& msg) +{ + oscc_result_t ret = OSCC_ERROR; + + ret = msg->enable_control ? oscc_enable() : oscc_disable(); + + if( ret == OSCC_ERROR ) + { + ROS_ERROR("OSCC_ERROR occured while trying to enable or disable control."); + } + else if( ret == OSCC_WARNING ) + { + ROS_WARN("OSCC_WARNING occured while trying to enable or disable control."); + } +} diff --git a/src/roscco_node.cpp b/src/roscco_node.cpp new file mode 100644 index 0000000..b3b3ba2 --- /dev/null +++ b/src/roscco_node.cpp @@ -0,0 +1,39 @@ +#include + +extern "C" { +#include +} + +#include + +#include +#include + +int main(int argc, char* argv[]) +{ + ros::init(argc, argv, "roscco_node"); + + ros::NodeHandle public_nh; + ros::NodeHandle private_nh("~"); + + int can_channel; + private_nh.param("can_channel", can_channel, 0); + + oscc_result_t ret = OSCC_ERROR; + + ret = oscc_open( can_channel ); + + if(ret != OSCC_OK ) + { + ROS_ERROR("Could not initialize OSCC"); + } + + RosToOscc subcriber(&public_nh, &private_nh); + OsccToRos publisher(&public_nh, &private_nh); + + ros::spin(); + + ret = oscc_close( 0 ); + + ros::waitForShutdown(); +} diff --git a/test/include/oscc.h b/test/include/oscc.h new file mode 100644 index 0000000..9dbeb81 --- /dev/null +++ b/test/include/oscc.h @@ -0,0 +1,58 @@ +#ifndef _OSCC_H +#define _OSCC_H + +#include + +#include "brake_can_protocol.h" +#include "fault_can_protocol.h" +#include "steering_can_protocol.h" +#include "throttle_can_protocol.h" + +typedef enum { OSCC_OK, OSCC_ERROR, OSCC_WARNING } oscc_result_t; + +oscc_result_t oscc_publish_steering_torque(double input); + +oscc_result_t oscc_publish_brake_position(double input); + +oscc_result_t oscc_publish_throttle_position(double input); + +oscc_result_t oscc_enable(void); + +oscc_result_t oscc_disable(void); + +oscc_result_t +oscc_subscribe_to_brake_reports(void (*callback)(oscc_brake_report_s* report)); + +oscc_result_t oscc_subscribe_to_throttle_reports( + void (*callback)(oscc_throttle_report_s* report)); + +oscc_result_t oscc_subscribe_to_steering_reports( + void (*callback)(oscc_steering_report_s* report)); + +oscc_result_t +oscc_subscribe_to_fault_reports(void (*callback)(oscc_fault_report_s* report)); + +oscc_result_t +oscc_subscribe_to_obd_messages(void (*callback)(struct can_frame* frame)); + +// Expose the Callback for testing API +void (*steering_report_callback)(oscc_steering_report_s* report); + +void (*brake_report_callback)(oscc_brake_report_s* report); + +void (*throttle_report_callback)(oscc_throttle_report_s* report); + +void (*fault_report_callback)(oscc_fault_report_s* report); + +void (*obd_frame_callback)(struct can_frame* frame); + +// Expose variables that are set for validating API call +double get_brake_command(); + +double get_steering_torque(); + +double get_throttle_command(); + +int get_enable_disable(); + +#endif /* _OSCC_H */ diff --git a/test/oscc_fixture.c b/test/oscc_fixture.c new file mode 100644 index 0000000..c6d9d97 --- /dev/null +++ b/test/oscc_fixture.c @@ -0,0 +1,84 @@ +#include + +static double steering_torque; +static double brake_command; +static double throttle_command; +int enabled; + +double get_brake_command() { return brake_command; } + +double get_steering_torque() { return steering_torque; } + +double get_throttle_command() { return throttle_command; } + +int get_enable_disable() { return enabled; } + +oscc_result_t oscc_publish_steering_torque(double input) +{ + steering_torque = input; + + return OSCC_OK; +} + +oscc_result_t oscc_publish_brake_position(double input) +{ + brake_command = input; + + return OSCC_OK; +} + +oscc_result_t oscc_publish_throttle_position(double input) +{ + throttle_command = input; + + return OSCC_OK; +} + +oscc_result_t oscc_enable(void) +{ + ++enabled; + + return OSCC_OK; +} + +oscc_result_t oscc_disable(void) +{ + enabled = 0; + + return OSCC_OK; +} + +oscc_result_t +oscc_subscribe_to_brake_reports(void (*callback)(oscc_brake_report_s* report)) +{ + brake_report_callback = callback; + return OSCC_OK; +} + +oscc_result_t oscc_subscribe_to_throttle_reports( + void (*callback)(oscc_throttle_report_s* report)) +{ + throttle_report_callback = callback; + return OSCC_OK; +} + +oscc_result_t oscc_subscribe_to_steering_reports( + void (*callback)(oscc_steering_report_s* report)) +{ + steering_report_callback = callback; + return OSCC_OK; +} + +oscc_result_t +oscc_subscribe_to_fault_reports(void (*callback)(oscc_fault_report_s* report)) +{ + fault_report_callback = callback; + return OSCC_OK; +} + +oscc_result_t +oscc_subscribe_to_obd_messages(void (*callback)(struct can_frame* frame)) +{ + obd_frame_callback = callback; + return OSCC_OK; +} diff --git a/test/rapidcheck/include/rapidcheck.h b/test/rapidcheck/include/rapidcheck.h new file mode 100644 index 0000000..6e2fb59 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck.h @@ -0,0 +1,37 @@ +#pragma once + +// MSVC HACK - Undefine stupid macros MS macros +#undef min +#undef max + +#include "rapidcheck/Seq.h" +#include "rapidcheck/seq/Create.h" +#include "rapidcheck/seq/Operations.h" +#include "rapidcheck/seq/SeqIterator.h" +#include "rapidcheck/seq/Transform.h" + +#include "rapidcheck/Shrinkable.h" +#include "rapidcheck/shrinkable/Create.h" +#include "rapidcheck/shrinkable/Operations.h" +#include "rapidcheck/shrinkable/Transform.h" + +#include "rapidcheck/Gen.h" +#include "rapidcheck/gen/Arbitrary.h" +#include "rapidcheck/gen/Build.h" +#include "rapidcheck/gen/Chrono.h" +#include "rapidcheck/gen/Container.h" +#include "rapidcheck/gen/Create.h" +#include "rapidcheck/gen/Exec.h" +#include "rapidcheck/gen/Maybe.h" +#include "rapidcheck/gen/Numeric.h" +#include "rapidcheck/gen/Predicate.h" +#include "rapidcheck/gen/Select.h" +#include "rapidcheck/gen/Text.h" +#include "rapidcheck/gen/Transform.h" +#include "rapidcheck/gen/Tuple.h" + +#include "rapidcheck/Assertions.h" +#include "rapidcheck/Check.h" +#include "rapidcheck/Classify.h" +#include "rapidcheck/Log.h" +#include "rapidcheck/Show.h" diff --git a/test/rapidcheck/include/rapidcheck/Assertions.h b/test/rapidcheck/include/rapidcheck/Assertions.h new file mode 100644 index 0000000..248f95c --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Assertions.h @@ -0,0 +1,97 @@ +#pragma once + +#include "rapidcheck/detail/Results.h" +#include "rapidcheck/detail/Capture.h" + +#define RC_INTERNAL_CONDITIONAL_RESULT( \ + ResultType, expression, invert, name, ...) \ + doAssert(RC_INTERNAL_CAPTURE(expression), \ + (invert), \ + ::rc::detail::CaseResult::Type::ResultType, \ + __FILE__, \ + __LINE__, \ + name "(" #expression ")") + +#define RC_INTERNAL_STRINGIFY(x) #x + +#define RC_INTERNAL_UNCONDITIONAL_RESULT(ResultType, name, expression) \ + do { \ + throw ::rc::detail::CaseResult( \ + ::rc::detail::CaseResult::Type::ResultType, \ + ::rc::detail::makeMessage( \ + __FILE__, __LINE__, name "(" #expression ")", {expression})); \ + } while (false) + +/// Fails the current test case unless the given condition is `true`. +#define RC_ASSERT(expression) \ + RC_INTERNAL_CONDITIONAL_RESULT(Failure, expression, true, "RC_ASSERT", ) + +/// Fails the current test case unless the given condition is `false`. +#define RC_ASSERT_FALSE(expression) \ + RC_INTERNAL_CONDITIONAL_RESULT(Failure, \ + expression, \ + false, \ + "RC_ASSERT_" \ + "FALSE") + +/// Fails the current test case unless the provided expression throws an +/// exception of any type +#define RC_ASSERT_THROWS(expression) \ + do { \ + try { \ + expression; \ + } catch (...) { \ + break; \ + } \ + throw ::rc::detail::CaseResult( \ + ::rc::detail::CaseResult::Type::Failure, \ + ::rc::detail::makeUnthrownExceptionMessage( \ + __FILE__, __LINE__, "RC_ASSERT_THROWS(" #expression ")")); \ + } while (false) + +/// Fails the current test case unless the given expression throws an +/// exception that matches the given exception type +#define RC_ASSERT_THROWS_AS(expression, ExceptionType) \ + do { \ + try { \ + expression; \ + } catch (const ExceptionType &) { \ + break; \ + } catch (...) { \ + throw ::rc::detail::CaseResult(::rc::detail::CaseResult::Type::Failure, \ + ::rc::detail::makeWrongExceptionMessage( \ + __FILE__, \ + __LINE__, \ + "RC_ASSERT_THROWS_AS(" #expression \ + ", " #ExceptionType ")", \ + #ExceptionType)); \ + } \ + throw ::rc::detail::CaseResult(::rc::detail::CaseResult::Type::Failure, \ + ::rc::detail::makeUnthrownExceptionMessage( \ + __FILE__, \ + __LINE__, \ + "RC_ASSERT_THROWS_AS(" #expression \ + ", " #ExceptionType ")")); \ + } while (false) + +/// Unconditionally fails the current test case with the given message. +#define RC_FAIL(...) \ + RC_INTERNAL_UNCONDITIONAL_RESULT(Failure, "RC_FAIL", __VA_ARGS__) + +/// Succeed if the given condition is true. +#define RC_SUCCEED_IF(expression) \ + RC_INTERNAL_CONDITIONAL_RESULT(Success, expression, false, "RC_SUCCEED_IF") + +/// Unconditionally succeed with the given message. +#define RC_SUCCEED(...) \ + RC_INTERNAL_UNCONDITIONAL_RESULT(Success, "RC_SUCCEED", __VA_ARGS__) + +/// Discards the current test case if the given condition is false. +#define RC_PRE(expression) \ + RC_INTERNAL_CONDITIONAL_RESULT(Discard, expression, true, "RC_PRE", !) + +/// Discards the current test case with the given description. +#define RC_DISCARD(...) \ + RC_INTERNAL_UNCONDITIONAL_RESULT(Discard, "RC_DISCARD", __VA_ARGS__) + +#include "Assertions.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Assertions.hpp b/test/rapidcheck/include/rapidcheck/Assertions.hpp new file mode 100644 index 0000000..b935905 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Assertions.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace rc { +namespace detail { + +std::string makeMessage(const std::string &file, + int line, + const std::string &assertion, + const std::string &extra = ""); + +std::string makeExpressionMessage(const std::string &file, + int line, + const std::string &assertion, + const std::string &expansion); + +std::string makeUnthrownExceptionMessage(const std::string &file, + int line, + const std::string &assertion); + +std::string makeWrongExceptionMessage(const std::string &file, + int line, + const std::string &assertion, + const std::string &expected); + +template +void doAssert(const Expression &expression, + bool expectedResult, + CaseResult::Type type, + const std::string &file, + int line, + const std::string &assertion) { + if (static_cast(expression.value()) != expectedResult) { + std::ostringstream ss; + expression.show(ss); + throw CaseResult(type, + makeExpressionMessage(file, line, assertion, ss.str())); + } +} + +} // namespace detail +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/BeforeMinimalTestCase.h b/test/rapidcheck/include/rapidcheck/BeforeMinimalTestCase.h new file mode 100644 index 0000000..b583084 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/BeforeMinimalTestCase.h @@ -0,0 +1,11 @@ +#pragma once + +namespace rc { + +/// This is called before the final minimal test case is run when a property +/// fails. Set a breakpoint here if you want to debug that test case. When the +/// debugger breaks here, you can set up any further breakpoints or other tools +/// before the test case is actually run. +void beforeMinimalTestCase(); + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Check.h b/test/rapidcheck/include/rapidcheck/Check.h new file mode 100644 index 0000000..1cb355f --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Check.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace rc { + +/// Checks the given testable and returns `true` on success and `false` on +/// failure. This method will also print information about the testing to +/// stderr. +template +bool check(Testable &&testable); + +/// Same as `check(Testable &&)` but also takes a description of the property +/// that is being tested as the first parameter. This will be used in the +/// output. +template +bool check(const std::string &description, Testable &&testable); + +} // namespace rc + +#include "Check.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Check.hpp b/test/rapidcheck/include/rapidcheck/Check.hpp new file mode 100644 index 0000000..9d75347 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Check.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include "rapidcheck/detail/Configuration.h" +#include "rapidcheck/detail/Results.h" +#include "rapidcheck/detail/Property.h" +#include "rapidcheck/detail/TestListener.h" + +namespace rc { +namespace detail { + +TestResult +checkProperty(const Property &property, + const TestMetadata &metadata, + const TestParams ¶ms, + TestListener &listener, + const std::unordered_map &reproduceMap); + +TestResult checkProperty(const Property &property, + const TestMetadata &metadata, + const TestParams ¶ms, + TestListener &listener); + +TestResult checkProperty(const Property &property, + const TestMetadata &metadata, + const TestParams ¶ms); + +TestResult checkProperty(const Property &property, + const TestMetadata &metadata); + +// Uses defaults from configuration +TestResult checkProperty(const Property &property); + +template +TestResult checkTestable(Testable &&testable, Args &&... args) { + return checkProperty(toProperty(std::forward(testable)), + std::forward(args)...); +} + +} // namespace detail + +template +bool check(Testable &&testable) { + return check(std::string(), std::forward(testable)); +} + +template +bool check(const std::string &description, Testable &&testable) { + using namespace rc::detail; + + // Force loading of the configuration so that message comes _before_ the + // description + configuration(); + + if (!description.empty()) { + std::cerr << std::endl << "- " << description << std::endl; + } + + TestMetadata metadata; + metadata.id = description; + metadata.description = description; + const auto result = + detail::checkTestable(std::forward(testable), metadata); + + printResultMessage(result, std::cerr); + std::cerr << std::endl; + + return result.template is(); +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Classify.h b/test/rapidcheck/include/rapidcheck/Classify.h new file mode 100644 index 0000000..71f9ce0 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Classify.h @@ -0,0 +1,17 @@ +#pragma once + +/// Tags the current test case if the specified condition is true. If any tags +/// are specified after the condition, those will be used. Otherwise, a string +/// version of the condition itself will be used as the tag. +#define RC_CLASSIFY(condition, ...) \ + do { \ + if (condition) { \ + ::rc::detail::classify(#condition, {__VA_ARGS__}); \ + } \ + } while (false) + +/// Tags the current test case with the given values which will be converted to +/// strings. +#define RC_TAG(...) ::rc::detail::tag({__VA_ARGS__}) + +#include "Classify.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Classify.hpp b/test/rapidcheck/include/rapidcheck/Classify.hpp new file mode 100644 index 0000000..68944d9 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Classify.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace rc { +namespace detail { + +struct Stringified { + template + Stringified(const T &value) + : str(toString(value)) {} + std::string str; +}; + +void tag(std::initializer_list tags); +void classify(std::string condition, std::initializer_list tags); + +} // namespace detail +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Gen.h b/test/rapidcheck/include/rapidcheck/Gen.h new file mode 100644 index 0000000..43e35e6 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Gen.h @@ -0,0 +1,74 @@ +#pragma once + +#include "rapidcheck/Traits.h" +#include "rapidcheck/Shrinkable.h" + +namespace rc { + +class Random; + +/// The reference size. This is not a max limit on the generator size parameter +/// but serves as a guideline. In general, genenerators for which there is a +/// natural limit which is not too expensive to generate should max out at this. +/// This applies to, for example, generation of numbers but not to the +/// generation of collection where there is an associated cost to generating +/// large collections. +constexpr int kNominalSize = 100; + +/// This class is the type of RapidCheck generators. A generator is essentially +/// a function which takes a `Random` and some generation parameters and returns +/// a randomly generated `Shrinkable`. This class has value semantics. +/// +/// A generator can be created from any type which has the following: +/// - A method `Shrinkable operator()(const Random &, int) const` +/// - A copy constructor which produces a semantically identical object. +template +class Gen { +public: + /// The type of the values generated by this generator. + using ValueType = T; + + template , Gen>::value>::type> + Gen(Impl &&impl); + + /// Returns the name of this generator. + std::string name() const; + + /// Returns a `Shrinkable` generated used the given parameters. + /// + /// @param random The random generator + /// @param size The generation size + /// + /// @return a random generated `Shrinkable` + Shrinkable operator()(const Random &random, int size = kNominalSize) const + noexcept; + + /// The meaning of this operator depends on the context in which it is used + /// but mainly, it is used when creating a generator using `gen::exec` to + /// pick a value in an impure context. + T operator*() const; + + /// Returns a generator identical to this one but with a different name. + Gen as(const std::string &name) const; + + Gen(const Gen &other) noexcept; + Gen &operator=(const Gen &rhs) noexcept; + Gen(Gen &&other) noexcept; + Gen &operator=(Gen &&rhs) noexcept; + ~Gen() noexcept; + +private: + class IGenImpl; + + template + class GenImpl; + + IGenImpl *m_impl; + std::string m_name; +}; + +} // namespace rc + +#include "Gen.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Gen.hpp b/test/rapidcheck/include/rapidcheck/Gen.hpp new file mode 100644 index 0000000..5adf074 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Gen.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include + +#include "rapidcheck/detail/Any.h" +#include "rapidcheck/detail/ImplicitParam.h" +#include "rapidcheck/gen/detail/GenerationHandler.h" +#include "rapidcheck/shrinkable/Create.h" + +namespace rc { +namespace gen { + +// Forward declare this so we don't need to include Transform.h +template +Gen::type>> map(Gen gen, + Mapper &&mapper); + +} // namespace gen + +template +class Gen::IGenImpl { +public: + virtual Shrinkable generate(const Random &random, int size) const = 0; + virtual void retain() = 0; + virtual void release() = 0; + virtual ~IGenImpl() = default; +}; + +template +template +class Gen::GenImpl : public IGenImpl { +public: + template + GenImpl(Args &&... args) + : m_impl(std::forward(args)...) + , m_count(1) {} + + Shrinkable generate(const Random &random, int size) const override { + return m_impl(random, size); + } + + void retain() override { m_count.fetch_add(1L); } + + void release() override { + if (m_count.fetch_sub(1L) == 1L) { + delete this; + } + } + +private: + const Impl m_impl; + std::atomic m_count; +}; + +template +template +Gen::Gen(Impl &&impl) + : m_impl(new GenImpl>(std::forward(impl))) {} + +template +std::string Gen::name() const { + return m_name; +} + +template +Shrinkable Gen::operator()(const Random &random, int size) const + noexcept { + try { + return m_impl->generate(random, size); + } catch (...) { + auto exception = std::current_exception(); + return shrinkable::lambda([=]() -> T { + std::rethrow_exception(exception); + + // MSVC HACK: the following is required for MSVC to stop complaining + // about missing return value. Will never be reached, of + // course. +#ifdef _MSC_VER + throw nullptr; +#endif // _MSC_VER + }); + } +} + +template +T Gen::operator*() const { + using namespace detail; + using rc::gen::detail::param::CurrentHandler; + const auto handler = ImplicitParam::value(); + return std::move(handler->onGenerate(gen::map(*this, &Any::of).as(m_name)) + .template get()); +} + +template +Gen Gen::as(const std::string &name) const { + auto gen = *this; + gen.m_name = name; + return gen; +} + +template +Gen::Gen(const Gen &other) noexcept : m_impl(other.m_impl), + m_name(other.m_name) { + m_impl->retain(); +} + +template +Gen &Gen::operator=(const Gen &rhs) noexcept { + rhs.m_impl->retain(); + if (m_impl) { + m_impl->release(); + } + m_impl = rhs.m_impl; + m_name = rhs.m_name; + return *this; +} + +template +Gen::Gen(Gen &&other) noexcept : m_impl(other.m_impl), + m_name(std::move(other.m_name)) { + other.m_impl = nullptr; +} + +template +Gen &Gen::operator=(Gen &&rhs) noexcept { + if (m_impl) { + m_impl->release(); + } + m_impl = rhs.m_impl; + rhs.m_impl = nullptr; + m_name = std::move(rhs.m_name); + return *this; +} + +template +Gen::~Gen() noexcept { + if (m_impl) { + m_impl->release(); + } +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/GenerationFailure.h b/test/rapidcheck/include/rapidcheck/GenerationFailure.h new file mode 100644 index 0000000..267bbf4 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/GenerationFailure.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace rc { + +/// Thrown to indicate that an appropriate value couldn't be generated. +class GenerationFailure : public std::runtime_error { +public: + explicit GenerationFailure(std::string msg); +}; + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Log.h b/test/rapidcheck/include/rapidcheck/Log.h new file mode 100644 index 0000000..cbf22c0 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Log.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +/// Logs additional information about the run of a test case. Can be used either +/// like a stream (`RC_LOG() << "foobar"`) or taking a string +/// (`RC_LOG("foobar")`). When using the latter form, a newline will be appended +/// automatically. +#define RC_LOG(...) ::rc::detail::log(__VA_ARGS__) + +#include "Log.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Log.hpp b/test/rapidcheck/include/rapidcheck/Log.hpp new file mode 100644 index 0000000..c40d4ed --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Log.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace rc { +namespace detail { + +/// Returns the current logging stream. +std::ostream &log(); + +/// Logs the given message. +void log(const std::string &msg); + +} // namespace detail +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Maybe.h b/test/rapidcheck/include/rapidcheck/Maybe.h new file mode 100644 index 0000000..d62aaa9 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Maybe.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include "rapidcheck/Nothing.h" + +namespace rc { + +/// Represents the presence of a value or nothing at all. Like `Maybe` in +/// Haskell, `Option` in Scala or `optional` in Boost. But we're not Haskell nor +/// Scala and we don't depend on Boost so we have our own. +template +class Maybe { +public: + /// The type of value contained in this `Maybe`. + using ValueType = T; + + /// Constructs a new empty `Maybe`. + Maybe() noexcept; + + /// Equivalent to default construction. + Maybe(NothingType) noexcept; + + /// Constructs a new `Maybe` and copy-constructs its value. + Maybe(const T &value); + + /// Constructs a new `Maybe` and move-constructs its value. + Maybe(T &&value); + + /// Constructs a new `Maybe` and copy-constructs or copy-assigns its value. + Maybe &operator=(const T &value); + + /// Constructs a new `Maybe` and move-constructs or move-assigns its value. + Maybe &operator=(T &&value); + + /// Equivalent to `reset()`. + Maybe &operator=(NothingType); + + /// Initializes this `Maybe` with an in-place constructed value that gets + /// forwarded the given arguments. If this `Maybe` is already initialized, + /// old value gets destroyed. If the constructor of the new value throws an + /// exception, this `Maybe` will be uninitialized on return. + template + void init(Args &&... args); + + /// Resets this `Maybe`, destroying the contained object. + void reset(); + + /// Returns a reference to the contained value. No checking is performed. + T &operator*() &; + + /// Returns a const reference to the contained value. No checking is + /// performed. + const T &operator*() const &; + + /// Returns an rvalue reference to the contained value. No checking is + /// performed. + T &&operator*() &&; + + /// Returns a pointer to the contained value. No checking is performed. + T *operator->(); + + /// Returns a const pointer to the contained value. No checking is + /// performed. + const T *operator->() const; + + /// Returns true if this `Maybe` is initialized. + explicit operator bool() const; + + Maybe(const Maybe &other) noexcept( + std::is_nothrow_copy_constructible::value); + Maybe &operator=(const Maybe &rhs) noexcept( + std::is_nothrow_copy_constructible::value + &&std::is_nothrow_copy_assignable::value); + + Maybe(Maybe &&other) noexcept(std::is_nothrow_move_constructible::value); + Maybe &operator=(Maybe &&rhs) noexcept(std::is_nothrow_move_constructible< + T>::value &&std::is_nothrow_move_assignable::value); + + ~Maybe(); + +private: + using Storage = typename std::aligned_storage::type; + Storage m_storage; + bool m_initialized; +}; + +template +bool operator==(const Maybe &lhs, const Maybe &rhs); + +template +bool operator!=(const Maybe &lhs, const Maybe &rhs); + +template +std::ostream &operator<<(std::ostream &os, const Maybe &value); + +} // namespace rc + +#include "Maybe.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Maybe.hpp b/test/rapidcheck/include/rapidcheck/Maybe.hpp new file mode 100644 index 0000000..b7e4778 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Maybe.hpp @@ -0,0 +1,174 @@ +#pragma once + +#include "rapidcheck/Show.h" + +namespace rc { + +template +Maybe::Maybe() noexcept : m_initialized(false) {} + +template +Maybe::Maybe(NothingType) noexcept : Maybe() {} + +template +Maybe::Maybe(const T &value) + : m_initialized(false) { + init(value); +} + +template +Maybe::Maybe(T &&value) + : m_initialized(false) { + init(std::move(value)); +} + +template +Maybe &Maybe::operator=(const T &value) { + if (m_initialized) { + **this = value; + } else { + init(value); + } + return *this; +} + +template +Maybe &Maybe::operator=(T &&value) { + if (m_initialized) { + **this = std::move(value); + } else { + init(std::move(value)); + } + return *this; +} + +template +Maybe &Maybe::operator=(NothingType) { + reset(); + return *this; +} + +template +template +void Maybe::init(Args &&... args) { + reset(); + new (&m_storage) T(std::forward(args)...); + m_initialized = true; +} + +template +void Maybe::reset() { + if (m_initialized) { + m_initialized = false; + (**this).~T(); + } +} + +template +T &Maybe::operator*() & { + return *reinterpret_cast(&m_storage); +} + +template +const T &Maybe::operator*() const & { + return *reinterpret_cast(&m_storage); +} + +template +T &&Maybe::operator*() && { + return std::move(*reinterpret_cast(&m_storage)); +} + +template +T *Maybe::operator->() { + return reinterpret_cast(&m_storage); +} + +template +const T *Maybe::operator->() const { + return reinterpret_cast(&m_storage); +} + +template +Maybe::operator bool() const { + return m_initialized; +} + +template +Maybe::Maybe(const Maybe &other) noexcept( + std::is_nothrow_copy_constructible::value) + : m_initialized(false) { + if (other.m_initialized) { + init(*other); + } +} + +template +Maybe &Maybe::operator=(const Maybe &rhs) noexcept( + std::is_nothrow_copy_constructible::value + &&std::is_nothrow_copy_assignable::value) { + if (rhs.m_initialized) { + *this = *rhs; + } else { + reset(); + } + + return *this; +} + +template +Maybe::Maybe(Maybe &&other) noexcept( + std::is_nothrow_move_constructible::value) + : m_initialized(false) { + if (other.m_initialized) { + init(std::move(*other)); + } +} + +template +Maybe &Maybe:: +operator=(Maybe &&rhs) noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value) { + if (rhs.m_initialized) { + *this = std::move(*rhs); + } else { + reset(); + } + + return *this; +} + +template +Maybe::~Maybe() { + if (m_initialized) { + (**this).~T(); + } +} + +template +bool operator==(const Maybe &lhs, const Maybe &rhs) { + if (!lhs && !rhs) { + return true; + } else if (lhs && rhs) { + return *lhs == *rhs; + } + + return false; +} + +template +bool operator!=(const Maybe &lhs, const Maybe &rhs) { + return !(lhs == rhs); +} + +template +std::ostream &operator<<(std::ostream &os, const Maybe &value) { + if (value) { + show(*value, os); + } else { + os << "Nothing"; + } + return os; +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Nothing.h b/test/rapidcheck/include/rapidcheck/Nothing.h new file mode 100644 index 0000000..c20c30e --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Nothing.h @@ -0,0 +1,15 @@ +#pragma once + +namespace rc { + +/// Tag struct that can be used to construct different types to an uninitialized +/// or empty state. +struct NothingType { + /// Explicit conversion to false. + explicit operator bool() const { return false; } +}; + +/// Singleton NothingType value. +constexpr NothingType Nothing = NothingType(); + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Random.h b/test/rapidcheck/include/rapidcheck/Random.h new file mode 100644 index 0000000..e510673 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Random.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +namespace rc { + +/// Implementation of a splittable random generator as described in: +/// Claessen, K. och Palka, M. (2013) Splittable Pseudorandom Number +/// Generators using Cryptographic Hashing. +class Random { + friend bool operator==(const Random &lhs, const Random &rhs); + friend bool operator<(const Random &lhs, const Random &rhs); + friend std::ostream &operator<<(std::ostream &os, const Random &random); + + template + friend Iterator serialize(const Random &random, Iterator output); + + template + friend Iterator deserialize(Iterator begin, Iterator end, Random &output); + +public: + /// Key type + using Key = std::array; + + /// Type of a generated random number. + using Number = uint64_t; + + /// Constructs a Random engine with a `{0, 0, 0, 0}` key. + Random(); + + /// Constructs a Random engine from a full size 256-bit key. + Random(const Key &key); + + /// Constructs a Random engine from a 64-bit seed. + Random(uint64_t seed); + + /// Splits this generator into to separate independent generators. The first + /// generator will be assigned to this one and the second will be returned. + Random split(); + + /// Returns the next random number. Both `split` and `next` should not be + /// called on the same state. + Number next(); + +private: + using Block = std::array; + + using Bits = uint64_t; + static constexpr auto kBits = std::numeric_limits::digits; + + using Counter = uint64_t; + static constexpr auto kCounterMax = std::numeric_limits::max(); + + void append(bool x); + void mash(Block &output); + + Block m_key; + Block m_block; + Bits m_bits; + Counter m_counter; + uint8_t m_bitsi; +}; + +bool operator!=(const Random &lhs, const Random &rhs); + +} // namespace rc + +namespace std { + +template <> +struct hash { + using argument_type = rc::Random; + using result_type = std::size_t; + + std::size_t operator()(const rc::Random &r) const { + return static_cast(rc::Random(r).next()); + } +}; + +} // namespace std + +#include "Random.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Random.hpp b/test/rapidcheck/include/rapidcheck/Random.hpp new file mode 100644 index 0000000..a2559b8 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Random.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "rapidcheck/detail/Serialization.h" + +namespace rc { + +template +Iterator serialize(const Random &random, Iterator output) { + using namespace rc::detail; + auto oit = output; + oit = serializeN(begin(random.m_key), random.m_key.size(), oit); + oit = serializeCompact(random.m_bits, oit); + oit = serializeCompact(random.m_counter, oit); + *oit = random.m_bitsi; + return ++oit; +} + +template +Iterator deserialize(Iterator begin, Iterator end, Random &output) { + using namespace rc::detail; + auto iit = begin; + + iit = deserializeN( + iit, end, output.m_key.size(), output.m_key.begin()); + iit = deserializeCompact(iit, end, output.m_bits); + + Random::Counter counter; + iit = deserializeCompact(iit, end, counter); + // Normally, the block is calculated lazily if counter is divisible by 4 so + // let's simulate this. + if (counter != 0) { + const auto blki = + ((counter - 1) % std::tuple_size::value) + 1; + if (blki != 0) { + // Calculate the block as if counter % 4 == 0 + output.m_counter = counter - blki; + output.mash(output.m_block); + } + } + output.m_counter = counter; + + output.m_bitsi = *iit; + return ++iit; +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Seq.h b/test/rapidcheck/include/rapidcheck/Seq.h new file mode 100644 index 0000000..aea770b --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Seq.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include "rapidcheck/Nothing.h" +#include "rapidcheck/Maybe.h" +#include "rapidcheck/Traits.h" + +namespace rc { + +/// This class implements lazy sequences, or streams if you will. This is +/// mainly used by RapidCheck to implement shrinking where it is not feasible to +/// materialize all of the possible shrinks at once. In particular, a Seq may be +/// infinite although that's not appropriate for shrinking, of course! +/// +/// A `Seq` object is constructed either as an empty sequence using the default +/// constructor or with an implementation object that implements the actual +/// sequence. +/// +/// The implementation class must meet the following requirements: +/// - It must provide a method `Maybe operator()()` (i.e. it must be a +/// functor) which returns the next value or nothing if there are no more +/// values. If this method throws, it is treated the same as `Nothing`. +/// - It must have a copy constructor that produces a semantically identical +/// copy. This means that it should provide equal values to the original. +/// +/// However, unless you have a reason to create your own implementation class, +/// you should just use the provided combinators in the `rc::seq` namespace to +/// construct your `Seq`s. +template +class Seq { + /// Creates a new `Seq` using the implementation class specificed by the + /// type parameter constructed by forwarding the given arguments. + template + friend Seq::type::ValueType> + makeSeq(Args &&... args); + +public: + /// The type of the values of this `Seq`. + using ValueType = T; + + /// Constructs an empty `Seq` that has no values. + Seq() noexcept = default; + + /// Equivalent to default constructor. + Seq(NothingType) noexcept; + + /// Constructs a `Seq` from the given implementation object. + template , Seq>::value>::type> + explicit Seq(Impl &&impl); + + /// Returns the next value. + Maybe next() noexcept; + + Seq(const Seq &other); + Seq &operator=(const Seq &rhs); + Seq(Seq &&other) noexcept = default; + Seq &operator=(Seq &&rhs) noexcept = default; + +private: + class ISeqImpl; + + template + class SeqImpl; + + std::unique_ptr m_impl; +}; + +/// Two `Seq`s are considered equal if they return equal values. Note that this +/// requires either copying or moving of the `Seq`s. +template +bool operator==(Seq lhs, Seq rhs); + +template +bool operator!=(Seq lhs, Seq rhs); + +template +std::ostream &operator<<(std::ostream &os, Seq seq); + +} // namespace rc + +#include "Seq.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Seq.hpp b/test/rapidcheck/include/rapidcheck/Seq.hpp new file mode 100644 index 0000000..0ccb814 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Seq.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "rapidcheck/Show.h" + +namespace rc { + +template +class Seq::ISeqImpl { +public: + virtual Maybe next() = 0; + virtual std::unique_ptr copy() const = 0; + virtual ~ISeqImpl() = default; +}; + +template +template +class Seq::SeqImpl : public Seq::ISeqImpl { +public: + template + SeqImpl(Args &&... args) + : m_impl(std::forward(args)...) {} + + Maybe next() override { return m_impl(); } + + std::unique_ptr copy() const override { + return std::unique_ptr(new SeqImpl(*this)); + } + +private: + Impl m_impl; +}; + +template +Seq::Seq(NothingType) noexcept {} + +template +template +Seq::Seq(Impl &&impl) + : m_impl(new SeqImpl>(std::forward(impl))) {} + +template +Maybe Seq::next() noexcept { + try { + return m_impl ? m_impl->next() : Nothing; + } catch (...) { + m_impl.reset(); + return Nothing; + } +} + +template +Seq::Seq(const Seq &other) + : m_impl(other.m_impl ? other.m_impl->copy() : nullptr) {} + +template +Seq &Seq::operator=(const Seq &rhs) { + m_impl = rhs.m_impl->copy(); + return *this; +} + +template +Seq::type::ValueType> makeSeq(Args &&... args) { + using SeqT = Seq::type::ValueType>; + using ImplT = typename SeqT::template SeqImpl; + + SeqT seq; + seq.m_impl.reset(new ImplT(std::forward(args)...)); + return seq; +} + +template +bool operator==(Seq lhs, Seq rhs) { + while (true) { + Maybe a(lhs.next()); + Maybe b(rhs.next()); + if (a != b) { + return false; + } + + if (!a && !b) { + return true; + } + } +} + +template +bool operator!=(Seq lhs, Seq rhs) { + return !(std::move(lhs) == std::move(rhs)); +} + +template +std::ostream &operator<<(std::ostream &os, Seq seq) { + os << "["; + int n = 1; + auto first = seq.next(); + if (first) { + show(*first, os); + while (true) { + const auto value = seq.next(); + if (!value) { + break; + } + + os << ", "; + // Don't print infinite sequences... + if (n++ >= 1000) { + os << "..."; + break; + } + + show(*value, os); + } + } + os << "]"; + return os; +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Show.h b/test/rapidcheck/include/rapidcheck/Show.h new file mode 100644 index 0000000..4ded118 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Show.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace rc { + +/// Outputs a human readable representation of the given value to the given +/// output stream. To do this, it tries the following methods in order until one +/// works: +/// +/// 1. Use a suitable overload of `void showValue(T, std::ostream)´ +/// 2. Use a suitable overload of `std::ostream &operator<<(...)` +/// 3. Output a placeholder value. +template +void show(const T &value, std::ostream &os); + +/// Uses show(...) to convert argument to a string. +template +std::string toString(const T &value); + +/// Helper function for showing collections of values. +/// +/// @param prefix The prefix to the collection, for example "[" +/// @param suffix The suffix to the collection, for example "]" +/// @param collection The collection type. Must support `begin()` and `end()`. +/// @param os The stream to output to. +template +void showCollection(const std::string &prefix, + const std::string &suffix, + const Collection &collection, + std::ostream &os); + +} // namespace rc + +#include "Show.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Show.hpp b/test/rapidcheck/include/rapidcheck/Show.hpp new file mode 100644 index 0000000..98fa572 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Show.hpp @@ -0,0 +1,239 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rapidcheck/detail/Traits.h" + +namespace rc { +namespace detail { + +template ::value> +struct TupleHelper; + +template +struct TupleHelper, I> { + static void showTuple(const std::tuple<> &tuple, std::ostream &os) {} +}; + +template +struct TupleHelper { + static void showTuple(const TupleT &tuple, std::ostream &os) { + show(std::get::value - 1>(tuple), os); + } +}; + +template +struct TupleHelper { + static void showTuple(const TupleT &tuple, std::ostream &os) { + show(std::get::value - I>(tuple), os); + os << ", "; + TupleHelper::showTuple(tuple, os); + } +}; + +template +void showValue(T value, + typename std::enable_if::value, + std::ostream>::type &os) { + os << (value ? "true" : "false"); +} + +template +void showValue(T value, + typename std::enable_if<(std::is_same::value || + std::is_same::value), + std::ostream>::type &os) { + os << static_cast(value); +} + +void showValue(const std::string &value, std::ostream &os); +void showValue(const char *value, std::ostream &os); + +template +void showValue(T *p, std::ostream &os) { + show(*p, os); + auto flags = os.flags(); + os << " (" << std::hex << std::showbase << p << ")"; + os.flags(flags); +} + +template +void showValue(const std::unique_ptr &p, std::ostream &os) { + show(p.get(), os); +} + +template +void showValue(const std::shared_ptr &p, std::ostream &os) { + show(p.get(), os); +} + +template +void showValue(const std::pair &pair, std::ostream &os) { + os << "("; + show(pair.first, os); + os << ", "; + show(pair.second, os); + os << ")"; +} + +template +void showValue(const std::tuple &tuple, std::ostream &os) { + os << "("; + detail::TupleHelper>::showTuple(tuple, os); + os << ")"; +} + +template +void showValue(const std::vector &value, std::ostream &os) { + showCollection("[", "]", value, os); +} + +template +void showValue(const std::deque &value, std::ostream &os) { + showCollection("[", "]", value, os); +} + +template +void showValue(const std::forward_list &value, std::ostream &os) { + showCollection("[", "]", value, os); +} + +template +void showValue(const std::list &value, std::ostream &os) { + showCollection("[", "]", value, os); +} + +template +void showValue(const std::set &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue(const std::map &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue(const std::multiset &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue(const std::multimap &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue(const std::unordered_set &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue( + const std::unordered_map &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue( + const std::unordered_multiset &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue( + const std::unordered_multimap &value, + std::ostream &os) { + showCollection("{", "}", value, os); +} + +template +void showValue(const std::basic_string &value, + std::ostream &os) { + showCollection("\"", "\"", value, os); +} + +template +void showValue(const std::array &value, std::ostream &os) { + showCollection("[", "]", value, os); +} + +RC_SFINAE_TRAIT(HasShowValue, decltype(showValue(std::declval(), std::cout))) + +template ::value, + bool = IsStreamInsertible::value> +struct ShowDefault { + static void show(const T &/*value*/, std::ostream &os) { os << "<\?\?\?>"; } +}; + +template +struct ShowDefault { + static void show(const T &value, std::ostream &os) { showValue(value, os); } +}; + +template +struct ShowDefault { + static void show(const T &value, std::ostream &os) { os << value; } +}; + +} // namespace detail + +template +void show(const T &value, std::ostream &os) { + detail::ShowDefault::show(value, os); +} + +template +std::string toString(const T &value) { + std::ostringstream os; + show(value, os); + return os.str(); +} + +template +void showCollection(const std::string &prefix, + const std::string &suffix, + const Collection &collection, + std::ostream &os) { + os << prefix; + auto cbegin = begin(collection); + auto cend = end(collection); + if (cbegin != cend) { + show(*cbegin, os); + for (auto it = ++cbegin; it != cend; it++) { + os << ", "; + show(*it, os); + } + } + os << suffix; +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Shrinkable.h b/test/rapidcheck/include/rapidcheck/Shrinkable.h new file mode 100644 index 0000000..04512e0 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Shrinkable.h @@ -0,0 +1,62 @@ +#pragma once + +#include "rapidcheck/Seq.h" + +namespace rc { + +/// A `Shrinkable` describes a value in addition to all the possible ways of +/// shrinking that value. +/// +/// `Shrinkable` is backed by a type erased implementation object which must +/// have the following: +/// - A method `T value() const` which returns the value. +/// - A method `Seq> shrinks() const` which returns a `Seq` of +/// the possible shrinks. If this method throws, it is treated as if it had +/// returned an empty `Seq`. +/// +/// A Shrinkable is immutable and the implementation object is shared when the +/// shrinkable is copied which is why the implementation object needs no copy +/// constructor. +template +class Shrinkable { + template + friend Shrinkable().value())>> + makeShrinkable(Args &&... args); + +public: + /// The type of the value in this `Shrinkable`. + using ValueType = T; + + /// Returns the value. + T value() const; + + /// Returns a `Seq` of all the possible shrinks of this `Shrinkable`. + Seq> shrinks() const noexcept; + + Shrinkable(const Shrinkable &other) noexcept; + Shrinkable(Shrinkable &&other) noexcept; + Shrinkable &operator=(const Shrinkable &other) noexcept; + Shrinkable &operator=(Shrinkable &&other) noexcept; + ~Shrinkable() noexcept; + +private: + Shrinkable() = default; + + class IShrinkableImpl; + + template + class ShrinkableImpl; + + IShrinkableImpl *m_impl = nullptr; +}; + +/// Two `Shrinkable`s are equal if the have the same value and the same shrinks. +template +bool operator==(const Shrinkable &lhs, const Shrinkable &rhs); + +template +bool operator!=(const Shrinkable &lhs, const Shrinkable &rhs); + +} // namespace rc + +#include "Shrinkable.hpp" diff --git a/test/rapidcheck/include/rapidcheck/Shrinkable.hpp b/test/rapidcheck/include/rapidcheck/Shrinkable.hpp new file mode 100644 index 0000000..20e0f7b --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Shrinkable.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include + +namespace rc { + +template +class Shrinkable::IShrinkableImpl { +public: + virtual T value() const = 0; + virtual Seq> shrinks() const = 0; + virtual void retain() = 0; + virtual void release() = 0; + virtual ~IShrinkableImpl() = default; +}; + +template +template +class Shrinkable::ShrinkableImpl : public IShrinkableImpl { +public: + template + explicit ShrinkableImpl(Args &&... args) + : m_impl(std::forward(args)...) + , m_count(1) {} + + T value() const override { return m_impl.value(); } + Seq> shrinks() const override { return m_impl.shrinks(); } + + void retain() override { m_count.fetch_add(1L); } + + void release() override { + if (m_count.fetch_sub(1L) == 1L) { + delete this; + } + } + +private: + const Impl m_impl; + std::atomic m_count; +}; + +template +T Shrinkable::value() const { + return m_impl->value(); +} + +template +Seq> Shrinkable::shrinks() const noexcept { + try { + return m_impl->shrinks(); + } catch (...) { + return Seq>(); + } +} + +template +Shrinkable::Shrinkable(const Shrinkable &other) noexcept + : m_impl(other.m_impl) { + m_impl->retain(); +} + +template +Shrinkable::Shrinkable(Shrinkable &&other) noexcept : m_impl(other.m_impl) { + other.m_impl = nullptr; +} + +template +Shrinkable &Shrinkable::operator=(const Shrinkable &other) noexcept { + other.m_impl->retain(); + if (m_impl) { + m_impl->release(); + } + m_impl = other.m_impl; + return *this; +} + +template +Shrinkable &Shrinkable::operator=(Shrinkable &&other) noexcept { + if (m_impl) { + m_impl->release(); + } + m_impl = other.m_impl; + other.m_impl = nullptr; + return *this; +} + +template +Shrinkable::~Shrinkable() noexcept { + if (m_impl) { + m_impl->release(); + } +} + +template +Shrinkable().value())>> +makeShrinkable(Args &&... args) { + using T = decltype(std::declval().value()); + using ShrinkableImpl = typename Shrinkable::template ShrinkableImpl; + + Shrinkable shrinkable; + shrinkable.m_impl = new ShrinkableImpl(std::forward(args)...); + return shrinkable; +} + +template +bool operator==(const Shrinkable &lhs, const Shrinkable &rhs) { + return (lhs.value() == rhs.value()) && (lhs.shrinks() == rhs.shrinks()); +} + +template +bool operator!=(const Shrinkable &lhs, const Shrinkable &rhs) { + return !(lhs == rhs); +} + +} // namespace rc diff --git a/test/rapidcheck/include/rapidcheck/Traits.h b/test/rapidcheck/include/rapidcheck/Traits.h new file mode 100644 index 0000000..b6ad113 --- /dev/null +++ b/test/rapidcheck/include/rapidcheck/Traits.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "rapidcheck/detail/IntSequence.h" + +namespace rc { + +/// Convenience wrapper over std::decay, shorter to type. +template +using Decay = typename std::decay::type; + +/// Checks that all the parameters are true. +template +struct AllTrue + : public std::is_same, + detail::IntSequence> {}; + +/// Checks that all the types in `Ts` conforms to type trait `F`. +template