From 685e93c5f5d14641dfde3ac00c949ed44793a640 Mon Sep 17 00:00:00 2001 From: JohnRenoII Date: Sun, 12 Dec 2021 17:56:28 -0500 Subject: [PATCH 1/2] Added Skeleton Unit Tests Added source folders and Unity unit tests that can be execute with ceedling. --- CAN_App/build/test/cache/CAN_Driver.h | 27 + .../build/test/cache/defines_dependency.yml | 7 + CAN_App/build/test/cache/input.yml | 247 ++ CAN_App/build/test/cache/test_CAN.c | 76 + CAN_App/build/test/cache/test_CAN_Driver.c | 33 + CAN_App/build/test/dependencies/CAN.d | 1 + CAN_App/build/test/dependencies/CAN_Driver.d | 1 + CAN_App/build/test/dependencies/cmock.d | 6 + CAN_App/build/test/dependencies/force_build | 0 .../build/test/dependencies/mock_CAN_Driver.d | 6 + CAN_App/build/test/dependencies/test_CAN.d | 4 + .../build/test/dependencies/test_CAN_Driver.d | 4 + .../dependencies/test_CAN_Driver_runner.d | 4 + .../build/test/dependencies/test_CAN_runner.d | 6 + CAN_App/build/test/dependencies/unity.d | 4 + CAN_App/build/test/mocks/mock_CAN_Driver.c | 558 ++++ CAN_App/build/test/mocks/mock_CAN_Driver.h | 96 + CAN_App/build/test/out/c/CAN.o | Bin 0 -> 3111 bytes CAN_App/build/test/out/c/CAN_Driver.o | Bin 0 -> 3286 bytes CAN_App/build/test/out/c/cmock.o | Bin 0 -> 8639 bytes CAN_App/build/test/out/c/mock_CAN_Driver.o | Bin 0 -> 33115 bytes CAN_App/build/test/out/c/test_CAN.o | Bin 0 -> 5154 bytes CAN_App/build/test/out/c/test_CAN_Driver.o | Bin 0 -> 3207 bytes .../build/test/out/c/test_CAN_Driver_runner.o | Bin 0 -> 6731 bytes CAN_App/build/test/out/c/test_CAN_runner.o | Bin 0 -> 7850 bytes CAN_App/build/test/out/c/unity.o | Bin 0 -> 42314 bytes CAN_App/build/test/out/test_CAN.out | Bin 0 -> 206274 bytes CAN_App/build/test/out/test_CAN_Driver.out | Bin 0 -> 179495 bytes .../build/test/preprocess/files/CAN_Driver.h | 27 + .../build/test/preprocess/files/test_CAN.c | 76 + .../test/preprocess/files/test_CAN_Driver.c | 33 + .../test/preprocess/includes/CAN_Driver.h | 1 + .../build/test/preprocess/includes/test_CAN.c | 4 + .../preprocess/includes/test_CAN_Driver.c | 3 + CAN_App/build/test/results/test_CAN.pass | 22 + .../build/test/results/test_CAN_Driver.pass | 18 + .../test/runners/test_CAN_Driver_runner.c | 81 + CAN_App/build/test/runners/test_CAN_runner.c | 89 + CAN_App/ceedling.cmd | 1 + CAN_App/project.yml | 104 + CAN_App/src/CAN.c | 27 + CAN_App/src/CAN.h | 9 + CAN_App/src/CAN_Driver.c | 16 + CAN_App/src/CAN_Driver.h | 16 + CAN_App/test/support/.gitkeep | 0 CAN_App/test/test_CAN.c | 42 + CAN_App/test/test_CAN_Driver.c | 20 + CAN_App/vendor/ceedling/bin/ceedling | 350 +++ CAN_App/vendor/ceedling/docs/CMock_Summary.md | 831 ++++++ .../vendor/ceedling/docs/CeedlingPacket.md | 2266 +++++++++++++++++ .../vendor/ceedling/docs/CeedlingUpgrade.md | 83 + .../docs/ThrowTheSwitchCodingStandard.md | 207 ++ .../ceedling/docs/UnityAssertionsReference.md | 787 ++++++ .../ceedling/docs/UnityConfigurationGuide.md | 505 ++++ .../ceedling/docs/UnityGettingStartedGuide.md | 242 ++ .../ceedling/docs/UnityHelperScriptsGuide.md | 245 ++ CAN_App/vendor/ceedling/docs/plugin_beep.md | 22 + .../vendor/ceedling/docs/plugin_bullseye.md | 76 + .../ceedling/docs/plugin_colour_report.md | 20 + .../ceedling/docs/plugin_command_hooks.md | 53 + .../docs/plugin_compile_commands_json.md | 29 + .../ceedling/docs/plugin_dependencies.md | 254 ++ .../docs/plugin_fake_function_framework.md | 250 ++ CAN_App/vendor/ceedling/docs/plugin_gcov.md | 433 ++++ .../ceedling/docs/plugin_json_tests_report.md | 36 + .../docs/plugin_junit_tests_report.md | 36 + .../ceedling/docs/plugin_module_generator.md | 119 + .../ceedling/docs/plugin_raw_output_report.md | 19 + .../plugin_stdout_gtestlike_tests_report.md | 19 + .../docs/plugin_stdout_ide_tests_report.md | 18 + .../docs/plugin_stdout_pretty_tests_report.md | 20 + .../ceedling/docs/plugin_subprojects.md | 63 + .../docs/plugin_teamcity_tests_report.md | 18 + .../ceedling/docs/plugin_warnings_report.md | 19 + .../ceedling/docs/plugin_xml_tests_report.md | 36 + CAN_App/vendor/ceedling/lib/ceedling.rb | 99 + .../lib/ceedling/build_invoker_utils.rb | 39 + .../ceedling/lib/ceedling/cacheinator.rb | 47 + .../lib/ceedling/cacheinator_helper.rb | 35 + .../ceedling/lib/ceedling/cmock_builder.rb | 15 + .../ceedling/lib/ceedling/configurator.rb | 382 +++ .../lib/ceedling/configurator_builder.rb | 475 ++++ .../lib/ceedling/configurator_plugins.rb | 131 + .../lib/ceedling/configurator_setup.rb | 128 + .../lib/ceedling/configurator_validator.rb | 193 ++ .../vendor/ceedling/lib/ceedling/constants.rb | 99 + .../vendor/ceedling/lib/ceedling/defaults.rb | 471 ++++ .../ceedling/lib/ceedling/dependinator.rb | 97 + .../ceedling/lib/ceedling/erb_wrapper.rb | 9 + .../ceedling/lib/ceedling/file_finder.rb | 149 ++ .../lib/ceedling/file_finder_helper.rb | 56 + .../ceedling/lib/ceedling/file_path_utils.rb | 202 ++ .../lib/ceedling/file_system_utils.rb | 69 + .../lib/ceedling/file_system_wrapper.rb | 10 + .../ceedling/lib/ceedling/file_wrapper.rb | 83 + .../ceedling/lib/ceedling/flaginator.rb | 74 + .../vendor/ceedling/lib/ceedling/generator.rb | 186 ++ .../ceedling/lib/ceedling/generator_helper.rb | 40 + .../lib/ceedling/generator_test_results.rb | 100 + .../generator_test_results_sanity_checker.rb | 65 + .../lib/ceedling/generator_test_runner.rb | 58 + .../vendor/ceedling/lib/ceedling/loginator.rb | 31 + .../vendor/ceedling/lib/ceedling/makefile.rb | 46 + .../vendor/ceedling/lib/ceedling/objects.yml | 313 +++ .../vendor/ceedling/lib/ceedling/par_map.rb | 19 + .../vendor/ceedling/lib/ceedling/plugin.rb | 80 + .../ceedling/lib/ceedling/plugin_builder.rb | 53 + .../ceedling/lib/ceedling/plugin_manager.rb | 107 + .../lib/ceedling/plugin_manager_helper.rb | 19 + .../lib/ceedling/plugin_reportinator.rb | 76 + .../ceedling/plugin_reportinator_helper.rb | 51 + .../ceedling/lib/ceedling/preprocessinator.rb | 56 + .../ceedling/preprocessinator_extractor.rb | 55 + .../ceedling/preprocessinator_file_handler.rb | 34 + .../lib/ceedling/preprocessinator_helper.rb | 50 + .../preprocessinator_includes_handler.rb | 189 ++ .../lib/ceedling/project_config_manager.rb | 52 + .../lib/ceedling/project_file_loader.rb | 99 + .../ceedling/lib/ceedling/rake_utils.rb | 17 + .../ceedling/lib/ceedling/rake_wrapper.rb | 33 + .../vendor/ceedling/lib/ceedling/rakefile.rb | 85 + .../ceedling/lib/ceedling/release_invoker.rb | 98 + .../lib/ceedling/release_invoker_helper.rb | 19 + .../ceedling/lib/ceedling/reportinator.rb | 26 + .../ceedling/lib/ceedling/rules_cmock.rake | 9 + .../lib/ceedling/rules_preprocess.rake | 26 + .../ceedling/lib/ceedling/rules_release.rake | 99 + .../rules_release_deep_dependencies.rake | 15 + .../ceedling/lib/ceedling/rules_tests.rake | 73 + .../rules_tests_deep_dependencies.rake | 15 + .../ceedling/lib/ceedling/setupinator.rb | 53 + .../ceedling/lib/ceedling/stream_wrapper.rb | 28 + .../ceedling/lib/ceedling/streaminator.rb | 40 + .../lib/ceedling/streaminator_helper.rb | 15 + .../ceedling/lib/ceedling/system_utils.rb | 37 + .../ceedling/lib/ceedling/system_wrapper.rb | 80 + .../ceedling/lib/ceedling/target_loader.rb | 38 + .../ceedling/lib/ceedling/task_invoker.rb | 122 + .../ceedling/lib/ceedling/tasks_base.rake | 116 + .../lib/ceedling/tasks_filesystem.rake | 113 + .../ceedling/lib/ceedling/tasks_release.rake | 30 + .../tasks_release_deep_dependencies.rake | 9 + .../ceedling/lib/ceedling/tasks_tests.rake | 62 + .../tasks_tests_deep_dependencies.rake | 9 + .../ceedling/lib/ceedling/tasks_vendor.rake | 35 + .../lib/ceedling/test_includes_extractor.rb | 111 + .../ceedling/lib/ceedling/test_invoker.rb | 165 ++ .../lib/ceedling/test_invoker_helper.rb | 32 + .../ceedling/lib/ceedling/tool_executor.rb | 229 ++ .../lib/ceedling/tool_executor_helper.rb | 164 ++ .../ceedling/lib/ceedling/verbosinator.rb | 10 + .../vendor/ceedling/lib/ceedling/version.rb | 54 + .../ceedling/lib/ceedling/yaml_wrapper.rb | 17 + .../vendor/ceedling/plugins/beep/README.md | 22 + .../vendor/ceedling/plugins/beep/lib/beep.rb | 40 + .../ceedling/plugins/bullseye/README.md | 76 + .../plugins/bullseye/assets/template.erb | 15 + .../ceedling/plugins/bullseye/bullseye.rake | 173 ++ .../plugins/bullseye/config/defaults.yml | 57 + .../ceedling/plugins/bullseye/lib/bullseye.rb | 194 ++ .../ceedling/plugins/colour_report/README.md | 20 + .../colour_report/lib/colour_report.rb | 16 + .../ceedling/plugins/command_hooks/README.md | 53 + .../command_hooks/lib/command_hooks.rb | 92 + .../plugins/compile_commands_json/README.md | 29 + .../lib/compile_commands_json.rb | 35 + .../ceedling/plugins/dependencies/README.md | 254 ++ .../plugins/dependencies/config/defaults.yml | 5 + .../plugins/dependencies/dependencies.rake | 147 ++ .../plugins/dependencies/lib/dependencies.rb | 237 ++ .../plugins/fake_function_framework/README.md | 250 ++ .../plugins/fake_function_framework/Rakefile | 19 + .../examples/fff_example/project.yml | 71 + .../examples/fff_example/rakefile.rb | 7 + .../examples/fff_example/src/bar.c | 1 + .../examples/fff_example/src/bar.h | 14 + .../examples/fff_example/src/custom_types.h | 6 + .../examples/fff_example/src/display.c | 7 + .../examples/fff_example/src/display.h | 16 + .../fff_example/src/event_processor.c | 93 + .../fff_example/src/event_processor.h | 11 + .../examples/fff_example/src/foo.c | 16 + .../examples/fff_example/src/foo.h | 8 + .../examples/fff_example/src/subfolder/zzz.c | 1 + .../examples/fff_example/src/subfolder/zzz.h | 6 + .../fff_example/test/test_event_processor.c | 155 ++ .../examples/fff_example/test/test_foo.c | 47 + .../lib/fake_function_framework.rb | 87 + .../lib/fff_mock_generator.rb | 163 ++ .../spec/fff_mock_header_generator_spec.rb | 304 +++ .../spec/fff_mock_source_generator_spec.rb | 149 ++ .../spec/header_generator.rb | 51 + .../spec/spec_helper.rb | 96 + .../src/fff_unity_helper.h | 33 + .../vendor/ceedling/plugins/gcov/README.md | 433 ++++ .../ceedling/plugins/gcov/assets/template.erb | 15 + .../plugins/gcov/config/defaults_gcov.rb | 118 + .../vendor/ceedling/plugins/gcov/gcov.rake | 209 ++ .../vendor/ceedling/plugins/gcov/lib/gcov.rb | 136 + .../plugins/gcov/lib/gcov_constants.rb | 48 + .../plugins/gcov/lib/gcovr_reportinator.rb | 331 +++ .../gcov/lib/reportgenerator_reportinator.rb | 195 ++ .../plugins/gcov/lib/reportinator_helper.rb | 15 + .../plugins/json_tests_report/README.md | 36 + .../lib/json_tests_report.rb | 83 + .../plugins/junit_tests_report/README.md | 36 + .../lib/junit_tests_report.rb | 134 + .../plugins/module_generator/README.md | 119 + .../config/module_generator.yml | 4 + .../module_generator/lib/module_generator.rb | 80 + .../module_generator/module_generator.rake | 62 + .../plugins/raw_output_report/README.md | 19 + .../lib/raw_output_report.rb | 41 + .../stdout_gtestlike_tests_report/README.md | 19 + .../assets/template.erb | 84 + .../assets/template.erb copy | 59 + .../config/stdout_gtestlike_tests_report.yml | 4 + .../lib/stdout_gtestlike_tests_report.rb | 43 + .../plugins/stdout_ide_tests_report/README.md | 18 + .../config/stdout_ide_tests_report.yml | 4 + .../lib/stdout_ide_tests_report.rb | 44 + .../stdout_pretty_tests_report/README.md | 20 + .../assets/template.erb | 59 + .../config/stdout_pretty_tests_report.yml | 4 + .../lib/stdout_pretty_tests_report.rb | 47 + .../ceedling/plugins/subprojects/README.md | 63 + .../plugins/subprojects/config/defaults.yml | 33 + .../plugins/subprojects/lib/subprojects.rb | 92 + .../plugins/subprojects/subprojects.rake | 78 + .../plugins/teamcity_tests_report/README.md | 18 + .../config/teamcity_tests_report.yml | 4 + .../lib/teamcity_tests_report.rb | 57 + .../plugins/warnings_report/README.md | 19 + .../warnings_report/lib/warnings_report.rb | 69 + .../plugins/xml_tests_report/README.md | 36 + .../xml_tests_report/lib/xml_tests_report.rb | 110 + .../vendor/c_exception/lib/CException.c | 46 + .../vendor/c_exception/lib/CException.h | 115 + .../vendor/c_exception/lib/meson.build | 11 + .../cmock/config/production_environment.rb | 12 + .../vendor/cmock/config/test_environment.rb | 16 + .../vendor/ceedling/vendor/cmock/lib/cmock.rb | 111 + .../ceedling/vendor/cmock/lib/cmock_config.rb | 174 ++ .../vendor/cmock/lib/cmock_file_writer.rb | 47 + .../vendor/cmock/lib/cmock_generator.rb | 368 +++ .../cmock/lib/cmock_generator_plugin_array.rb | 63 + .../lib/cmock_generator_plugin_callback.rb | 88 + .../lib/cmock_generator_plugin_cexception.rb | 50 + .../lib/cmock_generator_plugin_expect.rb | 100 + .../cmock_generator_plugin_expect_any_args.rb | 50 + .../lib/cmock_generator_plugin_ignore.rb | 88 + .../lib/cmock_generator_plugin_ignore_arg.rb | 42 + ...cmock_generator_plugin_ignore_stateless.rb | 85 + .../cmock_generator_plugin_return_thru_ptr.rb | 79 + .../vendor/cmock/lib/cmock_generator_utils.rb | 250 ++ .../vendor/cmock/lib/cmock_header_parser.rb | 623 +++++ .../vendor/cmock/lib/cmock_plugin_manager.rb | 50 + .../cmock/lib/cmock_unityhelper_parser.rb | 77 + .../vendor/ceedling/vendor/cmock/src/cmock.c | 216 ++ .../vendor/ceedling/vendor/cmock/src/cmock.h | 47 + .../vendor/cmock/src/cmock_internals.h | 91 + .../ceedling/vendor/cmock/src/meson.build | 12 + CAN_App/vendor/ceedling/vendor/diy/lib/diy.rb | 403 +++ .../ceedling/vendor/diy/lib/diy/factory.rb | 36 + .../vendor/unity/auto/colour_prompt.rb | 119 + .../vendor/unity/auto/colour_reporter.rb | 39 + .../vendor/unity/auto/generate_config.yml | 36 + .../vendor/unity/auto/generate_module.rb | 313 +++ .../vendor/unity/auto/generate_test_runner.rb | 511 ++++ .../vendor/unity/auto/parse_output.rb | 322 +++ .../ceedling/vendor/unity/auto/run_test.erb | 37 + .../vendor/unity/auto/stylize_as_junit.rb | 251 ++ .../vendor/unity/auto/test_file_filter.rb | 25 + .../vendor/unity/auto/type_sanitizer.rb | 6 + .../vendor/unity/auto/unity_test_summary.py | 139 + .../vendor/unity/auto/unity_test_summary.rb | 135 + .../vendor/unity/auto/unity_to_junit.py | 146 ++ .../ceedling/vendor/unity/src/meson.build | 11 + .../vendor/ceedling/vendor/unity/src/unity.c | 2110 +++++++++++++++ .../vendor/ceedling/vendor/unity/src/unity.h | 661 +++++ .../vendor/unity/src/unity_internals.h | 1053 ++++++++ InstallingCeedlingUnitTestTools.pdf | Bin 0 -> 260555 bytes README.md | 29 + source/src/main.c | 27 + 284 files changed, 30945 insertions(+) create mode 100644 CAN_App/build/test/cache/CAN_Driver.h create mode 100644 CAN_App/build/test/cache/defines_dependency.yml create mode 100644 CAN_App/build/test/cache/input.yml create mode 100644 CAN_App/build/test/cache/test_CAN.c create mode 100644 CAN_App/build/test/cache/test_CAN_Driver.c create mode 100644 CAN_App/build/test/dependencies/CAN.d create mode 100644 CAN_App/build/test/dependencies/CAN_Driver.d create mode 100644 CAN_App/build/test/dependencies/cmock.d create mode 100644 CAN_App/build/test/dependencies/force_build create mode 100644 CAN_App/build/test/dependencies/mock_CAN_Driver.d create mode 100644 CAN_App/build/test/dependencies/test_CAN.d create mode 100644 CAN_App/build/test/dependencies/test_CAN_Driver.d create mode 100644 CAN_App/build/test/dependencies/test_CAN_Driver_runner.d create mode 100644 CAN_App/build/test/dependencies/test_CAN_runner.d create mode 100644 CAN_App/build/test/dependencies/unity.d create mode 100644 CAN_App/build/test/mocks/mock_CAN_Driver.c create mode 100644 CAN_App/build/test/mocks/mock_CAN_Driver.h create mode 100644 CAN_App/build/test/out/c/CAN.o create mode 100644 CAN_App/build/test/out/c/CAN_Driver.o create mode 100644 CAN_App/build/test/out/c/cmock.o create mode 100644 CAN_App/build/test/out/c/mock_CAN_Driver.o create mode 100644 CAN_App/build/test/out/c/test_CAN.o create mode 100644 CAN_App/build/test/out/c/test_CAN_Driver.o create mode 100644 CAN_App/build/test/out/c/test_CAN_Driver_runner.o create mode 100644 CAN_App/build/test/out/c/test_CAN_runner.o create mode 100644 CAN_App/build/test/out/c/unity.o create mode 100644 CAN_App/build/test/out/test_CAN.out create mode 100644 CAN_App/build/test/out/test_CAN_Driver.out create mode 100644 CAN_App/build/test/preprocess/files/CAN_Driver.h create mode 100644 CAN_App/build/test/preprocess/files/test_CAN.c create mode 100644 CAN_App/build/test/preprocess/files/test_CAN_Driver.c create mode 100644 CAN_App/build/test/preprocess/includes/CAN_Driver.h create mode 100644 CAN_App/build/test/preprocess/includes/test_CAN.c create mode 100644 CAN_App/build/test/preprocess/includes/test_CAN_Driver.c create mode 100644 CAN_App/build/test/results/test_CAN.pass create mode 100644 CAN_App/build/test/results/test_CAN_Driver.pass create mode 100644 CAN_App/build/test/runners/test_CAN_Driver_runner.c create mode 100644 CAN_App/build/test/runners/test_CAN_runner.c create mode 100644 CAN_App/ceedling.cmd create mode 100644 CAN_App/project.yml create mode 100644 CAN_App/src/CAN.c create mode 100644 CAN_App/src/CAN.h create mode 100644 CAN_App/src/CAN_Driver.c create mode 100644 CAN_App/src/CAN_Driver.h create mode 100644 CAN_App/test/support/.gitkeep create mode 100644 CAN_App/test/test_CAN.c create mode 100644 CAN_App/test/test_CAN_Driver.c create mode 100644 CAN_App/vendor/ceedling/bin/ceedling create mode 100644 CAN_App/vendor/ceedling/docs/CMock_Summary.md create mode 100644 CAN_App/vendor/ceedling/docs/CeedlingPacket.md create mode 100644 CAN_App/vendor/ceedling/docs/CeedlingUpgrade.md create mode 100644 CAN_App/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md create mode 100644 CAN_App/vendor/ceedling/docs/UnityAssertionsReference.md create mode 100644 CAN_App/vendor/ceedling/docs/UnityConfigurationGuide.md create mode 100644 CAN_App/vendor/ceedling/docs/UnityGettingStartedGuide.md create mode 100644 CAN_App/vendor/ceedling/docs/UnityHelperScriptsGuide.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_beep.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_bullseye.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_colour_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_command_hooks.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_compile_commands_json.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_dependencies.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_fake_function_framework.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_gcov.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_json_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_junit_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_module_generator.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_raw_output_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_stdout_gtestlike_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_stdout_ide_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_stdout_pretty_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_subprojects.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_teamcity_tests_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_warnings_report.md create mode 100644 CAN_App/vendor/ceedling/docs/plugin_xml_tests_report.md create mode 100644 CAN_App/vendor/ceedling/lib/ceedling.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/build_invoker_utils.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/cacheinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/cacheinator_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/cmock_builder.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/configurator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/configurator_builder.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/configurator_plugins.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/configurator_setup.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/configurator_validator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/constants.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/defaults.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/dependinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/erb_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_finder.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_finder_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_path_utils.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_system_utils.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_system_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/file_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/flaginator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/generator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/generator_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/generator_test_results.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/generator_test_runner.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/loginator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/makefile.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/objects.yml create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/par_map.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin_builder.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin_manager.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/preprocessinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/project_config_manager.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/project_file_loader.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rake_utils.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rake_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rakefile.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/release_invoker.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/release_invoker_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/reportinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_cmock.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_preprocess.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_release.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_tests.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/setupinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/stream_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/streaminator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/streaminator_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/system_utils.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/system_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/target_loader.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/task_invoker.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_base.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_filesystem.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_release.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_tests.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tasks_vendor.rake create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/test_includes_extractor.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/test_invoker.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/test_invoker_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tool_executor.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/tool_executor_helper.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/verbosinator.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/version.rb create mode 100644 CAN_App/vendor/ceedling/lib/ceedling/yaml_wrapper.rb create mode 100644 CAN_App/vendor/ceedling/plugins/beep/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/beep/lib/beep.rb create mode 100644 CAN_App/vendor/ceedling/plugins/bullseye/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/bullseye/assets/template.erb create mode 100644 CAN_App/vendor/ceedling/plugins/bullseye/bullseye.rake create mode 100644 CAN_App/vendor/ceedling/plugins/bullseye/config/defaults.yml create mode 100644 CAN_App/vendor/ceedling/plugins/bullseye/lib/bullseye.rb create mode 100644 CAN_App/vendor/ceedling/plugins/colour_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/colour_report/lib/colour_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/command_hooks/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb create mode 100644 CAN_App/vendor/ceedling/plugins/compile_commands_json/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb create mode 100644 CAN_App/vendor/ceedling/plugins/dependencies/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/dependencies/config/defaults.yml create mode 100644 CAN_App/vendor/ceedling/plugins/dependencies/dependencies.rake create mode 100644 CAN_App/vendor/ceedling/plugins/dependencies/lib/dependencies.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/Rakefile create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb create mode 100644 CAN_App/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/assets/template.erb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/gcov.rake create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/lib/gcov.rb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb create mode 100644 CAN_App/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb create mode 100644 CAN_App/vendor/ceedling/plugins/json_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/junit_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/module_generator/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/module_generator/config/module_generator.yml create mode 100644 CAN_App/vendor/ceedling/plugins/module_generator/lib/module_generator.rb create mode 100644 CAN_App/vendor/ceedling/plugins/module_generator/module_generator.rake create mode 100644 CAN_App/vendor/ceedling/plugins/raw_output_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml create mode 100644 CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/subprojects/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/subprojects/config/defaults.yml create mode 100644 CAN_App/vendor/ceedling/plugins/subprojects/lib/subprojects.rb create mode 100644 CAN_App/vendor/ceedling/plugins/subprojects/subprojects.rake create mode 100644 CAN_App/vendor/ceedling/plugins/teamcity_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml create mode 100644 CAN_App/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/warnings_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb create mode 100644 CAN_App/vendor/ceedling/plugins/xml_tests_report/README.md create mode 100644 CAN_App/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb create mode 100644 CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.c create mode 100644 CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.h create mode 100644 CAN_App/vendor/ceedling/vendor/c_exception/lib/meson.build create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/config/production_environment.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/config/test_environment.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_config.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/src/cmock.c create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h create mode 100644 CAN_App/vendor/ceedling/vendor/cmock/src/meson.build create mode 100644 CAN_App/vendor/ceedling/vendor/diy/lib/diy.rb create mode 100644 CAN_App/vendor/ceedling/vendor/diy/lib/diy/factory.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/colour_prompt.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/colour_reporter.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/generate_config.yml create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/generate_module.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/parse_output.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/run_test.erb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/test_file_filter.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.py create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb create mode 100644 CAN_App/vendor/ceedling/vendor/unity/auto/unity_to_junit.py create mode 100644 CAN_App/vendor/ceedling/vendor/unity/src/meson.build create mode 100644 CAN_App/vendor/ceedling/vendor/unity/src/unity.c create mode 100644 CAN_App/vendor/ceedling/vendor/unity/src/unity.h create mode 100644 CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h create mode 100644 InstallingCeedlingUnitTestTools.pdf create mode 100644 source/src/main.c diff --git a/CAN_App/build/test/cache/CAN_Driver.h b/CAN_App/build/test/cache/CAN_Driver.h new file mode 100644 index 0000000..1ac8087 --- /dev/null +++ b/CAN_App/build/test/cache/CAN_Driver.h @@ -0,0 +1,27 @@ + + + + + + + + + + +typedef enum logOpt + +{ + + _LOG_BYTE, + + _LOG_LINE + + } LOGOPT_t; + + + +uint8_t CANSPIRead( uint32_t *idRx , uint8_t *dataRxTx , uint8_t *dataRxLen, uint8_t *canRcvFlags ); + +void mikrobus_logWrite( uint8_t *dataRxTx, const LOGOPT_t opt ); + +void Delay_1sec( void ); diff --git a/CAN_App/build/test/cache/defines_dependency.yml b/CAN_App/build/test/cache/defines_dependency.yml new file mode 100644 index 0000000..db0a609 --- /dev/null +++ b/CAN_App/build/test/cache/defines_dependency.yml @@ -0,0 +1,7 @@ +--- +src/CAN.c: +- TEST +build/test/mocks/mock_CAN_Driver.c: +- TEST +src/CAN_Driver.c: +- TEST diff --git a/CAN_App/build/test/cache/input.yml b/CAN_App/build/test/cache/input.yml new file mode 100644 index 0000000..2f0aff4 --- /dev/null +++ b/CAN_App/build/test/cache/input.yml @@ -0,0 +1,247 @@ +--- +:project: + :use_exceptions: false + :use_mocks: true + :compile_threads: 1 + :test_threads: 1 + :use_test_preprocessor: true + :use_preprocessor_directives: false + :use_deep_dependencies: false + :generate_deep_dependencies: true + :auto_link_deep_dependencies: false + :test_file_prefix: test_ + :options_paths: [] + :release_build: false + :use_auxiliary_dependencies: true + :build_root: build + :which_ceedling: vendor/ceedling + :ceedling_version: 0.31.1 + :default_tasks: + - test:all +:release_build: + :use_assembly: false + :artifacts: [] +:paths: + :test: + - "+:test/**" + - "-:test/support" + :source: + - src/** + :support: + - test/support + :include: [] + :libraries: [] + :test_toolchain_include: [] + :release_toolchain_include: [] +:files: + :test: [] + :source: [] + :assembly: [] + :support: [] + :include: [] +:environment: +- :rake_columns: '120' +:defines: + :test: + - &1 [] + - TEST + :test_preprocess: + - *1 + - TEST + :release: [] + :release_preprocess: [] + :use_test_definition: false + :common: [] +:libraries: + :flag: "-l${1}" + :path_flag: "-L ${1}" + :test: [] + :test_preprocess: [] + :release: [] + :release_preprocess: [] + :placement: :end + :system: [] +:flags: {} +:extension: + :header: ".h" + :source: ".c" + :assembly: ".s" + :object: ".o" + :libraries: + - ".a" + - ".so" + :executable: ".out" + :map: ".map" + :list: ".lst" + :testpass: ".pass" + :testfail: ".fail" + :dependencies: ".d" +:unity: + :vendor_path: C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor + :defines: [] +:cmock: + :vendor_path: C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor + :defines: [] + :includes: [] + :mock_prefix: mock_ + :when_no_prototypes: :warn + :enforce_strict_ordering: true + :plugins: + - :ignore + - :callback + - :ignore_arg + - :return_thru_ptr + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + :mock_path: build/test/mocks + :verbosity: 3 + :unity_helper: false +:cexception: + :vendor_path: C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor + :defines: [] +:test_runner: + :includes: [] + :file_suffix: _runner +:tools: + :test_compiler: + :executable: gcc.exe + :name: default_test_compiler + :stderr_redirect: :none + :background_exec: :none + :optional: false + :arguments: + - '' + - '' + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE + - "-D$": COLLECTION_DEFINES_TEST_AND_VENDOR + - "-DGNU_COMPILER" + - "-g" + - '' + - -c "${1}" + - -o "${2}" + - "-MMD" + - -MF "${4}" + :test_fixture: + :executable: "${1}" + :name: default_test_fixture + :stderr_redirect: :auto + :background_exec: :none + :optional: false + :arguments: [] + :test_linker: + :executable: gcc.exe + :name: default_test_linker + :stderr_redirect: :none + :background_exec: :none + :optional: false + :arguments: + - '' + - '' + - '' + - '"${1}"' + - "${5}" + - -o "${2}" + - '' + - "${4}" + - '' + :test_file_preprocessor: + :executable: gcc.exe + :name: default_test_file_preprocessor + :stderr_redirect: :none + :background_exec: :none + :optional: false + :arguments: + - '' + - '' + - "-E" + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE + - "-D$": COLLECTION_DEFINES_TEST_AND_VENDOR + - "-D$": DEFINES_TEST_PREPROCESS + - "-DGNU_COMPILER" + - '"${1}"' + - -o "${2}" + :test_file_preprocessor_directives: + :executable: gcc.exe + :name: default_test_file_preprocessor_directives + :stderr_redirect: :none + :background_exec: :none + :optional: false + :arguments: + - "-E" + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE + - "-D$": COLLECTION_DEFINES_TEST_AND_VENDOR + - "-D$": DEFINES_TEST_PREPROCESS + - "-DGNU_COMPILER" + - "-fdirectives-only" + - '"${1}"' + - -o "${2}" + :test_includes_preprocessor: + :executable: gcc.exe + :name: default_test_includes_preprocessor + :stderr_redirect: :none + :background_exec: :none + :optional: false + :arguments: + - '' + - '' + - "-E" + - "-MM" + - "-MG" + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE + - "-D$": COLLECTION_DEFINES_TEST_AND_VENDOR + - "-D$": DEFINES_TEST_PREPROCESS + - "-DGNU_COMPILER" + - '"${1}"' +:test_compiler: + :arguments: [] +:test_linker: + :arguments: [] +:test_fixture: + :arguments: [] + :link_objects: [] +:test_includes_preprocessor: + :arguments: [] +:test_file_preprocessor: + :arguments: [] +:test_file_preprocessor_directives: + :arguments: [] +:test_dependencies_generator: + :arguments: [] +:release_compiler: + :arguments: [] +:release_linker: + :arguments: [] +:release_assembler: + :arguments: [] +:release_dependencies_generator: + :arguments: [] +:plugins: + :load_paths: + - vendor/ceedling/plugins + - vendor/ceedling/lib/../plugins + :enabled: + - stdout_pretty_tests_report + - module_generator + - raw_output_report + :display_raw_test_results: false + :stdout_pretty_tests_report_path: vendor/ceedling/plugins/stdout_pretty_tests_report + :module_generator_path: vendor/ceedling/plugins/module_generator + :raw_output_report_path: vendor/ceedling/plugins/raw_output_report +:gcov: + :reports: + - HtmlDetailed + :gcovr: + :html_medium_threshold: 75 + :html_high_threshold: 90 +:module_generator: + :project_root: "./" + :source_root: src/ + :test_root: test/ diff --git a/CAN_App/build/test/cache/test_CAN.c b/CAN_App/build/test/cache/test_CAN.c new file mode 100644 index 0000000..537a51b --- /dev/null +++ b/CAN_App/build/test/cache/test_CAN.c @@ -0,0 +1,76 @@ +#include "build/test/mocks/mock_CAN_Driver.h" +#include "src/CAN.h" +#include "C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h" + + + + + + + + +void setUp(void) + +{ + +} + + + +void tearDown(void) + +{ + +} + + + +void test_GivenMessageReceived_ThenWritesToLog(void) + +{ + + CANSPIRead_CMockExpectAndReturn(18, 0, 0, 0, 0, 1); + + CANSPIRead_CMockIgnoreArg_idRx(19); + + CANSPIRead_CMockIgnoreArg_dataRxTx(20); + + CANSPIRead_CMockIgnoreArg_dataRxLen(21); + + CANSPIRead_CMockIgnoreArg_canRcvFlags(22); + + CANSPIRead_CMockReturnMemThruPtr_dataRxTx(23, "ABCDEFG", sizeof(uint8_t)); + + mikrobus_logWrite_CMockExpect(24, "ABCDEFG", _LOG_BYTE); + + Delay_1sec_CMockExpect(25); + + + + CAN_App(); + +} + + + +void test_GivenNoMessageReceived_ThenDoesNotWriteToLog(void) + +{ + + CANSPIRead_CMockExpectAndReturn(32, 0, 0, 0, 0, 0); + + CANSPIRead_CMockIgnoreArg_idRx(33); + + CANSPIRead_CMockIgnoreArg_dataRxTx(34); + + CANSPIRead_CMockIgnoreArg_dataRxLen(35); + + CANSPIRead_CMockIgnoreArg_canRcvFlags(36); + + CANSPIRead_CMockReturnMemThruPtr_dataRxTx(37, "ABCDEFG", sizeof(uint8_t)); + + + + CAN_App(); + +} diff --git a/CAN_App/build/test/cache/test_CAN_Driver.c b/CAN_App/build/test/cache/test_CAN_Driver.c new file mode 100644 index 0000000..44fddb0 --- /dev/null +++ b/CAN_App/build/test/cache/test_CAN_Driver.c @@ -0,0 +1,33 @@ +#include "src/CAN_Driver.h" +#include "C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h" + + + + + + + + +void setUp(void) + +{ + +} + + + +void tearDown(void) + +{ + +} + + + +void test_CAN_Driver_NeedToImplement(void) + +{ + + UnityIgnore( (("Need to Implement CAN_Driver")), (UNITY_UINT)(17)); + +} diff --git a/CAN_App/build/test/dependencies/CAN.d b/CAN_App/build/test/dependencies/CAN.d new file mode 100644 index 0000000..aef17cf --- /dev/null +++ b/CAN_App/build/test/dependencies/CAN.d @@ -0,0 +1 @@ +build/test/out/c/CAN.o: src/CAN.c src/CAN.h src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/CAN_Driver.d b/CAN_App/build/test/dependencies/CAN_Driver.d new file mode 100644 index 0000000..d7ba18e --- /dev/null +++ b/CAN_App/build/test/dependencies/CAN_Driver.d @@ -0,0 +1 @@ +build/test/out/c/CAN_Driver.o: src/CAN_Driver.c src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/cmock.d b/CAN_App/build/test/dependencies/cmock.d new file mode 100644 index 0000000..13fb123 --- /dev/null +++ b/CAN_App/build/test/dependencies/cmock.d @@ -0,0 +1,6 @@ +build/test/out/c/cmock.o: \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h diff --git a/CAN_App/build/test/dependencies/force_build b/CAN_App/build/test/dependencies/force_build new file mode 100644 index 0000000..e69de29 diff --git a/CAN_App/build/test/dependencies/mock_CAN_Driver.d b/CAN_App/build/test/dependencies/mock_CAN_Driver.d new file mode 100644 index 0000000..7fb696a --- /dev/null +++ b/CAN_App/build/test/dependencies/mock_CAN_Driver.d @@ -0,0 +1,6 @@ +build/test/out/c/mock_CAN_Driver.o: build/test/mocks/mock_CAN_Driver.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h \ + build/test/mocks/mock_CAN_Driver.h src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/test_CAN.d b/CAN_App/build/test/dependencies/test_CAN.d new file mode 100644 index 0000000..cdfcb65 --- /dev/null +++ b/CAN_App/build/test/dependencies/test_CAN.d @@ -0,0 +1,4 @@ +build/test/out/c/test_CAN.o: test/test_CAN.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h \ + src/CAN.h build/test/mocks/mock_CAN_Driver.h src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/test_CAN_Driver.d b/CAN_App/build/test/dependencies/test_CAN_Driver.d new file mode 100644 index 0000000..187e7f2 --- /dev/null +++ b/CAN_App/build/test/dependencies/test_CAN_Driver.d @@ -0,0 +1,4 @@ +build/test/out/c/test_CAN_Driver.o: test/test_CAN_Driver.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h \ + src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/test_CAN_Driver_runner.d b/CAN_App/build/test/dependencies/test_CAN_Driver_runner.d new file mode 100644 index 0000000..93d8ca6 --- /dev/null +++ b/CAN_App/build/test/dependencies/test_CAN_Driver_runner.d @@ -0,0 +1,4 @@ +build/test/out/c/test_CAN_Driver_runner.o: \ + build/test/runners/test_CAN_Driver_runner.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h diff --git a/CAN_App/build/test/dependencies/test_CAN_runner.d b/CAN_App/build/test/dependencies/test_CAN_runner.d new file mode 100644 index 0000000..e37b355 --- /dev/null +++ b/CAN_App/build/test/dependencies/test_CAN_runner.d @@ -0,0 +1,6 @@ +build/test/out/c/test_CAN_runner.o: build/test/runners/test_CAN_runner.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h \ + build/test/mocks/mock_CAN_Driver.h src/CAN_Driver.h diff --git a/CAN_App/build/test/dependencies/unity.d b/CAN_App/build/test/dependencies/unity.d new file mode 100644 index 0000000..f4e6329 --- /dev/null +++ b/CAN_App/build/test/dependencies/unity.d @@ -0,0 +1,4 @@ +build/test/out/c/unity.o: \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.c \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h \ + C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h diff --git a/CAN_App/build/test/mocks/mock_CAN_Driver.c b/CAN_App/build/test/mocks/mock_CAN_Driver.c new file mode 100644 index 0000000..470e822 --- /dev/null +++ b/CAN_App/build/test/mocks/mock_CAN_Driver.c @@ -0,0 +1,558 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ +#include +#include +#include +#include "cmock.h" +#include "mock_CAN_Driver.h" + +static const char* CMockString_CANSPIRead = "CANSPIRead"; +static const char* CMockString_Delay_1sec = "Delay_1sec"; +static const char* CMockString_canRcvFlags = "canRcvFlags"; +static const char* CMockString_dataRxLen = "dataRxLen"; +static const char* CMockString_dataRxTx = "dataRxTx"; +static const char* CMockString_idRx = "idRx"; +static const char* CMockString_mikrobus_logWrite = "mikrobus_logWrite"; +static const char* CMockString_opt = "opt"; + +typedef struct _CMOCK_CANSPIRead_CALL_INSTANCE +{ + UNITY_LINE_TYPE LineNumber; + uint8_t ReturnVal; + int CallOrder; + uint32_t* Expected_idRx; + uint8_t* Expected_dataRxTx; + uint8_t* Expected_dataRxLen; + uint8_t* Expected_canRcvFlags; + char ReturnThruPtr_idRx_Used; + uint32_t* ReturnThruPtr_idRx_Val; + size_t ReturnThruPtr_idRx_Size; + char ReturnThruPtr_dataRxTx_Used; + uint8_t* ReturnThruPtr_dataRxTx_Val; + size_t ReturnThruPtr_dataRxTx_Size; + char ReturnThruPtr_dataRxLen_Used; + uint8_t* ReturnThruPtr_dataRxLen_Val; + size_t ReturnThruPtr_dataRxLen_Size; + char ReturnThruPtr_canRcvFlags_Used; + uint8_t* ReturnThruPtr_canRcvFlags_Val; + size_t ReturnThruPtr_canRcvFlags_Size; + char IgnoreArg_idRx; + char IgnoreArg_dataRxTx; + char IgnoreArg_dataRxLen; + char IgnoreArg_canRcvFlags; + +} CMOCK_CANSPIRead_CALL_INSTANCE; + +typedef struct _CMOCK_mikrobus_logWrite_CALL_INSTANCE +{ + UNITY_LINE_TYPE LineNumber; + int CallOrder; + uint8_t* Expected_dataRxTx; + LOGOPT_t Expected_opt; + char ReturnThruPtr_dataRxTx_Used; + uint8_t* ReturnThruPtr_dataRxTx_Val; + size_t ReturnThruPtr_dataRxTx_Size; + char IgnoreArg_dataRxTx; + char IgnoreArg_opt; + +} CMOCK_mikrobus_logWrite_CALL_INSTANCE; + +typedef struct _CMOCK_Delay_1sec_CALL_INSTANCE +{ + UNITY_LINE_TYPE LineNumber; + int CallOrder; + +} CMOCK_Delay_1sec_CALL_INSTANCE; + +static struct mock_CAN_DriverInstance +{ + char CANSPIRead_IgnoreBool; + uint8_t CANSPIRead_FinalReturn; + char CANSPIRead_CallbackBool; + CMOCK_CANSPIRead_CALLBACK CANSPIRead_CallbackFunctionPointer; + int CANSPIRead_CallbackCalls; + CMOCK_MEM_INDEX_TYPE CANSPIRead_CallInstance; + char mikrobus_logWrite_IgnoreBool; + char mikrobus_logWrite_CallbackBool; + CMOCK_mikrobus_logWrite_CALLBACK mikrobus_logWrite_CallbackFunctionPointer; + int mikrobus_logWrite_CallbackCalls; + CMOCK_MEM_INDEX_TYPE mikrobus_logWrite_CallInstance; + char Delay_1sec_IgnoreBool; + char Delay_1sec_CallbackBool; + CMOCK_Delay_1sec_CALLBACK Delay_1sec_CallbackFunctionPointer; + int Delay_1sec_CallbackCalls; + CMOCK_MEM_INDEX_TYPE Delay_1sec_CallInstance; +} Mock; + +extern jmp_buf AbortFrame; +extern int GlobalExpectCount; +extern int GlobalVerifyOrder; + +void mock_CAN_Driver_Verify(void) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_MEM_INDEX_TYPE call_instance; + call_instance = Mock.CANSPIRead_CallInstance; + if (Mock.CANSPIRead_IgnoreBool) + call_instance = CMOCK_GUTS_NONE; + if (CMOCK_GUTS_NONE != call_instance) + { + UNITY_SET_DETAIL(CMockString_CANSPIRead); + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess); + } + if (Mock.CANSPIRead_CallbackFunctionPointer != NULL) + { + call_instance = CMOCK_GUTS_NONE; + (void)call_instance; + } + call_instance = Mock.mikrobus_logWrite_CallInstance; + if (Mock.mikrobus_logWrite_IgnoreBool) + call_instance = CMOCK_GUTS_NONE; + if (CMOCK_GUTS_NONE != call_instance) + { + UNITY_SET_DETAIL(CMockString_mikrobus_logWrite); + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess); + } + if (Mock.mikrobus_logWrite_CallbackFunctionPointer != NULL) + { + call_instance = CMOCK_GUTS_NONE; + (void)call_instance; + } + call_instance = Mock.Delay_1sec_CallInstance; + if (Mock.Delay_1sec_IgnoreBool) + call_instance = CMOCK_GUTS_NONE; + if (CMOCK_GUTS_NONE != call_instance) + { + UNITY_SET_DETAIL(CMockString_Delay_1sec); + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess); + } + if (Mock.Delay_1sec_CallbackFunctionPointer != NULL) + { + call_instance = CMOCK_GUTS_NONE; + (void)call_instance; + } +} + +void mock_CAN_Driver_Init(void) +{ + mock_CAN_Driver_Destroy(); +} + +void mock_CAN_Driver_Destroy(void) +{ + CMock_Guts_MemFreeAll(); + memset(&Mock, 0, sizeof(Mock)); + GlobalExpectCount = 0; + GlobalVerifyOrder = 0; +} + +uint8_t CANSPIRead(uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance; + UNITY_SET_DETAIL(CMockString_CANSPIRead); + cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.CANSPIRead_CallInstance); + Mock.CANSPIRead_CallInstance = CMock_Guts_MemNext(Mock.CANSPIRead_CallInstance); + if (Mock.CANSPIRead_IgnoreBool) + { + UNITY_CLR_DETAILS(); + if (cmock_call_instance == NULL) + return Mock.CANSPIRead_FinalReturn; + Mock.CANSPIRead_FinalReturn = cmock_call_instance->ReturnVal; + return cmock_call_instance->ReturnVal; + } + if (!Mock.CANSPIRead_CallbackBool && + Mock.CANSPIRead_CallbackFunctionPointer != NULL) + { + uint8_t cmock_cb_ret = Mock.CANSPIRead_CallbackFunctionPointer(idRx, dataRxTx, dataRxLen, canRcvFlags, Mock.CANSPIRead_CallbackCalls++); + UNITY_CLR_DETAILS(); + return cmock_cb_ret; + } + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore); + cmock_line = cmock_call_instance->LineNumber; + if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly); + if (cmock_call_instance->CallOrder < GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate); + if (!cmock_call_instance->IgnoreArg_idRx) + { + UNITY_SET_DETAILS(CMockString_CANSPIRead,CMockString_idRx); + if (cmock_call_instance->Expected_idRx == NULL) + { UNITY_TEST_ASSERT_NULL(idRx, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(cmock_call_instance->Expected_idRx, idRx, 1, cmock_line, CMockStringMismatch); } + } + if (!cmock_call_instance->IgnoreArg_dataRxTx) + { + UNITY_SET_DETAILS(CMockString_CANSPIRead,CMockString_dataRxTx); + if (cmock_call_instance->Expected_dataRxTx == NULL) + { UNITY_TEST_ASSERT_NULL(dataRxTx, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(cmock_call_instance->Expected_dataRxTx, dataRxTx, 1, cmock_line, CMockStringMismatch); } + } + if (!cmock_call_instance->IgnoreArg_dataRxLen) + { + UNITY_SET_DETAILS(CMockString_CANSPIRead,CMockString_dataRxLen); + if (cmock_call_instance->Expected_dataRxLen == NULL) + { UNITY_TEST_ASSERT_NULL(dataRxLen, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(cmock_call_instance->Expected_dataRxLen, dataRxLen, 1, cmock_line, CMockStringMismatch); } + } + if (!cmock_call_instance->IgnoreArg_canRcvFlags) + { + UNITY_SET_DETAILS(CMockString_CANSPIRead,CMockString_canRcvFlags); + if (cmock_call_instance->Expected_canRcvFlags == NULL) + { UNITY_TEST_ASSERT_NULL(canRcvFlags, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(cmock_call_instance->Expected_canRcvFlags, canRcvFlags, 1, cmock_line, CMockStringMismatch); } + } + if (Mock.CANSPIRead_CallbackFunctionPointer != NULL) + { + cmock_call_instance->ReturnVal = Mock.CANSPIRead_CallbackFunctionPointer(idRx, dataRxTx, dataRxLen, canRcvFlags, Mock.CANSPIRead_CallbackCalls++); + } + if (cmock_call_instance->ReturnThruPtr_idRx_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(idRx, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)idRx, (void*)cmock_call_instance->ReturnThruPtr_idRx_Val, + cmock_call_instance->ReturnThruPtr_idRx_Size); + } + if (cmock_call_instance->ReturnThruPtr_dataRxTx_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(dataRxTx, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)dataRxTx, (void*)cmock_call_instance->ReturnThruPtr_dataRxTx_Val, + cmock_call_instance->ReturnThruPtr_dataRxTx_Size); + } + if (cmock_call_instance->ReturnThruPtr_dataRxLen_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(dataRxLen, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)dataRxLen, (void*)cmock_call_instance->ReturnThruPtr_dataRxLen_Val, + cmock_call_instance->ReturnThruPtr_dataRxLen_Size); + } + if (cmock_call_instance->ReturnThruPtr_canRcvFlags_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(canRcvFlags, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)canRcvFlags, (void*)cmock_call_instance->ReturnThruPtr_canRcvFlags_Val, + cmock_call_instance->ReturnThruPtr_canRcvFlags_Size); + } + UNITY_CLR_DETAILS(); + return cmock_call_instance->ReturnVal; +} + +void CMockExpectParameters_CANSPIRead(CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance, uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags); +void CMockExpectParameters_CANSPIRead(CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance, uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags) +{ + cmock_call_instance->Expected_idRx = idRx; + cmock_call_instance->IgnoreArg_idRx = 0; + cmock_call_instance->ReturnThruPtr_idRx_Used = 0; + cmock_call_instance->Expected_dataRxTx = dataRxTx; + cmock_call_instance->IgnoreArg_dataRxTx = 0; + cmock_call_instance->ReturnThruPtr_dataRxTx_Used = 0; + cmock_call_instance->Expected_dataRxLen = dataRxLen; + cmock_call_instance->IgnoreArg_dataRxLen = 0; + cmock_call_instance->ReturnThruPtr_dataRxLen_Used = 0; + cmock_call_instance->Expected_canRcvFlags = canRcvFlags; + cmock_call_instance->IgnoreArg_canRcvFlags = 0; + cmock_call_instance->ReturnThruPtr_canRcvFlags_Used = 0; +} + +void CANSPIRead_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, uint8_t cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_CANSPIRead_CALL_INSTANCE)); + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.CANSPIRead_CallInstance = CMock_Guts_MemChain(Mock.CANSPIRead_CallInstance, cmock_guts_index); + Mock.CANSPIRead_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->ReturnVal = cmock_to_return; + Mock.CANSPIRead_IgnoreBool = (char)1; +} + +void CANSPIRead_CMockStopIgnore(void) +{ + if(Mock.CANSPIRead_IgnoreBool) + Mock.CANSPIRead_CallInstance = CMock_Guts_MemNext(Mock.CANSPIRead_CallInstance); + Mock.CANSPIRead_IgnoreBool = (char)0; +} + +void CANSPIRead_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags, uint8_t cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_CANSPIRead_CALL_INSTANCE)); + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.CANSPIRead_CallInstance = CMock_Guts_MemChain(Mock.CANSPIRead_CallInstance, cmock_guts_index); + Mock.CANSPIRead_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->CallOrder = ++GlobalExpectCount; + CMockExpectParameters_CANSPIRead(cmock_call_instance, idRx, dataRxTx, dataRxLen, canRcvFlags); + cmock_call_instance->ReturnVal = cmock_to_return; +} + +void CANSPIRead_AddCallback(CMOCK_CANSPIRead_CALLBACK Callback) +{ + Mock.CANSPIRead_IgnoreBool = (char)0; + Mock.CANSPIRead_CallbackBool = (char)1; + Mock.CANSPIRead_CallbackFunctionPointer = Callback; +} + +void CANSPIRead_Stub(CMOCK_CANSPIRead_CALLBACK Callback) +{ + Mock.CANSPIRead_IgnoreBool = (char)0; + Mock.CANSPIRead_CallbackBool = (char)0; + Mock.CANSPIRead_CallbackFunctionPointer = Callback; +} + +void CANSPIRead_CMockReturnMemThruPtr_idRx(UNITY_LINE_TYPE cmock_line, uint32_t* idRx, size_t cmock_size) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_idRx_Used = 1; + cmock_call_instance->ReturnThruPtr_idRx_Val = idRx; + cmock_call_instance->ReturnThruPtr_idRx_Size = cmock_size; +} + +void CANSPIRead_CMockReturnMemThruPtr_dataRxTx(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, size_t cmock_size) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_dataRxTx_Used = 1; + cmock_call_instance->ReturnThruPtr_dataRxTx_Val = dataRxTx; + cmock_call_instance->ReturnThruPtr_dataRxTx_Size = cmock_size; +} + +void CANSPIRead_CMockReturnMemThruPtr_dataRxLen(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxLen, size_t cmock_size) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_dataRxLen_Used = 1; + cmock_call_instance->ReturnThruPtr_dataRxLen_Val = dataRxLen; + cmock_call_instance->ReturnThruPtr_dataRxLen_Size = cmock_size; +} + +void CANSPIRead_CMockReturnMemThruPtr_canRcvFlags(UNITY_LINE_TYPE cmock_line, uint8_t* canRcvFlags, size_t cmock_size) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_canRcvFlags_Used = 1; + cmock_call_instance->ReturnThruPtr_canRcvFlags_Val = canRcvFlags; + cmock_call_instance->ReturnThruPtr_canRcvFlags_Size = cmock_size; +} + +void CANSPIRead_CMockIgnoreArg_idRx(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_idRx = 1; +} + +void CANSPIRead_CMockIgnoreArg_dataRxTx(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_dataRxTx = 1; +} + +void CANSPIRead_CMockIgnoreArg_dataRxLen(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_dataRxLen = 1; +} + +void CANSPIRead_CMockIgnoreArg_canRcvFlags(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_CANSPIRead_CALL_INSTANCE* cmock_call_instance = (CMOCK_CANSPIRead_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.CANSPIRead_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_canRcvFlags = 1; +} + +void mikrobus_logWrite(uint8_t* dataRxTx, const LOGOPT_t opt) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance; + UNITY_SET_DETAIL(CMockString_mikrobus_logWrite); + cmock_call_instance = (CMOCK_mikrobus_logWrite_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.mikrobus_logWrite_CallInstance); + Mock.mikrobus_logWrite_CallInstance = CMock_Guts_MemNext(Mock.mikrobus_logWrite_CallInstance); + if (Mock.mikrobus_logWrite_IgnoreBool) + { + UNITY_CLR_DETAILS(); + return; + } + if (!Mock.mikrobus_logWrite_CallbackBool && + Mock.mikrobus_logWrite_CallbackFunctionPointer != NULL) + { + Mock.mikrobus_logWrite_CallbackFunctionPointer(dataRxTx, opt, Mock.mikrobus_logWrite_CallbackCalls++); + UNITY_CLR_DETAILS(); + return; + } + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore); + cmock_line = cmock_call_instance->LineNumber; + if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly); + if (cmock_call_instance->CallOrder < GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate); + if (!cmock_call_instance->IgnoreArg_dataRxTx) + { + UNITY_SET_DETAILS(CMockString_mikrobus_logWrite,CMockString_dataRxTx); + if (cmock_call_instance->Expected_dataRxTx == NULL) + { UNITY_TEST_ASSERT_NULL(dataRxTx, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(cmock_call_instance->Expected_dataRxTx, dataRxTx, 1, cmock_line, CMockStringMismatch); } + } + if (!cmock_call_instance->IgnoreArg_opt) + { + UNITY_SET_DETAILS(CMockString_mikrobus_logWrite,CMockString_opt); + UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(&cmock_call_instance->Expected_opt), (void*)(&opt), sizeof(LOGOPT_t), cmock_line, CMockStringMismatch); + } + if (Mock.mikrobus_logWrite_CallbackFunctionPointer != NULL) + { + Mock.mikrobus_logWrite_CallbackFunctionPointer(dataRxTx, opt, Mock.mikrobus_logWrite_CallbackCalls++); + } + if (cmock_call_instance->ReturnThruPtr_dataRxTx_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(dataRxTx, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)dataRxTx, (void*)cmock_call_instance->ReturnThruPtr_dataRxTx_Val, + cmock_call_instance->ReturnThruPtr_dataRxTx_Size); + } + UNITY_CLR_DETAILS(); +} + +void CMockExpectParameters_mikrobus_logWrite(CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance, uint8_t* dataRxTx, const LOGOPT_t opt); +void CMockExpectParameters_mikrobus_logWrite(CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance, uint8_t* dataRxTx, const LOGOPT_t opt) +{ + cmock_call_instance->Expected_dataRxTx = dataRxTx; + cmock_call_instance->IgnoreArg_dataRxTx = 0; + cmock_call_instance->ReturnThruPtr_dataRxTx_Used = 0; + memcpy((void*)(&cmock_call_instance->Expected_opt), (void*)(&opt), + sizeof(LOGOPT_t[sizeof(opt) == sizeof(LOGOPT_t) ? 1 : -1])); /* add LOGOPT_t to :treat_as_array if this causes an error */ + cmock_call_instance->IgnoreArg_opt = 0; +} + +void mikrobus_logWrite_CMockIgnore(void) +{ + Mock.mikrobus_logWrite_IgnoreBool = (char)1; +} + +void mikrobus_logWrite_CMockStopIgnore(void) +{ + Mock.mikrobus_logWrite_IgnoreBool = (char)0; +} + +void mikrobus_logWrite_CMockExpect(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, const LOGOPT_t opt) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_mikrobus_logWrite_CALL_INSTANCE)); + CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance = (CMOCK_mikrobus_logWrite_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.mikrobus_logWrite_CallInstance = CMock_Guts_MemChain(Mock.mikrobus_logWrite_CallInstance, cmock_guts_index); + Mock.mikrobus_logWrite_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->CallOrder = ++GlobalExpectCount; + CMockExpectParameters_mikrobus_logWrite(cmock_call_instance, dataRxTx, opt); +} + +void mikrobus_logWrite_AddCallback(CMOCK_mikrobus_logWrite_CALLBACK Callback) +{ + Mock.mikrobus_logWrite_IgnoreBool = (char)0; + Mock.mikrobus_logWrite_CallbackBool = (char)1; + Mock.mikrobus_logWrite_CallbackFunctionPointer = Callback; +} + +void mikrobus_logWrite_Stub(CMOCK_mikrobus_logWrite_CALLBACK Callback) +{ + Mock.mikrobus_logWrite_IgnoreBool = (char)0; + Mock.mikrobus_logWrite_CallbackBool = (char)0; + Mock.mikrobus_logWrite_CallbackFunctionPointer = Callback; +} + +void mikrobus_logWrite_CMockReturnMemThruPtr_dataRxTx(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, size_t cmock_size) +{ + CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance = (CMOCK_mikrobus_logWrite_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mikrobus_logWrite_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_dataRxTx_Used = 1; + cmock_call_instance->ReturnThruPtr_dataRxTx_Val = dataRxTx; + cmock_call_instance->ReturnThruPtr_dataRxTx_Size = cmock_size; +} + +void mikrobus_logWrite_CMockIgnoreArg_dataRxTx(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance = (CMOCK_mikrobus_logWrite_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mikrobus_logWrite_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_dataRxTx = 1; +} + +void mikrobus_logWrite_CMockIgnoreArg_opt(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_mikrobus_logWrite_CALL_INSTANCE* cmock_call_instance = (CMOCK_mikrobus_logWrite_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mikrobus_logWrite_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_opt = 1; +} + +void Delay_1sec(void) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_Delay_1sec_CALL_INSTANCE* cmock_call_instance; + UNITY_SET_DETAIL(CMockString_Delay_1sec); + cmock_call_instance = (CMOCK_Delay_1sec_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.Delay_1sec_CallInstance); + Mock.Delay_1sec_CallInstance = CMock_Guts_MemNext(Mock.Delay_1sec_CallInstance); + if (Mock.Delay_1sec_IgnoreBool) + { + UNITY_CLR_DETAILS(); + return; + } + if (!Mock.Delay_1sec_CallbackBool && + Mock.Delay_1sec_CallbackFunctionPointer != NULL) + { + Mock.Delay_1sec_CallbackFunctionPointer(Mock.Delay_1sec_CallbackCalls++); + UNITY_CLR_DETAILS(); + return; + } + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore); + cmock_line = cmock_call_instance->LineNumber; + if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly); + if (cmock_call_instance->CallOrder < GlobalVerifyOrder) + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate); + if (Mock.Delay_1sec_CallbackFunctionPointer != NULL) + { + Mock.Delay_1sec_CallbackFunctionPointer(Mock.Delay_1sec_CallbackCalls++); + } + UNITY_CLR_DETAILS(); +} + +void Delay_1sec_CMockIgnore(void) +{ + Mock.Delay_1sec_IgnoreBool = (char)1; +} + +void Delay_1sec_CMockStopIgnore(void) +{ + Mock.Delay_1sec_IgnoreBool = (char)0; +} + +void Delay_1sec_CMockExpect(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_Delay_1sec_CALL_INSTANCE)); + CMOCK_Delay_1sec_CALL_INSTANCE* cmock_call_instance = (CMOCK_Delay_1sec_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.Delay_1sec_CallInstance = CMock_Guts_MemChain(Mock.Delay_1sec_CallInstance, cmock_guts_index); + Mock.Delay_1sec_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->CallOrder = ++GlobalExpectCount; +} + +void Delay_1sec_AddCallback(CMOCK_Delay_1sec_CALLBACK Callback) +{ + Mock.Delay_1sec_IgnoreBool = (char)0; + Mock.Delay_1sec_CallbackBool = (char)1; + Mock.Delay_1sec_CallbackFunctionPointer = Callback; +} + +void Delay_1sec_Stub(CMOCK_Delay_1sec_CALLBACK Callback) +{ + Mock.Delay_1sec_IgnoreBool = (char)0; + Mock.Delay_1sec_CallbackBool = (char)0; + Mock.Delay_1sec_CallbackFunctionPointer = Callback; +} + diff --git a/CAN_App/build/test/mocks/mock_CAN_Driver.h b/CAN_App/build/test/mocks/mock_CAN_Driver.h new file mode 100644 index 0000000..da2d1a2 --- /dev/null +++ b/CAN_App/build/test/mocks/mock_CAN_Driver.h @@ -0,0 +1,96 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ +#ifndef _MOCK_CAN_DRIVER_H +#define _MOCK_CAN_DRIVER_H + +#include "unity.h" +#include "CAN_Driver.h" + +/* Ignore the following warnings, since we are copying code */ +#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0))) +#pragma GCC diagnostic push +#endif +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wpragmas" +#endif +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wduplicate-decl-specifier" +#endif + +void mock_CAN_Driver_Init(void); +void mock_CAN_Driver_Destroy(void); +void mock_CAN_Driver_Verify(void); + + + + +#define CANSPIRead_IgnoreAndReturn(cmock_retval) CANSPIRead_CMockIgnoreAndReturn(__LINE__, cmock_retval) +void CANSPIRead_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, uint8_t cmock_to_return); +#define CANSPIRead_StopIgnore() CANSPIRead_CMockStopIgnore() +void CANSPIRead_CMockStopIgnore(void); +#define CANSPIRead_ExpectAndReturn(idRx, dataRxTx, dataRxLen, canRcvFlags, cmock_retval) CANSPIRead_CMockExpectAndReturn(__LINE__, idRx, dataRxTx, dataRxLen, canRcvFlags, cmock_retval) +void CANSPIRead_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags, uint8_t cmock_to_return); +typedef uint8_t (* CMOCK_CANSPIRead_CALLBACK)(uint32_t* idRx, uint8_t* dataRxTx, uint8_t* dataRxLen, uint8_t* canRcvFlags, int cmock_num_calls); +void CANSPIRead_AddCallback(CMOCK_CANSPIRead_CALLBACK Callback); +void CANSPIRead_Stub(CMOCK_CANSPIRead_CALLBACK Callback); +#define CANSPIRead_StubWithCallback CANSPIRead_Stub +#define CANSPIRead_ReturnThruPtr_idRx(idRx) CANSPIRead_CMockReturnMemThruPtr_idRx(__LINE__, idRx, sizeof(uint32_t)) +#define CANSPIRead_ReturnArrayThruPtr_idRx(idRx, cmock_len) CANSPIRead_CMockReturnMemThruPtr_idRx(__LINE__, idRx, cmock_len * sizeof(*idRx)) +#define CANSPIRead_ReturnMemThruPtr_idRx(idRx, cmock_size) CANSPIRead_CMockReturnMemThruPtr_idRx(__LINE__, idRx, cmock_size) +void CANSPIRead_CMockReturnMemThruPtr_idRx(UNITY_LINE_TYPE cmock_line, uint32_t* idRx, size_t cmock_size); +#define CANSPIRead_ReturnThruPtr_dataRxTx(dataRxTx) CANSPIRead_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, sizeof(uint8_t)) +#define CANSPIRead_ReturnArrayThruPtr_dataRxTx(dataRxTx, cmock_len) CANSPIRead_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, cmock_len * sizeof(*dataRxTx)) +#define CANSPIRead_ReturnMemThruPtr_dataRxTx(dataRxTx, cmock_size) CANSPIRead_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, cmock_size) +void CANSPIRead_CMockReturnMemThruPtr_dataRxTx(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, size_t cmock_size); +#define CANSPIRead_ReturnThruPtr_dataRxLen(dataRxLen) CANSPIRead_CMockReturnMemThruPtr_dataRxLen(__LINE__, dataRxLen, sizeof(uint8_t)) +#define CANSPIRead_ReturnArrayThruPtr_dataRxLen(dataRxLen, cmock_len) CANSPIRead_CMockReturnMemThruPtr_dataRxLen(__LINE__, dataRxLen, cmock_len * sizeof(*dataRxLen)) +#define CANSPIRead_ReturnMemThruPtr_dataRxLen(dataRxLen, cmock_size) CANSPIRead_CMockReturnMemThruPtr_dataRxLen(__LINE__, dataRxLen, cmock_size) +void CANSPIRead_CMockReturnMemThruPtr_dataRxLen(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxLen, size_t cmock_size); +#define CANSPIRead_ReturnThruPtr_canRcvFlags(canRcvFlags) CANSPIRead_CMockReturnMemThruPtr_canRcvFlags(__LINE__, canRcvFlags, sizeof(uint8_t)) +#define CANSPIRead_ReturnArrayThruPtr_canRcvFlags(canRcvFlags, cmock_len) CANSPIRead_CMockReturnMemThruPtr_canRcvFlags(__LINE__, canRcvFlags, cmock_len * sizeof(*canRcvFlags)) +#define CANSPIRead_ReturnMemThruPtr_canRcvFlags(canRcvFlags, cmock_size) CANSPIRead_CMockReturnMemThruPtr_canRcvFlags(__LINE__, canRcvFlags, cmock_size) +void CANSPIRead_CMockReturnMemThruPtr_canRcvFlags(UNITY_LINE_TYPE cmock_line, uint8_t* canRcvFlags, size_t cmock_size); +#define CANSPIRead_IgnoreArg_idRx() CANSPIRead_CMockIgnoreArg_idRx(__LINE__) +void CANSPIRead_CMockIgnoreArg_idRx(UNITY_LINE_TYPE cmock_line); +#define CANSPIRead_IgnoreArg_dataRxTx() CANSPIRead_CMockIgnoreArg_dataRxTx(__LINE__) +void CANSPIRead_CMockIgnoreArg_dataRxTx(UNITY_LINE_TYPE cmock_line); +#define CANSPIRead_IgnoreArg_dataRxLen() CANSPIRead_CMockIgnoreArg_dataRxLen(__LINE__) +void CANSPIRead_CMockIgnoreArg_dataRxLen(UNITY_LINE_TYPE cmock_line); +#define CANSPIRead_IgnoreArg_canRcvFlags() CANSPIRead_CMockIgnoreArg_canRcvFlags(__LINE__) +void CANSPIRead_CMockIgnoreArg_canRcvFlags(UNITY_LINE_TYPE cmock_line); +#define mikrobus_logWrite_Ignore() mikrobus_logWrite_CMockIgnore() +void mikrobus_logWrite_CMockIgnore(void); +#define mikrobus_logWrite_StopIgnore() mikrobus_logWrite_CMockStopIgnore() +void mikrobus_logWrite_CMockStopIgnore(void); +#define mikrobus_logWrite_Expect(dataRxTx, opt) mikrobus_logWrite_CMockExpect(__LINE__, dataRxTx, opt) +void mikrobus_logWrite_CMockExpect(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, const LOGOPT_t opt); +typedef void (* CMOCK_mikrobus_logWrite_CALLBACK)(uint8_t* dataRxTx, const LOGOPT_t opt, int cmock_num_calls); +void mikrobus_logWrite_AddCallback(CMOCK_mikrobus_logWrite_CALLBACK Callback); +void mikrobus_logWrite_Stub(CMOCK_mikrobus_logWrite_CALLBACK Callback); +#define mikrobus_logWrite_StubWithCallback mikrobus_logWrite_Stub +#define mikrobus_logWrite_ReturnThruPtr_dataRxTx(dataRxTx) mikrobus_logWrite_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, sizeof(uint8_t)) +#define mikrobus_logWrite_ReturnArrayThruPtr_dataRxTx(dataRxTx, cmock_len) mikrobus_logWrite_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, cmock_len * sizeof(*dataRxTx)) +#define mikrobus_logWrite_ReturnMemThruPtr_dataRxTx(dataRxTx, cmock_size) mikrobus_logWrite_CMockReturnMemThruPtr_dataRxTx(__LINE__, dataRxTx, cmock_size) +void mikrobus_logWrite_CMockReturnMemThruPtr_dataRxTx(UNITY_LINE_TYPE cmock_line, uint8_t* dataRxTx, size_t cmock_size); +#define mikrobus_logWrite_IgnoreArg_dataRxTx() mikrobus_logWrite_CMockIgnoreArg_dataRxTx(__LINE__) +void mikrobus_logWrite_CMockIgnoreArg_dataRxTx(UNITY_LINE_TYPE cmock_line); +#define mikrobus_logWrite_IgnoreArg_opt() mikrobus_logWrite_CMockIgnoreArg_opt(__LINE__) +void mikrobus_logWrite_CMockIgnoreArg_opt(UNITY_LINE_TYPE cmock_line); +#define Delay_1sec_Ignore() Delay_1sec_CMockIgnore() +void Delay_1sec_CMockIgnore(void); +#define Delay_1sec_StopIgnore() Delay_1sec_CMockStopIgnore() +void Delay_1sec_CMockStopIgnore(void); +#define Delay_1sec_Expect() Delay_1sec_CMockExpect(__LINE__) +void Delay_1sec_CMockExpect(UNITY_LINE_TYPE cmock_line); +typedef void (* CMOCK_Delay_1sec_CALLBACK)(int cmock_num_calls); +void Delay_1sec_AddCallback(CMOCK_Delay_1sec_CALLBACK Callback); +void Delay_1sec_Stub(CMOCK_Delay_1sec_CALLBACK Callback); +#define Delay_1sec_StubWithCallback Delay_1sec_Stub + +#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0))) +#pragma GCC diagnostic pop +#endif +#endif + +#endif diff --git a/CAN_App/build/test/out/c/CAN.o b/CAN_App/build/test/out/c/CAN.o new file mode 100644 index 0000000000000000000000000000000000000000..27d8c57da423b5ac75ed101c1d07297644a36f89 GIT binary patch literal 3111 zcmbtWU2GIp6h3!;c4s@?ZJ`8cM7tP7RJz+g+fo6SmbF{iLerlpNp;zu>2_c{+sw?C zEo#!FNNi#gh%Y|)WOy^tClh0gk!U~@AN9p2LW1$Z1W14v6Y4ql&MrGG_~K3O`Tfp0 zcV^DqdkdGsKtl@wbZ|+5m}^vBE(@}VY>x!6TNApBED956Vuh@m6{e@s1Wf!8%Q?LTRanQ>%X`Qa6$hDa-Zb1DqQ;AHj{} zU17RI>KEztip(Ym0#FtVb|BxtAp@(nHV3uKOgJ`u-a1|(f28%2q)?}Z1Ik%s z*D+9p!HNW+0^@3M^yEx*qHp=U_N~}w=dlH3+C?U-(^UvIkuF`C^Gs>O0xe86 zq$`HJ;1nnGO9cpdmNG|SSd(Y4WV0|?1wBG_r7LITeAdEN1Ci6Xh?Xo8 z{MXn%(DkFbj7=!1FxH04=>u6C+RI+k59woV(ry4{Cs}l1z}I^K)jc{sUkm7aHHryt zV=riJy4>+h$2PV-*t!e9lEP%|sIFrU5y}u3+RsZg#o+b!TFMvO54Qv8G3GZByxDIY zYdhRd=CI#P6%)JP=dK82w4Ro|SSOB4go8Noo#LqdbzJfNF2W+{d1NYL4=ahAV1w zT5Ujfe5q0}pkeL&2^_b8|88s^DEV>QTrzA_Mn^`5 zq6a38rJjS);fh&uqq*hi_|)mCp6H@oK5OLNm%RNCUz!;7w)fyvAcK^4Jp~F#pW_k| z)Ujtd6%u`C+8AXX7ePsfC{pq=-UZva?FdqAZuB0jOH@tZW>DJ6z4!3L3mg5vt=vXb z*q%p8WZ7SrUam`YG6*l7*g`s5mqzN+I7%X4Y|g~}QH)0$07WKR!ZUGJ`(UInd^QPJ zxOp_*oyaXS+H~j~dORBMP1LLMlkzm&nJD^+n1=fjJ%!BZ#^fK3cO?~F0^?r~MU=cS zde`CruKz`z2VmKg=&B$yx+M|XKR)zS>IRwJ^gBK66OZGS6ERPaH!|;w`bTjhr zBjoEpm6rS;Tcbzy%w@nKP6vZuR3DE53k0kv|x7n5?)uxT|qf(J-@p|2j>Bbw` z-gFZ#oC*OBsf0M7TsR^G2P95hDpf*)V}&?y>!BBJoO~Wj!O{$M2#%GmnrL>9*0}B99ejnDY(tmmL#&&_4v>W8i(^^OB(FNNzeiJP}a% zr~_w~5%SpQwR2v2Iy-VU#(NCf*NGSe@qjlr8Uu0tK7eH7I5B7tUhv+U2mt$*TbbQ?I=eHUp50mL zDePR;3On-yySE<&FrdB$!!9%`O9`3Lh2{}szUT+W@f19m4#zX_q~SRV&vR&u&_=o= zrR-7wpfzn_7)M5XSNgsr!$y(LqTVIT-E=GuCaol!PX^t43c(V%?#f9Nr3XZ+!Q7Pdn_N>! zvJHfe{N?LQ3v-L6UM7^aAr!PAB;9)#j)P>C7aN52aruFW%(5b~B4G(zY!pnJu)M(1 zlA+s+#^&2qz2XvfQ=mO0c^XsDexj;VDuWXyB?wc_ouKE{v+7*81Z9%FfQTvpShN99 zkE`%D!0d`KlZS|ULYeD^lYN+;Qo2>v_j2E1dL)&G$tyBt%9N@?R1u5J5^-){8E=p6y;j)C@hyfUX^J(_DApg+~7A<5wh#0T*}|J^lMAA-6$EK;gImBc=4O zu1*~I+Hjzc6~OWlJfFfN(LU*r{0JoSD?IcGVt=5NprlRY1?`wJb?LiH1qtd76Jx(5mtC(hx99q6%A3M6{t!w`&~;jCyEz3t)W!w$$5z3Jh9 z`9MzOmWSttQQU=ii{9|CA~UFdP>JFs&_4GOTrox;LXT3sedVk8CubDzc6gtQw{LwF z&kmz_uWM~T`Vh{7(IMz@72@p|U&RcN3wn$gZ@>8}lxN_yz7v|C_#FPN&}Y6DAO7@J zyuUO0X50wc-|%G0W~p8&-f{GGGf*u1PA!o2QpqtlL(S3cis=Sw)v`k;*K?w9k?Rf? X%>M1|Z7d_fFR}Gs8~Hu|U-S77>uJf2 literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/cmock.o b/CAN_App/build/test/out/c/cmock.o new file mode 100644 index 0000000000000000000000000000000000000000..beeb8439e3714a5c706b413af6fec7801f4be5c5 GIT binary patch literal 8639 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 LyQn@&@1On`=D**_ literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/mock_CAN_Driver.o b/CAN_App/build/test/out/c/mock_CAN_Driver.o new file mode 100644 index 0000000000000000000000000000000000000000..d09fb55aa28c8f9cf16be941a12bf0eb8a4cfee1 GIT binary patch literal 33115 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 M4LD$F-rxFv0bt3NfB*mh literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/test_CAN.o b/CAN_App/build/test/out/c/test_CAN.o new file mode 100644 index 0000000000000000000000000000000000000000..f9c25d12ca13bca363ffb3974406eb59ece94144 GIT binary patch literal 5154 zcmb_fTWlOx89rxc_F}J-I87*Zn&Nfiq%N^{ZO1r{+a&8-Z8yGYvldt~EW5ME>m}=1 zYi2gFEszR9K~<^bQmGOss6?tLH>odusClYrxQI6-5HGwSwGv1bydo;4eE&H!f4=ko-}%o!=YEQJjxtIQkbPbvoh2QyrW>)>v{d4`~tkeBZ^C5N}g5EYlr?X zP5zeBsUcO*lm7zv5WJtc{$ePnS$aG*aM7d4Jf$=CKE8juycWr8A5RTls^i^9=>!=A zDb2tex)9JXM*s0=@Um)17`hzog7<}yI^I2$qOFXryS!r_?;?44c#{4`2I@%P$N2sP z{(?#JF4g}pc^`A7&p1=ur=iy(2xo9EO6;y!flV&+c2KYTxphXW8rUkZfRg=Jx%56|5Jf zta*5p39tTMmye$G(HS3gd#EYOvz{dw!LFaU9t!IkU-I*q7LH{|!x#@>cwtczNR-p? z$K3pF}TvwI3sCjBmBKMPs|vnMgw_^QG5@PWGeicS1TX znj;aNCgg8W(Pet|JieEI+IsHvWj}WPHPyR!aC`KRl)j}h)>Q8sFa+W}k2M-bPZGUl zdSN*+F>o<4FpwNf_9yx_oSG>vmxL*-vVmgWGS)9|jSTk<4<-6a?1^XGM!bwV*GnND zG1l`I)9vz($T=)@rgsyzP%K)hnx%vkHPbGaOi@gfO%P(?eT!6tqV{^#auW44JuHp7 zVk;bNpr|(Ll|1H`WniJ0UaZy%6~RKfzE-K`5kk8zL-6!G;>M@`q=v>u@GDw($KUB! zLSYpj#h{b%!XR0DPSU2PFE7kyS8~g<3)$fzroWz`zYi#x{^qF_^jB-8T3Xz6SX8H4 z;mFaqpbY2c7N>LLE7>Uz&&@7OG0t@87nibVCfurj6U;c#BQqquNpU|wl;;~u2`_QuVG-;FsUZ%tNjnXJv*nYMLm+>&0 z#d#F!X7TuyxQ1;*OY*B7T5Pl{@=W|vSLCUVXFE6;`lAfWICOEU5bijqs zNEJ;oRgt?DkMl>O>Bqi~BPn*UX$Pj3`ja#0w+<}PRw6lxjvZKw5ngjX8g2!)?&m}x`M0=pU!3=pOh#A zc}60df^?PA*%~BWopfeH9+&6^$a50C45@TK0D4O5-hk|p=%F6I6qJ^wHQw=P77H0`avT~M%RJt$uy6^hBU0-+K*Zt1dQOtCGjxz%Q;v>UH8$c@NB7tq0%N7?^`OfwwgV z3LKha^(pwS4#TAJen^_p`zaZCQ`0zW>RP=|a1qTjO4K77r}akjtv>z9Es{m!^qOv| zC!q9KS3?$!-*2*$;-9XTp0Fxr)&H(qW)Iu~j%RT0LDH+aKDD}iE7u#RS8;PX4!a86opzTw`S!x zgjXn=YgG^D3k6Hu^h;LWED75y;so+5*^U)d=VVzZ#*h3@-@bAqpkM%LkPm50BC4tYxb2Q*%5 A#Q*>R literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/test_CAN_Driver.o b/CAN_App/build/test/out/c/test_CAN_Driver.o new file mode 100644 index 0000000000000000000000000000000000000000..b3204bd50608230668d59651f2c21076011766ac GIT binary patch literal 3207 zcmb_eU2GIp6h3!;cDD}D7UXA9>|n5n(wVkYN{hfkYuJi34RjMBCF|_&?RIrLQ)gyt zX-G7|U`Qlsd@wN~@xd5hN%TdZP!k`0^2rC^G?JiizGxzzbMMSB(*i!|N$$Dl=ey_r zojG$G*R}vrsS9AQkTkGDz7h&qvO(-76@a^WsiO!c%)DkboY0Y`hw>84+_q{#@K8YH zc!zK+*?>=?FQmy*RD!{hwUou}L;o7#3KEE00fFTVlSO)h2YH#Wplf;c2${6XKRh;aXC>}0B z;$AqC!CgY_5aRxfaC9wV-cgG1XcD+1LmAKk+{YSiGDQM6mfbH!|1!#f7>f5>s+6z) zymsl^R9C)w*Zis%G36U#-{tFH|4Jj)1Tjs~HI>aIeL>X=!(@?B<0+`8?4)f{|2(!H zY}1@K%+NEZ7MI$5k-MQeadNsk>9>}-k8XGz+dU)|*h ziiVd7n{yf+{C7AIJEC}8jF0<&8%KFbB)>3#JyM1BI;q!$_2ErLcZ;} zO;fOr8?>4(9%IW5(WBq@2!oOj7Cb*RvoK8;PEDevoL>i#b7YOIkmzDLte`c#POZ&> zY1(|-b8rbgnpi?)&len8G0R1(l*1HVu8L+>E7Mc67pj%1>Dj|0@Uc0M_>>Jk+dYD( zs+5rxDsC&hJk@kPpM!Em+5R>fwXMW1B7?4w2VwPo3RNk;X1yf4fOY5AUm?4xhurZe zy|-L2;(UmEbOt=EEF}q+2<#@XeP9^J4Ha)&>4&Lpxa=LE7RL7~M~yLqbWIm#-GUQE zaxir4&FwGtvaR{juGJzyu9rPi*k-7GPxO(oXD2|u8^hA*g2luz@U8@iZ2!! z+Pk7~22B$OyMn>sAU4W)`Jo9xuyiKfw*pWy>G)AnabS%7wPiDPbduOm=u^+3d;=R5 zK85uu-RkcsflmIN$P|>i2v;%7aPESmXo6nx(%fkoF zQ=L{jG;5d5v*$0IA2yeK?{!`e4@5PDj$Y7H$fliS%P4k%!i8i2Y04)2PeH{fYEkB2mqk-myeFo=9bsWE5+@g>_8Q z-}eBTEPHw6k;On_7?}7;NIA{pYC<(cV1%=coJxawdDI8Y zm41)WAMb&f)LV=)LuGUtSxUdd=r^xoZNoy70a>Xx7@e5P;=f=929yOvw11V|LQd!` zeH5vb9V(;O7fD(AG*W35Dx(6lkv@-1k72K}LFCypB=5ZH zy~^knwUK^`OmvI#zBXd+Bqq!a_yaYo!E2pnb>4Rtd91YDc`ufoTFvLniRL?QlLxVi kr6w5^gnrsw6}q^ReXFahqW(PU&-cHrgVwZu^7L={AB@7)M*si- literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/test_CAN_Driver_runner.o b/CAN_App/build/test/out/c/test_CAN_Driver_runner.o new file mode 100644 index 0000000000000000000000000000000000000000..d254b566ba6dd34b73211bcb7598de14a7023004 GIT binary patch literal 6731 zcmb_geQX>@6`#4?J+E_de2$yOPSWID+}4Hq&UW0yO`GN%$Idw)&e7O_E2HB+*3Px( zz3bjx>VyiRMlCg=B^9VhC{iM%ssa_Me}Gz{AOci`76eFqsC>6Y6)vLU4@xP}HvHbq z?%eI2`z07@XWsnYZ{C}EGdnx8x%d?aytDJ>l95pe}efj`(;NP}_)3J?-*ZJ>E{T@Xkrt z8yWPGzKZ_+oCaB!1n(yQc**)b4U>oYjmHKDd>tgp2$B@kRl-8SyS4 z>ucc6B$i*gWV~_t@?|1kLX>4nTr#}bJM;1H6U$#DPW?5qLe!P#(RSrU%ATN%urrBM zPe)`c!TUB84On9Na5%9%({*ThJd!vq+$K)XbS0LPW~~}QwWf{tB_2$2PrID#^TbPW z8Xc=u>7`*SJ@A@XUP&x}&(oZ`m`p5xS9I~di`9@25~s(*mw)z7yft#1ufK)S{hKnf z-Y^_#>OnaU*Tv{lv`7;V8q}S-UO@etXGR9`b$gt&b>aI1x(1;wsP~}w z0UEkwubszlaq*^h^m4D#j$%UT{kHS`_Q~d~$=?T{Sj8X3m=eW-Qd2uxFRQepd7<=E zSv0qM7K&Bc(cDp*kVSL5Q7G!#O z7E7s2b)G%bnMHN|II?iXv(k<+>$zezQ?MA<_4z_EjWC3D5e666(&UZ@-9U*>^MD}( zLxT^^OifPDre-Fmrgsdpa}nxZ9mXEp^d$Us&e-|NiD|sT6IDA~$ro*=eV&J3hf1T5 z<`o;HnWNK3ZX4Y{o|L&dV>#>(e8Z36ClZkLBlDy$vp@1Kg8O8L z;GpVQW%j&82Lw8~U>6B|L4qUo4kpmT%CQ%vDqgRW&2sEdQkS@LG=r5l;n3K$mwDSX z+=i2EqX;9@Ug6u`0%Ghh6f^0<_)^KrR>q1|Oh?do4Do5DElVVq@)5i@^CyltISj7F zrFdUhmm-woj#G4)+43zkZq>Z4u1mNbw{uK;l?TZ|u!4Zp?Z!>JZd+5=`qv^daWd3s?QSuHAkCCm z2pvc}`-*qlZ0%KxY53p=#jqN89e#GJQ?*mnUB+W=jhh)i`!%>R=dmbR-|yie@GvuN ziux|Vk2$OiFn0pYeeQx<##(P;PbZdyFFVMy`b61ac zvvE^svU3DSQ>~qaddx{2DD^9aE^u>*bPw>JFjmyCz_`TrbO-Qt4o2CYZn8pXPuq3f zw{)X71|u7$;qrT8WQ6FgQDuV29ejODyz3;Itm#3M7`hOWjy36ov9&%2L9fj{Xw%lg zuQ5AA+P2Nx$WVAAKFQY#&+VODGeepdb6U2f+EcwP5oCq<__%0;^n^;*bX}A&9L0i&#S$ zdkPf;hqE|J&iQ}VwNZhUPKS92+w=vb@FirL!C9;H z4V~7(jSUCS8CRS$_}6bec|_(PR7{H7QA|uzR9t&lYL*&pqQ;1Cb@k_k1=64g~Ng6*y%zU$o{dh zU6H=qtm8vBMD`-`l}P49gp@WbY&WE+ z(0&+sOh}(W{vjbfqNGQa^bI9_M@c_X(u$H^g+%ufoxO=%S`K67OUqjzN$oX|WDkZR z(H&f82}rcr>+A?5y7lPnqmYJ#lv9`uiC$uzeG<}kAt7UQZP$Q$5`CQMEU&S(3d0WW z@<=1KQBwFJXrI1+TQcK=@ z1xBX=DSc2$Q6Fije$DP8FoD5=kaBd`~c)B8|E{)QT@GeYENm)s{U1^4ug0mL{#5@r z6iWgedUyP({!b{D2sre%Z19nyb$>x=^=O>dL35&=h*Aq4jh{!2)__0FxEy*nG*16N zDN_EFb~*G{QsZ=uYfeQMT_ZRH$mPt|Ge6~WaO8oV3&_c$KYiBav;o#Q{l{!hUv^O% zjmBwDZcdN79GVN+6S|&?s4L_dAY$AK`HkC(=1#U+aS@72fTdF2Dnpe;fT-&U0RoX1 z7g2Q)@4_Q7YDFom0^}iA`w($^qPv(#9;aV%80-j+B#1n~4nrT!S()lWYF^wb-Adk` zFM5h}CgWJgRn1A;3%JR;M7jofGOjM3C~ilhE*4+!$&)9UT!+0;$C@uy9K1>X14H8v zx(2%Jdt;;8VN6_~#N9{Uq}+Rw|5{bMg4NyCR&~?>Hu`Pq?Efol|$7n%JcVx5qu!_JMt8 z_U?jBX@xMQxB(T^2Y)mIq*kS^`uGQ_sA?r>DQRh?QbY?aZAx1O841)sN)aj#_xsK4 z&b`dTg^_mVoA39SncvLL?o9HIZH&@)b~1LQKu#9O8A~~VW%d+j?57UKp4l$zdrAOlys0>eG7VDvigVNvycyQ?mSR3VA6XS zdS5Nq>)J<6sYH5@L+}1Fy~*(Eh49MD{&V`9>+9>JeooD9_?&L9oHdf%6JB}T|LI@Y zCY$2co`CzcKTx(x*+%}p2|-yK;ngu;cy+R2bal`lJ|lV-J~P=6UX8d5RX?f)X{arH zcZA#0)kvES&xI%mtWa&B>e_$bUSAKdu7y{BZcA1kjf7Vp6J7kmqj`@2;WLB2^{3uV zuSV#tork-%S12RzWy6uK?v~@Qn&^9kx)r`#7e%2L5WfaDeUH#q0da_m67jQkE7Gl) z9PZY1cb8M+5yn0&jE0%L=%^gVv&AZ}MM-g_as^MTRo;iP0}qumSiFa=lJ>zcaL&l) zqWyj2fdndceK>X6NRJuWY;4vzX(S9(lhKJeBYm}*g26;)G&3vJrM=J}&unBdkTJ63 znVc{zwzJH|HizZ5t}(vk+fwDehh8wGvTaO_+ez}RzB?RF4Z!Pi22eZ!Lll&3J3CI+ zw#O>$XoK6pj$%q_vvw4BN^P>JZ0{owY|tLf6{XF#NApSPce1E#?@0(YXphzarR!x; zX=gxCWcNDW5gjX-)V;&wlm7nB1OCp=Kv$r{-@cH`r;Q`CM%pk_2~=ZdV(!RNZ%=zq zx4(UsU3`Vrh+WX4vlHj8#9Yi|Tc^Nqch!6*J?j@)KAla?rj4XOmCiw;7bJpkRAuKf zX3k%#=G2AIDYXeZg=1$`nVxK@l;mzz7k#RYo*JAuJ{E}%jh^V6=-I&P(Gc=r7~%zeVO}ShsgOBvK{r(&8dkKC;Ns6 zqZ2H`ecB(nyA>~=L)fLz%jfvEuS1(T`tzn~q;n(~k1ZJNPg3Bv1p`JdmYVNm&r9A{ z%6GB9NWQ`5$r};ze}>WxPc*97)kaIDGC*fq*;>vQ_8|S`{G!6Lne()dx?7|(5x@Qc8M@D?G@hq zHW=6Nd?p^7A6!~A61o0N9@F90A3}VpY0DDHQu!p_o0;VkW)g#|wp8pdy`>_AgQl4= zSxwbXU|g-)TU~=_b}*e}+N<0}4QTyinZymz;e0L|9Wxe&Ov4yTrDOBV*}hB5Az0B( z?bO`oEwb8ZcQ5`P@LF%9=+QA_Hid=sZ|)k#V5-E@*exXenRH@4pEPLRnD!cXHAS;V z4sQcGiG(nG7}4cUFo<@Deb6hwI{n82ZgWAts`HpX6uvnE7;+g2?!OZ~6Ai&rV~b zbkE>5Vr_p#uO0U?cbeA8AsWBqUblN53ptw3FR%!$R>!?QwgsKW{EKJ=%RdDC>=vw8 zL%dAv((=y>KU*#5+8%Rr`G-qsSR-#L{~{^{F@F2|qdv6!EaR=rGg8;=Qb&^Q1n%S> z!gZo;kBkQGlWZ@#Rxh0C(1AY$iZpjx{zTj$q0sFTk+7_fiqJKvXhLys<47IFvoXM} z5DW5fBRSk%s!4kIs1#2H8I$Hkm=3gdu`&=7HBwiI<(>dWZ_l7jlPS9#4;|SsHzK#Q z;xD+(QTb;)I&@E>NBMK zB60shrg3z@S&dBBIipf0Z3oxuS02!JJfLeo(D~G9U0W#>%cxBufw@Tvmh>j~5xwPt zUh|UP@O@ps;C@oCp3>d!+w|(usW}_y`Zd?UXz4axo4VkB*8QAbLrxqnbie=5^kmjB zv(v{jbLr`UOd`L4&s=tTIF$?McrM|^Q7SE^VlR?@x zg4jRsqm&gDv295Lf8PCvg2I20{0Ei)p!6Tyz<=XCiEVDyuIK6ZBA7Kl$Ovs;<-QZN z`wQPiAto;S{$fs3QaTIrQxuqEqT5mYMj0kPo>?%WF_dLr95BQE{fGQ*Cymoxm-~-l zu5a;gIMJ`dZ;@W`d{=+HGbzK7Cz{&Or zltkVw&=T_f0@3T&F3`^u`mI9GD@3oT)T19*iE8myO4J0@1K!D6fu!dHO4_faqd>9; z*8&|778xKqkviFFpxpv}9;jEKGm85f5FPHE?0brP7$_*X$AAtB^aRi$fsiqLXqW~Y z8`1VfS1p=2DN8Bk5>Y&*6rUW6yGS9wlC~<2A}(!diBKF$kU|4W z8dlt>LJ=jUzR=@ge&pnZvkByq`;_ADQrta?dt7n9Qrw%0qkUQW>jskkdKEXXxJAWL zB>i|i>u)1fqC(*f{=oDZ-rHsL)pwdfjPT?82{;v`s1WMTNer(31+i zq>u~#>0-tsJV-B>pf3AIxkMjVh}M+k=zWo>N1>2Hg9;s2=!8Nv3(|H1Je!*c%q$h`T0T*&0Sdh@6nJrDlcl20zmQH<% zQlRL5-X(O*61iRZr8V{{q4OYWTWyglpSE=9jng>Y%u}RF(|Jp$8nnjgR=zU5Y_aqW z(Rc@PIY-5swk3WPp+mRv-eR%9RhAh^G)_0{m1&p7(r7e(S)~G)ODr9l3kkbXh^Q-O z2#grFVy?EVXdOyuSPadkg!fqt#mT{=KZdK#igHnX>a=_+Bw`Iye~C056k4vmY_EhUVw61>bD?AwqBl14l~8=VpN z6jmjbp2^sPSUheTr&Z02rDt(BVky!w)y6nZ+fbZdMO`fP)|+p>naMTZ8ZeES#hi(^ zcC!}@@95yY?i$JrWf=VjO@Kb^8c=pvS03~nBQ{Jn(1=dz8n#~F1dUXjJ&9l U%cJtntT4FuJ8cDv_X@}V07fsDL;wH) literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/c/unity.o b/CAN_App/build/test/out/c/unity.o new file mode 100644 index 0000000000000000000000000000000000000000..eee35439442a3e9a874edfffadac94778beab37d GIT binary patch literal 42314 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 NCpz}A#*t?j|6i0cY?J^1 literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/out/test_CAN.out b/CAN_App/build/test/out/test_CAN.out new file mode 100644 index 0000000000000000000000000000000000000000..3e8a140cd935cb86c17dcbc2c3f7866f4bd8577e GIT binary patch literal 206274 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 $7q(G%R%SsHS1VK+r_8oX#U>L)szI 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 Tk0ZrRG`EfFSeM>%lE?o8I7Ep5 literal 0 HcmV?d00001 diff --git a/CAN_App/build/test/preprocess/files/CAN_Driver.h b/CAN_App/build/test/preprocess/files/CAN_Driver.h new file mode 100644 index 0000000..1ac8087 --- /dev/null +++ b/CAN_App/build/test/preprocess/files/CAN_Driver.h @@ -0,0 +1,27 @@ + + + + + + + + + + +typedef enum logOpt + +{ + + _LOG_BYTE, + + _LOG_LINE + + } LOGOPT_t; + + + +uint8_t CANSPIRead( uint32_t *idRx , uint8_t *dataRxTx , uint8_t *dataRxLen, uint8_t *canRcvFlags ); + +void mikrobus_logWrite( uint8_t *dataRxTx, const LOGOPT_t opt ); + +void Delay_1sec( void ); diff --git a/CAN_App/build/test/preprocess/files/test_CAN.c b/CAN_App/build/test/preprocess/files/test_CAN.c new file mode 100644 index 0000000..537a51b --- /dev/null +++ b/CAN_App/build/test/preprocess/files/test_CAN.c @@ -0,0 +1,76 @@ +#include "build/test/mocks/mock_CAN_Driver.h" +#include "src/CAN.h" +#include "C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h" + + + + + + + + +void setUp(void) + +{ + +} + + + +void tearDown(void) + +{ + +} + + + +void test_GivenMessageReceived_ThenWritesToLog(void) + +{ + + CANSPIRead_CMockExpectAndReturn(18, 0, 0, 0, 0, 1); + + CANSPIRead_CMockIgnoreArg_idRx(19); + + CANSPIRead_CMockIgnoreArg_dataRxTx(20); + + CANSPIRead_CMockIgnoreArg_dataRxLen(21); + + CANSPIRead_CMockIgnoreArg_canRcvFlags(22); + + CANSPIRead_CMockReturnMemThruPtr_dataRxTx(23, "ABCDEFG", sizeof(uint8_t)); + + mikrobus_logWrite_CMockExpect(24, "ABCDEFG", _LOG_BYTE); + + Delay_1sec_CMockExpect(25); + + + + CAN_App(); + +} + + + +void test_GivenNoMessageReceived_ThenDoesNotWriteToLog(void) + +{ + + CANSPIRead_CMockExpectAndReturn(32, 0, 0, 0, 0, 0); + + CANSPIRead_CMockIgnoreArg_idRx(33); + + CANSPIRead_CMockIgnoreArg_dataRxTx(34); + + CANSPIRead_CMockIgnoreArg_dataRxLen(35); + + CANSPIRead_CMockIgnoreArg_canRcvFlags(36); + + CANSPIRead_CMockReturnMemThruPtr_dataRxTx(37, "ABCDEFG", sizeof(uint8_t)); + + + + CAN_App(); + +} diff --git a/CAN_App/build/test/preprocess/files/test_CAN_Driver.c b/CAN_App/build/test/preprocess/files/test_CAN_Driver.c new file mode 100644 index 0000000..44fddb0 --- /dev/null +++ b/CAN_App/build/test/preprocess/files/test_CAN_Driver.c @@ -0,0 +1,33 @@ +#include "src/CAN_Driver.h" +#include "C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h" + + + + + + + + +void setUp(void) + +{ + +} + + + +void tearDown(void) + +{ + +} + + + +void test_CAN_Driver_NeedToImplement(void) + +{ + + UnityIgnore( (("Need to Implement CAN_Driver")), (UNITY_UINT)(17)); + +} diff --git a/CAN_App/build/test/preprocess/includes/CAN_Driver.h b/CAN_App/build/test/preprocess/includes/CAN_Driver.h new file mode 100644 index 0000000..dcd024e --- /dev/null +++ b/CAN_App/build/test/preprocess/includes/CAN_Driver.h @@ -0,0 +1 @@ +--- [] diff --git a/CAN_App/build/test/preprocess/includes/test_CAN.c b/CAN_App/build/test/preprocess/includes/test_CAN.c new file mode 100644 index 0000000..b560dc8 --- /dev/null +++ b/CAN_App/build/test/preprocess/includes/test_CAN.c @@ -0,0 +1,4 @@ +--- +- C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h +- src/CAN.h +- build/test/mocks/mock_CAN_Driver.h diff --git a/CAN_App/build/test/preprocess/includes/test_CAN_Driver.c b/CAN_App/build/test/preprocess/includes/test_CAN_Driver.c new file mode 100644 index 0000000..fbb72da --- /dev/null +++ b/CAN_App/build/test/preprocess/includes/test_CAN_Driver.c @@ -0,0 +1,3 @@ +--- +- C:/Users/John/Documents/GitHub/MadScienceLabDocker/CAN_App/vendor/ceedling/vendor/unity/src/unity.h +- src/CAN_Driver.h diff --git a/CAN_App/build/test/results/test_CAN.pass b/CAN_App/build/test/results/test_CAN.pass new file mode 100644 index 0000000..f277fb6 --- /dev/null +++ b/CAN_App/build/test/results/test_CAN.pass @@ -0,0 +1,22 @@ +--- +:source: + :path: test + :file: test_CAN.c +:successes: +- :test: test_GivenMessageReceived_ThenWritesToLog + :line: 16 + :message: '' + :unity_test_time: 0 +- :test: test_GivenNoMessageReceived_ThenDoesNotWriteToLog + :line: 30 + :message: '' + :unity_test_time: 0 +:failures: [] +:ignores: [] +:counts: + :total: 2 + :passed: 2 + :failed: 0 + :ignored: 0 +:stdout: [] +:time: 0.99216639999986 diff --git a/CAN_App/build/test/results/test_CAN_Driver.pass b/CAN_App/build/test/results/test_CAN_Driver.pass new file mode 100644 index 0000000..2399e7f --- /dev/null +++ b/CAN_App/build/test/results/test_CAN_Driver.pass @@ -0,0 +1,18 @@ +--- +:source: + :path: test + :file: test_CAN_Driver.c +:successes: [] +:failures: [] +:ignores: +- :test: test_CAN_Driver_NeedToImplement + :line: 17 + :message: Need to Implement CAN_Driver + :unity_test_time: 0 +:counts: + :total: 1 + :passed: 0 + :failed: 0 + :ignored: 1 +:stdout: [] +:time: 1.0419115999998212 diff --git a/CAN_App/build/test/runners/test_CAN_Driver_runner.c b/CAN_App/build/test/runners/test_CAN_Driver_runner.c new file mode 100644 index 0000000..f9ee9f6 --- /dev/null +++ b/CAN_App/build/test/runners/test_CAN_Driver_runner.c @@ -0,0 +1,81 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ + +/*=======Automagically Detected Files To Include=====*/ +#include "unity.h" + +int GlobalExpectCount; +int GlobalVerifyOrder; +char* GlobalOrderError; + +/*=======External Functions This Runner Calls=====*/ +extern void setUp(void); +extern void tearDown(void); +extern void test_CAN_Driver_NeedToImplement(void); + + +/*=======Mock Management=====*/ +static void CMock_Init(void) +{ + GlobalExpectCount = 0; + GlobalVerifyOrder = 0; + GlobalOrderError = NULL; +} +static void CMock_Verify(void) +{ +} +static void CMock_Destroy(void) +{ +} + +/*=======Test Reset Options=====*/ +void resetTest(void); +void resetTest(void) +{ + tearDown(); + CMock_Verify(); + CMock_Destroy(); + CMock_Init(); + setUp(); +} +void verifyTest(void); +void verifyTest(void) +{ + CMock_Verify(); +} + +/*=======Test Runner Used To Run Each Test=====*/ +static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num) +{ + Unity.CurrentTestName = name; + Unity.CurrentTestLineNumber = line_num; +#ifdef UNITY_USE_COMMAND_LINE_ARGS + if (!UnityTestMatches()) + return; +#endif + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + CMock_Init(); + if (TEST_PROTECT()) + { + setUp(); + func(); + } + if (TEST_PROTECT()) + { + tearDown(); + CMock_Verify(); + } + CMock_Destroy(); + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} + +/*=======MAIN=====*/ +int main(void) +{ + UnityBegin("test_CAN_Driver.c"); + run_test(test_CAN_Driver_NeedToImplement, "test_CAN_Driver_NeedToImplement", 15); + + return UnityEnd(); +} diff --git a/CAN_App/build/test/runners/test_CAN_runner.c b/CAN_App/build/test/runners/test_CAN_runner.c new file mode 100644 index 0000000..bf90744 --- /dev/null +++ b/CAN_App/build/test/runners/test_CAN_runner.c @@ -0,0 +1,89 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ + +/*=======Automagically Detected Files To Include=====*/ +#include "unity.h" +#include "cmock.h" +#include "mock_CAN_Driver.h" + +int GlobalExpectCount; +int GlobalVerifyOrder; +char* GlobalOrderError; + +/*=======External Functions This Runner Calls=====*/ +extern void setUp(void); +extern void tearDown(void); +extern void test_GivenMessageReceived_ThenWritesToLog(void); +extern void test_GivenNoMessageReceived_ThenDoesNotWriteToLog(void); + + +/*=======Mock Management=====*/ +static void CMock_Init(void) +{ + GlobalExpectCount = 0; + GlobalVerifyOrder = 0; + GlobalOrderError = NULL; + mock_CAN_Driver_Init(); +} +static void CMock_Verify(void) +{ + mock_CAN_Driver_Verify(); +} +static void CMock_Destroy(void) +{ + mock_CAN_Driver_Destroy(); +} + +/*=======Test Reset Options=====*/ +void resetTest(void); +void resetTest(void) +{ + tearDown(); + CMock_Verify(); + CMock_Destroy(); + CMock_Init(); + setUp(); +} +void verifyTest(void); +void verifyTest(void) +{ + CMock_Verify(); +} + +/*=======Test Runner Used To Run Each Test=====*/ +static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num) +{ + Unity.CurrentTestName = name; + Unity.CurrentTestLineNumber = line_num; +#ifdef UNITY_USE_COMMAND_LINE_ARGS + if (!UnityTestMatches()) + return; +#endif + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + CMock_Init(); + if (TEST_PROTECT()) + { + setUp(); + func(); + } + if (TEST_PROTECT()) + { + tearDown(); + CMock_Verify(); + } + CMock_Destroy(); + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} + +/*=======MAIN=====*/ +int main(void) +{ + UnityBegin("test_CAN.c"); + run_test(test_GivenMessageReceived_ThenWritesToLog, "test_GivenMessageReceived_ThenWritesToLog", 16); + run_test(test_GivenNoMessageReceived_ThenDoesNotWriteToLog, "test_GivenNoMessageReceived_ThenDoesNotWriteToLog", 30); + + CMock_Guts_MemFreeFinal(); + return UnityEnd(); +} diff --git a/CAN_App/ceedling.cmd b/CAN_App/ceedling.cmd new file mode 100644 index 0000000..592d9ac --- /dev/null +++ b/CAN_App/ceedling.cmd @@ -0,0 +1 @@ +ruby vendor\ceedling\bin\ceedling %* diff --git a/CAN_App/project.yml b/CAN_App/project.yml new file mode 100644 index 0000000..e2913fb --- /dev/null +++ b/CAN_App/project.yml @@ -0,0 +1,104 @@ +--- + +# Notes: +# Sample project C code is not presently written to produce a release artifact. +# As such, release build options are disabled. +# This sample, therefore, only demonstrates running a collection of unit tests. + +:project: + :use_exceptions: FALSE + :use_test_preprocessor: TRUE + :use_auxiliary_dependencies: TRUE + :build_root: build +# :release_build: TRUE + :test_file_prefix: test_ + :which_ceedling: vendor/ceedling + :ceedling_version: 0.31.1 + :default_tasks: + - test:all + +#:test_build: +# :use_assembly: TRUE + +#:release_build: +# :output: MyApp.out +# :use_assembly: FALSE + +:environment: + +:extension: + :executable: .out + +:paths: + :test: + - +:test/** + - -:test/support + :source: + - src/** + :support: + - test/support + :libraries: [] + +:defines: + # in order to add common defines: + # 1) remove the trailing [] from the :common: section + # 2) add entries to the :common: section (e.g. :test: has TEST defined) + :common: &common_defines [] + :test: + - *common_defines + - TEST + :test_preprocess: + - *common_defines + - TEST + +:cmock: + :mock_prefix: mock_ + :when_no_prototypes: :warn + :enforce_strict_ordering: TRUE + :plugins: + - :ignore + - :callback + - :ignore_arg + - :return_thru_ptr + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + +# Add -gcov to the plugins list to make sure of the gcov plugin +# You will need to have gcov and gcovr both installed to make it work. +# For more information on these options, see docs in plugins/gcov +:gcov: + :reports: + - HtmlDetailed + :gcovr: + :html_medium_threshold: 75 + :html_high_threshold: 90 + +#:tools: +# Ceedling defaults to using gcc for compiling, linking, etc. +# As [:tools] is blank, gcc will be used (so long as it's in your system path) +# See documentation to configure a given toolchain for use + +# LIBRARIES +# These libraries are automatically injected into the build process. Those specified as +# common will be used in all types of builds. Otherwise, libraries can be injected in just +# tests or releases. These options are MERGED with the options in supplemental yaml files. +:libraries: + :placement: :end + :flag: "-l${1}" + :path_flag: "-L ${1}" + :system: [] # for example, you might list 'm' to grab the math library + :test: [] + :release: [] + +:plugins: + :load_paths: + - vendor/ceedling/plugins + :enabled: + - stdout_pretty_tests_report + - module_generator + - raw_output_report +... diff --git a/CAN_App/src/CAN.c b/CAN_App/src/CAN.c new file mode 100644 index 0000000..4206351 --- /dev/null +++ b/CAN_App/src/CAN.c @@ -0,0 +1,27 @@ +// Example file for CAN SPI project +#include "CAN.h" +#include "CAN_Driver.h" + +void CAN_App(void) +{ + char dataRxTx[ 8 ] = { 0 }; + uint8_t dataRxLen = 0; + uint8_t msgRcvd = 0; + uint8_t dataTx = 0; + uint32_t idRx = 0; + uint8_t canRcvFlags = 0; + + dataTx = 0xAB; + + msgRcvd = CANSPIRead( &idRx , &dataRxTx[ 0 ] , &dataRxLen, &canRcvFlags ); + + if ( msgRcvd ) + { + mikrobus_logWrite( &dataRxTx[ 0 ], _LOG_BYTE ); + Delay_1sec(); + } + +// CANSPIWrite( id2nd, dataTx, 1, canSendFlags ); +// mikrobus_logWrite( "MESSAGE SENT", _LOG_LINE ); +// Delay_1sec(); +} \ No newline at end of file diff --git a/CAN_App/src/CAN.h b/CAN_App/src/CAN.h new file mode 100644 index 0000000..e33602a --- /dev/null +++ b/CAN_App/src/CAN.h @@ -0,0 +1,9 @@ +// CAN driver files from mikroe.com +// MIT licese link... +#ifndef CAN_H +#define CAN_H + +void CAN_App(void); + +#endif // CAN_H + diff --git a/CAN_App/src/CAN_Driver.c b/CAN_App/src/CAN_Driver.c new file mode 100644 index 0000000..27eb420 --- /dev/null +++ b/CAN_App/src/CAN_Driver.c @@ -0,0 +1,16 @@ +#include "CAN_Driver.h" + +uint8_t CANSPIRead( uint32_t *idRx , uint8_t *dataRxTx , uint8_t *dataRxLen, uint8_t *canRcvFlags ) +{ + //TODO: This function comes from sample code from mikrobus.com +} + +void mikrobus_logWrite( uint8_t *dataRxTx, const LOGOPT_t opt ) +{ + //TODO: This function comes from sample code from mikrobus.com +} + +void Delay_1sec( void ) +{ + //TODO: This function comes from sample code from mikrobus.com +} diff --git a/CAN_App/src/CAN_Driver.h b/CAN_App/src/CAN_Driver.h new file mode 100644 index 0000000..0dac8f9 --- /dev/null +++ b/CAN_App/src/CAN_Driver.h @@ -0,0 +1,16 @@ +#ifndef CAN_DRIVER_H +#define CAN_DRIVER_H + +#include + +typedef enum logOpt +{ + _LOG_BYTE, + _LOG_LINE + } LOGOPT_t; + +uint8_t CANSPIRead( uint32_t *idRx , uint8_t *dataRxTx , uint8_t *dataRxLen, uint8_t *canRcvFlags ); +void mikrobus_logWrite( uint8_t *dataRxTx, const LOGOPT_t opt ); +void Delay_1sec( void ); + +#endif // CAN_DRIVER_H diff --git a/CAN_App/test/support/.gitkeep b/CAN_App/test/support/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/CAN_App/test/test_CAN.c b/CAN_App/test/test_CAN.c new file mode 100644 index 0000000..a02533a --- /dev/null +++ b/CAN_App/test/test_CAN.c @@ -0,0 +1,42 @@ +#ifdef TEST + +#include "unity.h" + +#include "CAN.h" +#include "mock_CAN_Driver.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_GivenMessageReceived_ThenWritesToLog(void) +{ + CANSPIRead_ExpectAndReturn( 0,0,0,0, 1); + CANSPIRead_IgnoreArg_idRx(); + CANSPIRead_IgnoreArg_dataRxTx(); + CANSPIRead_IgnoreArg_dataRxLen(); + CANSPIRead_IgnoreArg_canRcvFlags(); + CANSPIRead_ReturnThruPtr_dataRxTx("ABCDEFG"); + mikrobus_logWrite_Expect("ABCDEFG", _LOG_BYTE); + Delay_1sec_Expect(); + + CAN_App(); +} + +void test_GivenNoMessageReceived_ThenDoesNotWriteToLog(void) +{ + CANSPIRead_ExpectAndReturn( 0,0,0,0, 0); + CANSPIRead_IgnoreArg_idRx(); + CANSPIRead_IgnoreArg_dataRxTx(); + CANSPIRead_IgnoreArg_dataRxLen(); + CANSPIRead_IgnoreArg_canRcvFlags(); + CANSPIRead_ReturnThruPtr_dataRxTx("ABCDEFG"); + + CAN_App(); +} + +#endif // TEST diff --git a/CAN_App/test/test_CAN_Driver.c b/CAN_App/test/test_CAN_Driver.c new file mode 100644 index 0000000..d665d48 --- /dev/null +++ b/CAN_App/test/test_CAN_Driver.c @@ -0,0 +1,20 @@ +#ifdef TEST + +#include "unity.h" + +#include "CAN_Driver.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_CAN_Driver_NeedToImplement(void) +{ + TEST_IGNORE_MESSAGE("Need to Implement CAN_Driver"); +} + +#endif // TEST diff --git a/CAN_App/vendor/ceedling/bin/ceedling b/CAN_App/vendor/ceedling/bin/ceedling new file mode 100644 index 0000000..d110f3d --- /dev/null +++ b/CAN_App/vendor/ceedling/bin/ceedling @@ -0,0 +1,350 @@ +#!/usr/bin/env ruby + +#these are always used +require 'rubygems' +require 'fileutils' + +# Check for the main project file (either the one defined in the ENV or the default) +main_filepath = ENV['CEEDLING_MAIN_PROJECT_FILE'] +project_found = (!main_filepath.nil? && File.exists?(main_filepath)) +if (!project_found) + main_filepath = "project.yml" + project_found = File.exists?(main_filepath) +end + +def is_windows? + return ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) if defined?(RbConfig) + return ((Config::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) +end + +unless (project_found) +#===================================== We Do Not Have A Project ================================================ + + puts "Welcome to Ceedling!" + require 'thor' + + def here + File.dirname(__FILE__) + "/.." + end + + class CeedlingTasks < Thor + include Thor::Actions + + desc "new PROJECT_NAME", "create a new ceedling project" + method_option :docs, :type => :boolean, :default => false, :desc => "Add docs in project vendor directory" + method_option :local, :type => :boolean, :default => false, :desc => "Create a copy of Ceedling in the project vendor directory" + method_option :gitignore, :type => :boolean, :default => false, :desc => "Create a gitignore file for ignoring ceedling generated files" + method_option :no_configs, :type => :boolean, :default => false, :desc => "Don't install starter configuration files" + method_option :noconfigs, :type => :boolean, :default => false + + #deprecated: + method_option :no_docs, :type => :boolean, :default => false + method_option :nodocs, :type => :boolean, :default => false + method_option :as_gem, :type => :boolean, :default => false + method_option :asgem, :type => :boolean, :default => false + method_option :with_ignore, :type => :boolean, :default => false + method_option :withignore, :type => :boolean, :default => false + def new(name, silent = false) + copy_assets_and_create_structure(name, silent, false, options) + end + + desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)" + def upgrade(name, silent = false) + as_local = true + begin + require "yaml" + as_local = (YAML.load_file(File.join(name, "project.yml"))[:project][:which_ceedling] != 'gem') + rescue + raise "ERROR: Could not find valid project file '#{yaml_path}'" + end + found_docs = File.exists?( File.join(name, "docs", "CeedlingPacket.md") ) + copy_assets_and_create_structure(name, silent, true, {:upgrade => true, :no_configs => true, :local => as_local, :docs => found_docs}) + end + + no_commands do + def copy_assets_and_create_structure(name, silent=false, force=false, options = {}) + + puts "WARNING: --no_docs deprecated. It is now the default. Specify -docs if you want docs installed." if (options[:no_docs] || options[:nodocs]) + puts "WARNING: --as_gem deprecated. It is now the default. Specify -local if you want ceedling installed to this project." if (options[:as_gem] || options[:asgem]) + puts "WARNING: --with_ignore deprecated. It is now called -gitignore" if (options[:with_ignore] || options[:with_ignore]) + + use_docs = options[:docs] || false + use_configs = !(options[:no_configs] || options[:noconfigs] || false) + use_gem = !(options[:local]) + use_ignore = options[:gitignore] || false + is_upgrade = options[:upgrade] || false + + ceedling_path = File.join(name, 'vendor', 'ceedling') + source_path = File.join(name, 'src') + test_path = File.join(name, 'test') + test_support_path = File.join(name, 'test/support') + + # If it's not an upgrade, make sure we have the paths we expect + if (!is_upgrade) + [source_path, test_path, test_support_path].each do |d| + FileUtils.mkdir_p d + end + end + + # Genarate gitkeep in test support path + FileUtils.touch(File.join(test_support_path, '.gitkeep')) + + # If documentation requested, create a place to dump them and do so + doc_path = "" + if use_docs + doc_path = use_gem ? File.join(name, 'docs') : File.join(ceedling_path, 'docs') + FileUtils.mkdir_p doc_path + + in_doc_path = lambda {|f| File.join(doc_path, f)} + + # Add documentation from main projects to list + doc_files = {} + ['docs','vendor/unity/docs','vendor/cmock/docs','vendor/cexception/docs'].each do |p| + Dir[ File.expand_path(File.join(here, p, '*.md')) ].each do |f| + doc_files[ File.basename(f) ] = f unless(doc_files.include? f) + end + end + + # Add documentation from plugins to list + Dir[ File.join(here, 'plugins/**/README.md') ].each do |plugin_path| + k = "plugin_" + plugin_path.split(/\\|\//)[-2] + ".md" + doc_files[ k ] = File.expand_path(plugin_path) + end + + # Copy all documentation + doc_files.each_pair do |k, v| + copy_file(v, in_doc_path.call(k), :force => force) + end + end + + # If installed locally to project, copy ceedling, unity, cmock, & supports to vendor + unless use_gem + FileUtils.mkdir_p ceedling_path + + #copy full folders from ceedling gem into project + %w{plugins lib bin}.map do |f| + {:src => f, :dst => File.join(ceedling_path, f)} + end.each do |f| + directory(f[:src], f[:dst], :force => force) + end + + # mark ceedling as an executable + File.chmod(0755, File.join(ceedling_path, 'bin', 'ceedling')) unless is_windows? + + #copy necessary subcomponents from ceedling gem into project + sub_components = [ + {:src => 'vendor/c_exception/lib/', :dst => 'vendor/c_exception/lib'}, + {:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'}, + {:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'}, + {:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'}, + {:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'}, + {:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'}, + {:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'}, + ] + + sub_components.each do |c| + directory(c[:src], File.join(ceedling_path, c[:dst]), :force => force) + end + end + + # We're copying in a configuration file if we haven't said not to + if (use_configs) + dst_yaml = File.join(name, 'project.yml') + src_yaml = if use_gem + File.join(here, 'assets', 'project_as_gem.yml') + else + if is_windows? + copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force) + else + copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force) + File.chmod(0755, File.join(name, 'ceedling')) + end + File.join(here, 'assets', 'project_with_guts.yml') + end + + # Perform the actual clone of the config file, while updating the version + File.open(dst_yaml,'w') do |dst| + require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb")) + dst << File.read(src_yaml).gsub(":ceedling_version: '?'",":ceedling_version: #{Ceedling::Version::CEEDLING}") + puts " create #{dst_yaml}" + end + end + + # Copy the gitignore file if requested + if (use_ignore) + copy_file(File.join('assets', 'default_gitignore'), File.join(name, '.gitignore'), :force => force) + end + + unless silent + puts "\n" + puts "Project '#{name}' #{force ? "upgraded" : "created"}!" + puts " - Tool documentation is located in #{doc_path}" if use_docs + puts " - Execute 'ceedling help' from #{name} to view available test & build tasks" + puts '' + end + end + end + + desc "examples", "list available example projects" + def examples() + puts "Available sample projects:" + FileUtils.cd(File.join(here, "examples")) do + Dir["*"].each {|proj| puts " #{proj}"} + end + end + + desc "example PROJ_NAME [DEST]", "new specified example project (in DEST, if specified)" + def example(proj_name, dest=nil) + if dest.nil? then dest = proj_name end + + copy_assets_and_create_structure(dest, true, false, {:local=>true, :docs=>true}) + + dest_src = File.join(dest,'src') + dest_test = File.join(dest,'test') + dest_project = File.join(dest,'project.yml') + + directory "examples/#{proj_name}/src", dest_src + directory "examples/#{proj_name}/test", dest_test + remove_file dest_project + copy_file "examples/#{proj_name}/project.yml", dest_project + + puts "\n" + puts "Example project '#{proj_name}' created!" + puts " - Tool documentation is located in vendor/ceedling/docs" + puts " - Execute 'ceedling help' to view available test & build tasks" + puts '' + end + + desc "version", "return the version of the tools installed" + def version() + require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb")) + puts " Ceedling:: #{Ceedling::Version::CEEDLING}" + puts " CMock:: #{Ceedling::Version::CMOCK}" + puts " Unity:: #{Ceedling::Version::UNITY}" + puts " CException:: #{Ceedling::Version::CEXCEPTION}" + end + end + + if (ARGV[0] =~ /^\-T$/) + puts "\n(No Project Detected, Therefore Showing Options to Create Projects)" + CeedlingTasks.tasks.each_pair do |k,v| + puts v.usage.ljust(25,' ') + v.description + end + puts "\n" + else + CeedlingTasks.source_root here + CeedlingTasks.start + end + +#===================================== We Have A Project Already ================================================ +else + require 'yaml' + require 'rbconfig' + + #determine platform + platform = begin + case(RbConfig::CONFIG['host_os']) + when /mswin|mingw|cygwin/i + :mswin + when /darwin/ + :osx + else + :linux + end + rescue + :linux + end + + #create our default meta-runner option set + options = { + :pretest => nil, + :args => [], + :add_path => [], + :path_connector => (platform == :mswin) ? ";" : ":", + :graceful_fail => false, + :which_ceedling => (Dir.exists?("vendor/ceedling") ? "vendor/ceedling" : 'gem'), + :default_tasks => [ 'test:all' ], + :list_tasks => false + } + + #guess that we need a special script file first if it exists + if (platform == :mswin) + options[:pretest] = File.exists?("#{ platform.to_s }_setup.bat") ? "#{ platform.to_s }_setup.bat" : nil + else + options[:pretest] = File.exists?("#{ platform.to_s }_setup.sh") ? "source #{ platform.to_s }_setup.sh" : nil + end + + #merge in project settings if they can be found here + yaml_options = YAML.load_file(main_filepath) + if (yaml_options[:paths]) + options[:add_path] = yaml_options[:paths][:tools] || [] + else + options[:add_path] = [] + end + options[:graceful_fail] = yaml_options[:graceful_fail] if yaml_options[:graceful_fail] + options[:which_ceedling] = yaml_options[:project][:which_ceedling] if (yaml_options[:project] && yaml_options[:project][:which_ceedling]) + options[:default_tasks] = yaml_options[:default_tasks] if yaml_options[:default_tasks] + + #sort through command line options + ARGV.each do |v| + case(v) + when /^(?:new|examples?|templates?)$/ + puts "\nOops. You called ceedling with argument '#{v}'.\n" + + " This is an operation that will create a new project... \n" + + " but it looks like you're already in a project. If you really \n" + + " want to do this, try moving to an empty folder.\n\n" + abort + when /^help$/ + options[:list_tasks] = true + when /^-T$/ + options[:list_tasks] = true + when /^--tasks$/ + options[:list_tasks] = true + when /^project:(\w+)/ + ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml" + else + options[:args].push(v) + end + end + + #add to the path + if (options[:add_path] && !options[:add_path].empty?) + path = ENV["PATH"] + options[:add_path].each do |p| + f = File.expand_path(File.dirname(__FILE__),p) + path = (f + options[:path_connector] + path) unless path.include? f + end + ENV["PATH"] = path + end + + # Load Ceedling (either through the rakefile OR directly) + if (File.exists?("rakefile.rb")) + load 'rakefile.rb' + else + if (options[:which_ceedling] == 'gem') + require 'ceedling' + else + load "#{options[:which_ceedling]}/lib/ceedling.rb" + end + Ceedling.load_project + end + + Rake.application.standard_exception_handling do + if options[:list_tasks] + # Display helpful task list when requested. This required us to dig into Rake internals a bit + Rake.application.define_singleton_method(:name=) {|n| @name = n} + Rake.application.name = 'ceedling' + Rake.application.options.show_tasks = :tasks + Rake.application.options.show_task_pattern = /^(?!.*build).*$/ + Rake.application.display_tasks_and_comments() + else + task :default => options[:default_tasks] + + # Run our Tasks! + Rake.application.collect_command_line_tasks(options[:args]) + Rake.application.top_level + end + end + true +#=================================================================================================================== +end diff --git a/CAN_App/vendor/ceedling/docs/CMock_Summary.md b/CAN_App/vendor/ceedling/docs/CMock_Summary.md new file mode 100644 index 0000000..9a89634 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/CMock_Summary.md @@ -0,0 +1,831 @@ +CMock: A Summary +================ + +*[ThrowTheSwitch.org](http://throwtheswitch.org)* + +*This documentation is released under a Creative Commons 3.0 Attribution Share-Alike License* + + +What Exactly Are We Talking About Here? +--------------------------------------- + +CMock is a nice little tool which takes your header files and creates +a Mock interface for it so that you can more easily unit test modules +that touch other modules. For each function prototype in your +header, like this one: + + int DoesSomething(int a, int b); + + +...you get an automatically generated DoesSomething function +that you can link to instead of your real DoesSomething function. +By using this Mocked version, you can then verify that it receives +the data you want, and make it return whatever data you desire, +make it throw errors when you want, and more... Create these for +everything your latest real module touches, and you're suddenly +in a position of power: You can control and verify every detail +of your latest creation. + +To make that easier, CMock also gives you a bunch of functions +like the ones below, so you can tell that generated DoesSomething +function how to behave for each test: + + void DoesSomething_ExpectAndReturn(int a, int b, int toReturn); + void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error); + void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback); + void DoesSomething_IgnoreAndReturn(int toReturn); + + +You can pile a bunch of these back to back, and it remembers what +you wanted to pass when, like so: + + test_CallsDoesSomething_ShouldDoJustThat(void) + { + DoesSomething_ExpectAndReturn(1,2,3); + DoesSomething_ExpectAndReturn(4,5,6); + DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS); + + CallsDoesSomething( ); + } + + +This test will call CallsDoesSomething, which is the function +we are testing. We are expecting that function to call DoesSomething +three times. The first time, we check to make sure it's called +as DoesSomething(1, 2) and we'll magically return a 3. The second +time we check for DoesSomething(4, 5) and we'll return a 6. The +third time we verify DoesSomething(7, 8) and we'll throw an error +instead of returning anything. If CallsDoesSomething gets +any of this wrong, it fails the test. It will fail if you didn't +call DoesSomething enough, or too much, or with the wrong arguments, +or in the wrong order. + +CMock is based on Unity, which it uses for all internal testing. +It uses Ruby to do all the main work (versions 2.0.0 and above). + + +Installing +========== + +The first thing you need to do to install CMock is to get yourself +a copy of Ruby. If you're on linux or osx, you probably already +have it. You can prove it by typing the following: + + ruby --version + + +If it replied in a way that implies ignorance, then you're going to +need to install it. You can go to [ruby-lang](https://ruby-lang.org) +to get the latest version. You're also going to need to do that if it +replied with a version that is older than 2.0.0. Go ahead. We'll wait. + +Once you have Ruby, you have three options: + +* Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/) +* Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/) +* Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`. + + +Generated Mock Module Summary +============================= + +In addition to the mocks themselves, CMock will generate the +following functions for use in your tests. The expect functions +are always generated. The other functions are only generated +if those plugins are enabled: + + +Expect: +------- + +Your basic staple Expects which will be used for most of your day +to day CMock work. By calling this, you are telling CMock that you +expect that function to be called during your test. It also specifies +which arguments you expect it to be called with, and what return +value you want returned when that happens. You can call this function +multiple times back to back in order to queue up multiple calls. + +* `void func(void)` => `void func_Expect(void)` +* `void func(params)` => `void func_Expect(expected_params)` +* `retval func(void)` => `void func_ExpectAndReturn(retval_to_return)` +* `retval func(params)` => `void func_ExpectAndReturn(expected_params, retval_to_return)` + + +ExpectAnyArgs: +-------------- + +This behaves just like the Expects calls, except that it doesn't really +care what the arguments are that the mock gets called with. It still counts +the number of times the mock is called and it still handles return values +if there are some. Note that an ExpectAnyArgs call is not generated for +functions that have no arguments, because it would act exactly like the existing +Expect and ExpectAndReturn calls. + +* `void func(params)` => `void func_ExpectAnyArgs(void)` +* `retval func(params)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)` + + +Array: +------ + +An ExpectWithArray is another variant of Expect. Like expect, it cares about +the number of times a mock is called, the arguments it is called with, and the +values it is to return. This variant has another feature, though. For anything +that resembles a pointer or array, it breaks the argument into TWO arguments. +The first is the original pointer. The second specify the number of elements +it is to verify of that array. If you specify 1, it'll check one object. If 2, +it'll assume your pointer is pointing at the first of two elements in an array. +If you specify zero elements, it will check just the pointer if +`:smart` mode is configured or fail if `:compare_data` is set. + +* `void func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers) +* `void func(ptr * param, other)` => `void func_ExpectWithArray(ptr* param, int param_depth, other)` +* `retval func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers) +* `retval func(other, ptr* param)` => `void func_ExpectWithArrayAndReturn(other, ptr* param, int param_depth, retval_to_return)` + + +Ignore: +------- + +Maybe you don't care about the number of times a particular function is called or +the actual arguments it is called with. In that case, you want to use Ignore. Ignore +only needs to be called once per test. It will then ignore any further calls to that +particular mock. The IgnoreAndReturn works similarly, except that it has the added +benefit of knowing what to return when that call happens. If the mock is called more +times than IgnoreAndReturn was called, it will keep returning the last value without +complaint. If it's called fewer times, it will also ignore that. You SAID you didn't +care how many times it was called, right? + +* `void func(void)` => `void func_Ignore(void)` +* `void func(params)` => `void func_Ignore(void)` +* `retval func(void)` => `void func_IgnoreAndReturn(retval_to_return)` +* `retval func(params)` => `void func_IgnoreAndReturn(retval_to_return)` + +StopIgnore: +------- + +Maybe you want to ignore a particular function for part of a test but dont want to +ignore it later on. In that case, you want to use StopIgnore which will cancel the +previously called Ignore or IgnoreAndReturn requiring you to Expect or otherwise +handle the call to a function. + +* `void func(void)` => `void func_StopIgnore(void)` +* `void func(params)` => `void func_StopIgnore(void)` +* `retval func(void)` => `void func_StopIgnore(void)` +* `retval func(params)` => `void func_StopIgnore(void)` + +IgnoreStateless: +---------------- + +This plugin is similar to the Ignore plugin, but the IgnoreAndReturn functions are +stateless. So the Ignored function will always return the last specified return value +and does not queue the return values as the IgnoreAndReturn of the default plugin will. + +To stop ignoring a function you can call StopIgnore or simply overwrite the Ignore +(resp. IgnoreAndReturn) with an Expect (resp. ExpectAndReturn). Note that calling +Ignore (resp IgnoreAndReturn) will clear your previous called Expect +(resp. ExpectAndReturn), so they are not restored after StopIgnore is called. + +You can use this plugin by using `:ignore_stateless` instead of `:ignore` in your +CMock configuration file. + +The generated functions are the same as **Ignore** and **StopIgnore** above. + +Ignore Arg: +------------ + +Maybe you overall want to use Expect and its similar variations, but you don't care +what is passed to a particular argument. This is particularly useful when that argument +is a pointer to a value that is supposed to be filled in by the function. You don't want +to use ExpectAnyArgs, because you still care about the other arguments. Instead, after +an Expect call is made, you can call this function. It tells CMock to ignore +a particular argument for the rest of this test, for this mock function. You may call +multiple instances of this to ignore multiple arguments after each expectation if +desired. + +* `void func(params)` => `void func_IgnoreArg_paramName(void)` + + +ReturnThruPtr: +-------------- + +Another option which operates on a particular argument of a function is the ReturnThruPtr +plugin. For every argument that resembles a pointer or reference, CMock generates an +instance of this function. Just as the AndReturn functions support injecting one or more +return values into a queue, this function lets you specify one or more return values which +are queued up and copied into the space being pointed at each time the mock is called. + +* `void func(param1)` => `void func_ReturnThruPtr_paramName(val_to_return)` +* => `void func_ReturnArrayThruPtr_paramName(cal_to_return, len)` +* => `void func_ReturnMemThruPtr_paramName(val_to_return, size)` + + +Callback: +--------- + +If all those other options don't work, and you really need to do something custom, you +still have a choice. As soon as you stub a callback in a test, it will call the callback +whenever the mock is encountered and return the retval returned from the callback (if any). + +* `void func(void)` => `void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback)` +where `CMOCK_func_CALLBACK` looks like: `void func(int NumCalls)` +* `void func(params)` => `void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback)` +where `CMOCK_func_CALLBACK` looks like: `void func(params, int NumCalls)` +* `retval func(void)` => `void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback)` +where `CMOCK_func_CALLBACK` looks like: `retval func(int NumCalls)` +* `retval func(params)` => `void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback)` +where `CMOCK_func_CALLBACK` looks like: `retval func(params, int NumCalls)` + +You can choose from two options: + +* `func_AddCallback` tells the mock to check its arguments and calling +order (based on any Expects you've set up) before calling the callback. +* `func_Stub` tells the mock to skip all the normal checks and jump directly +to the callback instead. In this case, you are replacing the normal mock calls +with your own custom stub function. + +There is also an older name, `func_StubWithCallback`, which is just an alias +for either `func_AddCallback` or `func_Stub` depending on setting of the +`:callback_after_arg_check` toggle. This is deprecated and we recommend using +the two options above. + + +Cexception: +----------- + +Finally, if you are using Cexception for error handling, you can use this to throw errors +from inside mocks. Like Expects, it remembers which call was supposed to throw the error, +and it still checks parameters first. + +* `void func(void)` => `void func_ExpectAndThrow(value_to_throw)` +* `void func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)` +* `retval func(void)` => `void func_ExpectAndThrow(value_to_throw)` +* `retval func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)` + + + +Running CMock +============= + +CMock is a Ruby script and class. You can therefore use it directly +from the command line, or include it in your own scripts or rakefiles. + + +Mocking from the Command Line +----------------------------- + +After unpacking CMock, you will find cmock.rb in the 'lib' directory. +This is the file that you want to run. It takes a list of header files +to be mocked, as well as an optional yaml file for a more detailed +configuration (see config options below). + +For example, this will create three mocks using the configuration +specified in MyConfig.yml: + + ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h + +And this will create two mocks using the default configuration: + + ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h + + +Mocking From Scripts or Rake +---------------------------- + +CMock can be used directly from your own scripts or from a rakefile. +Start by including cmock.rb, then create an instance of CMock. +When you create your instance, you may initialize it in one of +three ways. + +You may specify nothing, allowing it to run with default settings: + + require 'cmock.rb' + cmock = CMock.new + +You may specify a YAML file containing the configuration options +you desire: + + cmock = CMock.new('../MyConfig.yml') + +You may specify the options explicitly: + + cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/') + +Creating Skeletons: +------------------- + +Not only is CMock able to generate mock files from a header file, but it is also able +to generate (and update) skeleton C files from headers. It does this by creating a +(mostly) empty implementation for every function that is declared in the header. If you later +add to that header list, just run this feature again and it will add prototypes for the missing +functions! + +Like the normal usecase for CMock, this feature can be used from the command line +or from within its ruby API. For example, from the command line, add `--skeleton` to +generate a skeleton instead: + +``` +ruby cmock.rb --skeleton ../create/c/for/this.h +``` + +Config Options: +--------------- + +The following configuration options can be specified in the +yaml file or directly when instantiating. + +Passed as Ruby, they look like this: + + { :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare } + +Defined in the yaml file, they look more like this: + + :cmock: + :attributes: + - __funky + - __intrinsic + :when_ptr: :compare + +In all cases, you can just include the things that you want to override +from the defaults. We've tried to specify what the defaults are below. + +* `:attributes`: + These are attributes that CMock should ignore for you for testing + purposes. Custom compiler extensions and externs are handy things to + put here. If your compiler is choking on some extended syntax, this + is often a good place to look. + + * defaults: ['__ramfunc', '__irq', '__fiq', 'register', 'extern'] + * **note:** this option will reinsert these attributes onto the mock's calls. + If that isn't what you are looking for, check out :strippables. + +* `:c_calling_conventions`: + Similarly, CMock may need to understand which C calling conventions + might show up in your codebase. If it encounters something it doesn't + recognize, it's not going to mock it. We have the most common covered, + but there are many compilers out there, and therefore many other options. + + * defaults: ['__stdcall', '__cdecl', '__fastcall'] + * **note:** this option will reinsert these attributes onto the mock's calls. + If that isn't what you are looking for, check out :strippables. + +* `:callback_after_arg_check`: + Tell `:callback` plugin to do the normal argument checking **before** it + calls the callback function by setting this to true. When false, the + callback function is called **instead** of the argument verification. + + * default: false + +* `:callback_include_count`: + Tell `:callback` plugin to include an extra parameter to specify the + number of times the callback has been called. If set to false, the + callback has the same interface as the mocked function. This can be + handy when you're wanting to use callback as a stub. + + * default: true + +* `:cexception_include`: + Tell `:cexception` plugin where to find CException.h... You only need to + define this if it's not in your build path already... which it usually + will be for the purpose of your builds. + + * default: *nil* + +* `:enforce_strict_ordering`: + CMock always enforces the order that you call a particular function, + so if you expect GrabNabber(int size) to be called three times, it + will verify that the sizes are in the order you specified. You might + *also* want to make sure that all different functions are called in a + particular order. If so, set this to true. + + * default: false + +* `:framework`: + Currently the only option is `:unity.` Eventually if we support other + unity test frameworks (or if you write one for us), they'll get added + here. + + : default: :unity + +* `:includes`: + An array of additional include files which should be added to the + mocks. Useful for global types and definitions used in your project. + There are more specific versions if you care WHERE in the mock files + the includes get placed. You can define any or all of these options. + + * `:includes` + * `:includes_h_pre_orig_header` + * `:includes_h_post_orig_header` + * `:includes_c_pre_header` + * `:includes_c_post_header` + * default: nil #for all 5 options + +* `:memcmp_if_unknown`: + C developers create a lot of types, either through typedef or preprocessor + macros. CMock isn't going to automatically know what you were thinking all + the time (though it tries its best). If it comes across a type it doesn't + recognize, you have a choice on how you want it to handle it. It can either + perform a raw memory comparison and report any differences, or it can fail + with a meaningful message. Either way, this feature will only happen after + all other mechanisms have failed (The thing encountered isn't a standard + type. It isn't in the :treat_as list. It isn't in a custom unity_helper). + + * default: true + +* `:mock_path`: + The directory where you would like the mock files generated to be + placed. + + * default: mocks + +* `:mock_prefix`: + The prefix to prepend to your mock files. For example, if it's `Mock`, a file + “USART.h” will get a mock called “MockUSART.c”. This CAN be used with a suffix + at the same time. + + * default: Mock + +* `:mock_suffix`: + The suffix to append to your mock files. For example, it it's `_Mock`, a file + "USART.h" will get a mock called "USART_Mock.h". This CAN be used with a prefix + at the same time. + + * default: "" + +* `:plugins`: + An array of which plugins to enable. ':expect' is always active. Also + available currently: + + * `:ignore` + * `:ignore_stateless` + * `:ignore_arg` + * `:expect_any_args` + * `:array` + * `:cexception` + * `:callback` + * `:return_thru_ptr` + +* `:strippables`: + An array containing a list of items to remove from the header + before deciding what should be mocked. This can be something simple + like a compiler extension CMock wouldn't recognize, or could be a + regex to reject certain function name patterns. This is a great way to + get rid of compiler extensions when your test compiler doesn't support + them. For example, use `:strippables: ['(?:functionName\s*\(+.*?\)+)']` + to prevent a function `functionName` from being mocked. By default, it + is ignoring all gcc attribute extensions. + + * default: `['(?:__attribute__\s*\(+.*?\)+)']` + +* `:exclude_setjmp_h`: + Some embedded systems don't have available. Setting this to true + removes references to this header file and the ability to use cexception. + + * default: false + + +* `:subdir`: + This is a relative subdirectory for your mocks. Set this to e.g. "sys" in + order to create a mock for `sys/types.h` in `(:mock_path)/sys/`. + + * default: "" + +* `:treat_as`: + The `:treat_as` list is a shortcut for when you have created typedefs + of standard types. Why create a custom unity helper for UINT16 when + the unity function TEST_ASSERT_EQUAL_HEX16 will work just perfectly? + Just add 'UINT16' => 'HEX16' to your list (actually, don't. We already + did that one for you). Maybe you have a type that is a pointer to an + array of unsigned characters? No problem, just add 'UINT8_T*' => + 'HEX8*' + + * NOTE: unlike the other options, your specifications MERGE with the + default list. Therefore, if you want to override something, you must + reassign it to something else (or to *nil* if you don't want it) + + * default: + * 'int': 'INT' + * 'char': 'INT8' + * 'short': 'INT16' + * 'long': 'INT' + * 'int8': 'INT8' + * 'int16': 'INT16' + * 'int32': 'INT' + * 'int8_t': 'INT8' + * 'int16_t': 'INT16' + * 'int32_t': 'INT' + * 'INT8_T': 'INT8' + * 'INT16_T': 'INT16' + * 'INT32_T': 'INT' + * 'bool': 'INT' + * 'bool_t': 'INT' + * 'BOOL': 'INT' + * 'BOOL_T': 'INT' + * 'unsigned int': 'HEX32' + * 'unsigned long': 'HEX32' + * 'uint32': 'HEX32' + * 'uint32_t': 'HEX32' + * 'UINT32': 'HEX32' + * 'UINT32_T': 'HEX32' + * 'void*': 'HEX8_ARRAY' + * 'unsigned short': 'HEX16' + * 'uint16': 'HEX16' + * 'uint16_t': 'HEX16' + * 'UINT16': 'HEX16' + * 'UINT16_T': 'HEX16' + * 'unsigned char': 'HEX8' + * 'uint8': 'HEX8' + * 'uint8_t': 'HEX8' + * 'UINT8': 'HEX8' + * 'UINT8_T': 'HEX8' + * 'char*': 'STRING' + * 'pCHAR': 'STRING' + * 'cstring': 'STRING' + * 'CSTRING': 'STRING' + * 'float': 'FLOAT' + * 'double': 'FLOAT' + +* `:treat_as_array`: + A specialized sort of `:treat_as` to be used when you've created a + typedef of an array type, such as `typedef int TenIntegers[10];`. This + is a hash of typedef name to element type. For example: + + { "TenIntegers" => "int", + "ArrayOfFloat" => "float" } + + Telling CMock about these typedefs allows it to be more intelligent + about parameters of such types, so that you can use features like + ExpectWithArray and ReturnArrayThruPtr with them. + +* `:treat_as_void`: + We've seen "fun" legacy systems typedef 'void' with a custom type, + like MY_VOID. Add any instances of those to this list to help CMock + understand how to deal with your code. + + * default: [] + +* `:treat_externs`: + This specifies how you want CMock to handle functions that have been + marked as extern in the header file. Should it mock them? + + * `:include` will mock externed functions + * `:exclude` will ignore externed functions (default). + +* `:treat_inlines`: + This specifies how you want CMock to handle functions that have been + marked as inline in the header file. Should it mock them? + + * `:include` will mock inlined functions + * `:exclude` will ignore inlined functions (default). + + CMock will look for the following default patterns (simplified from the actual regex): + - "static inline" + - "inline static" + - "inline" + - "static" + You can override these patterns, check out :inline_function_patterns. + + Enabling this feature does require a change in the build system that + is using CMock. To understand why, we need to give some more info + on how we are handling inline functions internally. + Let's say we want to mock a header called example.h. example.h + contains inline functions, we cannot include this header in the + mocks or test code if we want to mock the inline functions simply + because the inline functions contain an implementation that we want + to override in our mocks! + So, to circumvent this, we generate a new header, also named + example.h, in the same directory as mock_example.h/c . This newly + generated header should/is exactly the same as the original header, + only difference is the inline functions are transformed to 'normal' + functions declarations. Placing the new header in the same + directory as mock_example.h/c ensures that they will include the new + header and not the old one. + However, CMock has no control in how the build system is configured + and which include paths the test code is compiled with. In order + for the test code to also see the newly generated header ,and not + the old header with inline functions, the build system has to add + the mock folder to the include paths. + Furthermore, we need to keep the order of include paths in mind. We + have to set the mock folder before the other includes to avoid the + test code including the original header instead of the newly + generated header (without inline functions). + +* `:unity_helper_path`: + If you have created a header with your own extensions to unity to + handle your own types, you can set this argument to that path. CMock + will then automagically pull in your helpers and use them. The only + trick is that you make sure you follow the naming convention: + `UNITY_TEST_ASSERT_EQUAL_YourType`. If it finds macros of the right + shape that match that pattern, it'll use them. + + * default: [] + +* `:verbosity`: + How loud should CMock be? + + * 0 for errors only + * 1 for errors and warnings + * 2 for normal (default) + * 3 for verbose + +* `:weak`: + When set this to some value, the generated mocks are defined as weak + symbols using the configured format. This allows them to be overridden + in particular tests. + + * Set to '__attribute ((weak))' for weak mocks when using GCC. + * Set to any non-empty string for weak mocks when using IAR. + * default: "" + +* `:when_no_prototypes`: + When you give CMock a header file and ask it to create a mock out of + it, it usually contains function prototypes (otherwise what was the + point?). You can control what happens when this isn't true. You can + set this to `:warn,` `:ignore,` or `:error` + + * default: :warn + +* `:when_ptr`: + You can customize how CMock deals with pointers (c strings result in + string comparisons... we're talking about **other** pointers here). Your + options are `:compare_ptr` to just verify the pointers are the same, + `:compare_data` or `:smart` to verify that the data is the same. + `:compare_data` and `:smart` behaviors will change slightly based on + if you have the array plugin enabled. By default, they compare a + single element of what is being pointed to. So if you have a pointer + to a struct called ORGAN_T, it will compare one ORGAN_T (whatever that + is). + + * default: :smart + +* `:array_size_type`: +* `:array_size_name`: + When the `:array` plugin is disabled, these options do nothing. + + When the `:array` plugin is enabled, these options allow CMock to recognize + functions with parameters that might refer to an array, like the following, + and treat them more intelligently: + + * `void GoBananas(Banana * bananas, int num_bananas)` + * `int write_data(int fd, const uint8_t * data, uint32_t size)` + + To recognize functions like these, CMock looks for a parameter list + containing a pointer (which could be an array) followed by something that + could be an array size. "Something", by default, means an `int` or `size_t` + parameter with a name containing "size" or "len". + + `:array_size_type` is a list of additional types (besides `int` and `size_t`) + that could be used for an array size parameter. For example, to get CMock to + recognize that `uint32_t size` is an array size, you'd need to say: + + cfg[:array_size_type] = ['uint32_t'] + + `:array_size_name` is a regular expression used to match an array size + parameter by name. By default, it's 'size|len'. To get CMock to recognize a + name like `num_bananas`, you could tell it to also accept names containing + 'num_' like this: + + cfg[:array_size_name] = 'size|len|num_' + + Parameters must match *both* `:array_size_type` and `:array_size_name` (and + must come right after a pointer parameter) to be treated as an array size. + + Once you've told it how to recognize your arrays, CMock will give you `_Expect` + calls that work more like `_ExpectWithArray`, and compare an array of objects + rather than just a single object. + + For example, if you write the following, CMock will check that GoBananas is + called and passed an array containing a green banana followed by a yellow + banana: + + Banana b[2] = {GreenBanana, YellowBanana}; + GoBananas_Expect(b, 2); + + In other words, `GoBananas_Expect(b, 2)` now works just the same as: + + GoBananas_ExpectWithArray(b, 2, 2); + +* `:fail_on_unexpected_calls`: + By default, CMock will fail a test if a mock is called without `_Expect` and `_Ignore` + called first. While this forces test writers to be more explicit in their expectations, + it can clutter tests with `_Expect` or `_Ignore` calls for functions which are not the focus + of the test. While this is a good indicator that this module should be refactored, some + users are not fans of the additional noise. + + Therefore, :fail_on_unexpected_calls can be set to false to force all mocks to start with + the assumption that they are operating as `_Ignore` unless otherwise specified. + + * default: true + * **note:** + If this option is disabled, the mocked functions will return + a default value (0) when called (and only if they have to return something of course). + +* `:inline_function_patterns`: + An array containing a list of strings to detect inline functions. + This option is only taken into account if you enable :treat_inlines. + These strings are interpreted as regex patterns so be sure to escape + certain characters. For example, use `:inline_function_patterns: ['static inline __attribute__ \(\(always_inline\)\)']` + to recognize `static inline __attribute__ ((always_inline)) int my_func(void)` + as an inline function. + The default patterns are are: + + * default: ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] + * **note:** + The order of patterns is important here! + We go from specific patterns ('static inline') to general patterns ('inline'), + otherwise we would miss functions that use 'static inline' iso 'inline'. + + +Compiled Options: +----------------- + +A number of #defines also exist for customizing the cmock experience. +Feel free to pass these into your compiler or whatever is most +convenient. CMock will otherwise do its best to guess what you want +based on other settings, particularly Unity's settings. + +* `CMOCK_MEM_STATIC` or `CMOCK_MEM_DYNAMIC` + Define one of these to determine if you want to dynamically add + memory during tests as required from the heap. If static, you + can control the total footprint of Cmock. If dynamic, you will + need to make sure you make some heap space available for Cmock. + +* `CMOCK_MEM_SIZE` + In static mode this is the total amount of memory you are allocating + to Cmock. In Dynamic mode this is the size of each chunk allocated + at once (larger numbers grab more memory but require fewer mallocs). + +* `CMOCK_MEM_ALIGN` + The way to align your data to. Not everything is as flexible as + a PC, as most embedded designers know. This defaults to 2, meaning + align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment + by setting 0, force alignment to the closest uint16 with 1 or even + to the closest uint64 with 3. + +* `CMOCK_MEM_PTR_AS_INT` + This is used internally to hold pointers... it needs to be big + enough. On most processors a pointer is the same as an unsigned + long... but maybe that's not true for yours? + +* `CMOCK_MEM_INDEX_TYPE` + This needs to be something big enough to point anywhere in Cmock's + memory space... usually it's a size_t. + +Other Tips +========== + +resetTest +--------- + +While this isn't strictly a CMock feature, often users of CMock are using +either the test runner generator scripts in Unity or using Ceedling. In +either case, there is a handy function called `resetTest` which gets +generated with your runner. You can then use this handy function in your tests +themselves. Call it during a test to have CMock validate everything to this point +and start over clean. This is really useful when wanting to test a function in +an iterative manner with different arguments. + +C++ Support +--------- +C++ unit test/mocking frameworks often use a completely different approach (vs. +CMock) that relies on overloading virtual class members and does not support +directly mocking static class member methods or free functions (i.e., functions +in plain C). One workaround is to wrap the non-virtual functions in an object +that exposes them as virtual methods and modify your code to inject mocks at +run-time... but there is another way! + +Simply use CMock to mock the static member methods and a C++ mocking framework +to handle the virtual methods. (Yes, you can mix mocks from CMock and a C++ +mocking framework together in the same test!) + +Keep in mind that since C++ mocking frameworks often link the real object to the +unit test too, we need to resolve multiple definition errors with something like +the following in the source of the real implementation for any functions that +CMock mocks: + + #if defined(TEST) + __attribute__((weak)) + #endif + +To address potential issues with re-using the same function name in different +namespaces/classes, the generated function names include the namespace(s) and +class. For example: + + namespace MyNamespace { + class MyClass { + static int DoesSomething(int a, int b); + }; + } + +Will generate functions like + + void MyNamespace_MyClass_DoesSomething_ExpectAndReturn(int a, int b, int toReturn); + +Examples +======== + +You can look in the [examples directory](/examples/) for a couple of examples on how +you might tool CMock into your build process. You may also want to consider +using [Ceedling](https://throwtheswitch.org/ceedling). Please note that +these examples are meant to show how the build process works. They have +failing tests ON PURPOSE to show what that would look like. Don't be alarmed. ;) diff --git a/CAN_App/vendor/ceedling/docs/CeedlingPacket.md b/CAN_App/vendor/ceedling/docs/CeedlingPacket.md new file mode 100644 index 0000000..7aa1789 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/CeedlingPacket.md @@ -0,0 +1,2266 @@ +[All code is copyright © 2010-2021 Ceedling Project +by Mike Karlesky, Mark VanderVoord, and Greg Williams. + +This Documentation Is Released Under a +Creative Commons 3.0 Attribution Share-Alike License] + +What the What? + +Assembling build environments for C projects - especially with +automated unit tests - is a pain. Whether it's Make or Rake or Premake +or what-have-you, set up with an all-purpose build environment +tool is tedious and requires considerable glue code to pull together +the necessary tools and libraries. Ceedling allows you to generate +an entire test and build environment for a C project from a single +YAML configuration file. Ceedling is written in Ruby and works +with the Rake build tool plus other goodness like Unity and CMock — +the unit testing and mocking frameworks for C. Ceedling and +its complementary tools can support the tiniest of embedded +processors, the beefiest 64 bit power houses available, and +everything in between. + +For a build project including unit tests and using the default +toolchain gcc, the configuration file could be as simple as this: + +```yaml +:project: + :build_root: project/build/ + :release_build: TRUE + +:paths: + :test: + - tests/** + :source: + - source/** +``` + +From the command line, to build the release version of your project, +you would simply run `ceedling release`. To run all your unit tests, +you would run `ceedling test:all`. That's it! + +Of course, many more advanced options allow you to configure +your project with a variety of features to meet a variety of needs. +Ceedling can work with practically any command line toolchain +and directory structure – all by way of the configuration file. +Further, because Ceedling piggy backs on Rake, you can add your +own Rake tasks to accomplish project tasks outside of testing +and release builds. A facility for plugins also allows you to +extend Ceedling's capabilities for needs such as custom code +metrics reporting and coverage testing. + +What's with this Name? + +Glad you asked. Ceedling is tailored for unit tested C projects +and is built upon / around Rake (Rake is a Make replacement implemented +in the Ruby scripting language). So, we've got C, our Rake, and +the fertile soil of a build environment in which to grow and tend +your project and its unit tests. Ta da - _Ceedling_. + +What Do You Mean "tailored for unit tested C projects"? + +Well, we like to write unit tests for our C code to make it lean and +mean (that whole [Test-Driven Development][tdd] +thing). Along the way, this style of writing C code spawned two +tools to make the job easier: a unit test framework for C called +_Unity_ and a mocking library called _CMock_. And, though it's +not directly related to testing, a C framework for exception +handling called _CException_ also came along. + +[tdd]: http://en.wikipedia.org/wiki/Test-driven_development + +These tools and frameworks are great, but they require quite +a bit of environment support to pull them all together in a convenient, +usable fashion. We started off with Rakefiles to assemble everything. +These ended up being quite complicated and had to be hand-edited +or created anew for each new project. Ceedling replaces all that +tedium and rework with a configuration file that ties everything +together. + +Though Ceedling is tailored for unit testing, it can also go right ahead +and build your final binary release artifact for you as well. Or, +Ceedling and your tests can live alongside your existing release build +setup. That said, Ceedling is more powerful as a unit test build +environment than it is a general purpose release build environment; +complicated projects including separate bootloaders or multiple library +builds, etc. are not its strong suit. + +Hold on. Back up. Ruby? Rake? YAML? Unity? CMock? CException? + +Seem overwhelming? It's not bad at all, and for the benefits tests +bring us, it's all worth it. + +[Ruby][] is a handy scripting +language like Perl or Python. It's a modern, full featured language +that happens to be quite handy for accomplishing tasks like code +generation or automating one's workflow while developing in +a compiled language such as C. + +[Ruby]: http://www.ruby-lang.org/en/ + +[Rake][] is a utility written in Ruby +for accomplishing dependency tracking and task automation +common to building software. It's a modern, more flexible replacement +for [Make][]). +Rakefiles are Ruby files, but they contain build targets similar +in nature to that of Makefiles (but you can also run Ruby code in +your Rakefile). + +[Rake]: http://rubyrake.org/ +[Make]: http://en.wikipedia.org/wiki/Make_(software) + +[YAML][] is a "human friendly data serialization standard for all +programming languages." It's kinda like a markup language, but don't +call it that. With a YAML library, you can [serialize][] data structures +to and from the file system in a textual, human readable form. Ceedling +uses a serialized data structure as its configuration input. + +[YAML]: http://en.wikipedia.org/wiki/Yaml +[serialize]: http://en.wikipedia.org/wiki/Serialization + +[Unity] is a [unit test framework][test] for C. It provides facilities +for test assertions, executing tests, and collecting / reporting test +results. Unity derives its name from its implementation in a single C +source file (plus two C header files) and from the nature of its +implementation - Unity will build in any C toolchain and is configurable +for even the very minimalist of processors. + +[Unity]: http://github.com/ThrowTheSwitch/Unity +[test]: http://en.wikipedia.org/wiki/Unit_testing + +[CMock] is a tool written in Ruby able to generate entire +[mock functions][mock] in C code from a given C header file. Mock +functions are invaluable in [interaction-based unit testing][ut]. +CMock's generated C code uses Unity. + +[CMock]: http://github.com/ThrowTheSwitch/CMock +[mock]: http://en.wikipedia.org/wiki/Mock_object +[ut]: http://martinfowler.com/articles/mocksArentStubs.html + +[CException] is a C source and header file that provide a simple +[exception mechanism][exn] for C by way of wrapping up the +[setjmp / longjmp][setjmp] standard library calls. Exceptions are a much +cleaner and preferable alternative to managing and passing error codes +up your return call trace. + +[CException]: http://github.com/ThrowTheSwitch/CException +[exn]: http://en.wikipedia.org/wiki/Exception_handling +[setjmp]: http://en.wikipedia.org/wiki/Setjmp.h + +Notes +----- + +* YAML support is included with Ruby - requires no special installation + or configuration. + +* Unity, CMock, and CException are bundled with Ceedling, and + Ceedling is designed to glue them all together for your project + as seamlessly as possible. + + +Installation & Setup: What Exactly Do I Need to Get Started? +------------------------------------------------------------ + +As a [Ruby gem](http://docs.rubygems.org/read/chapter/1): + +1. [Download and install Ruby](http://www.ruby-lang.org/en/downloads/) + +2. Use Ruby's command line gem package manager to install Ceedling: + `gem install ceedling` + (Unity, CMock, and CException come along with Ceedling for free) + +3. Execute Ceedling at command line to create example project + or an empty Ceedling project in your filesystem (executing + `ceedling help` first is, well, helpful). + +Gem install notes: + +1. Steps 1-2 are a one time affair for your local environment. + When steps 1-2 are completed once, only step 3 is needed for + each new project. + +Getting Started after Ceedling is installed: + +1. Once Ceedling is installed, you'll want to start to integrate it with new + and old projects alike. If you wanted to start to work on a new project + named `foo`, Ceedling can create the skeleton of the project using `ceedling + new foo`. Likewise if you already have a project named `bar` and you want to + integrate Ceedling into it, you would run `ceedling new bar` and Ceedling + will create any files and directories it needs to run. + +2. Now that you have Ceedling integrated with a project, you can start using it. + A good starting point to get use to Ceedling either in a new project or an + existing project is creating a new module to get use to Ceedling by issuing + the command `ceedling module:create[unicorn]`. + +General notes: + +1. Certain advanced features of Ceedling rely on gcc and cpp + as preprocessing tools. In most linux systems, these tools + are already available. For Windows environments, we recommend + the [mingw project](http://www.mingw.org/) (Minimalist + GNU for Windows). This represents an optional, additional + setup / installation step to complement the list above. Upon + installing mingw ensure your system path is updated or set + [:environment][:path] in your `project.yml` file (see + environment section later in this document). + +2. To use a project file name other than the default `project.yml` + or place the project file in a directory other than the one + in which you'll run Rake, create an environment variable + `CEEDLING_MAIN_PROJECT_FILE` with your desired project + file path. + +3. To better understand Rake conventions, Rake execution, + and Rakefiles, consult the [Rake tutorial, examples, and + user guide](http://rubyrake.org/). + +4. When using Ceedling in Windows environments, a test file name may + not include the sequences “patch” or “setup”. The Windows Installer + Detection Technology (part of UAC), requires administrator + privileges to execute file names with these strings. + +Now What? How Do I Make It GO? +------------------------------ + +We're getting a little ahead of ourselves here, but it's good +context on how to drive this bus. Everything is done via the command +line. We'll cover conventions and how to actually configure +your project in later sections. + +To run tests, build your release artifact, etc., you will be interacting +with Rake on the command line. Ceedling works with Rake to present +you with named tasks that coordinate the file generation and +build steps needed to accomplish something useful. You can also +add your own independent Rake tasks or create plugins to extend +Ceedling (more on this later). + +* `ceedling [no arguments]`: + + Run the default Rake task (conveniently recognized by the name default + by Rake). Neither Rake nor Ceedling provide a default task. Rake will + abort if run without arguments when no default task is defined. You can + conveniently define a default task in the Rakefile discussed in the + preceding setup & installation section of this document. + +* `ceedling -T`: + + List all available Rake tasks with descriptions (Rake tasks without + descriptions are not listed). -T is a command line switch for Rake and + not the same as tasks that follow. + +* `ceedling --trace`: + + For advanced users troubleshooting a confusing build error, debug + Ceedling or a plugin, --trace provides a stack trace of dependencies + walked during task execution and any Ruby failures along the way. Note + that --trace is a command line switch for Rake and is not the same as + tasks that follow. + +* `ceedling environment`: + + List all configured environment variable names and string values. This + task is helpful in verifying the evaluation of any Ruby expressions in + the [:environment] section of your config file. *: Note: Ceedling may + set some convenience environment variables by default.* + +* `ceedling paths:*`: + + List all paths collected from [:paths] entries in your YAML config + file where * is the name of any section contained in [:paths]. This + task is helpful in verifying the expansion of path wildcards / globs + specified in the [:paths] section of your config file. + +* `ceedling files:assembly` +* `ceedling files:include` +* `ceedling files:source` +* `ceedling files:support` +* `ceedling files:test` + + List all files and file counts collected from the relevant search + paths specified by the [:paths] entries of your YAML config file. The + files:assembly task will only be available if assembly support is + enabled in the [:release_build] section of your configuration file. + +* `ceedling options:*`: + + Load and merge configuration settings into the main project + configuration. Each task is named after a `*.yml` file found in the + configured options directory. See documentation for the configuration + setting [:project][:options_paths] and for options files in advanced + topics. + +* `ceedling test:all`: + + Run all unit tests (rebuilding anything that's changed along the way). + +* `ceedling test:build_only`: + + Build all unit tests, object files and executable but not run them. + +* `ceedling test:delta`: + + Run only those unit tests for which the source or test files have + changed (i.e. incremental build). Note: with the + [:project][:use_test_preprocessor] configuration file option set, + runner files are always regenerated limiting the total efficiency this + text execution option can afford. + +* `ceedling test:*`: + + Execute the named test file or the named source file that has an + accompanying test. No path. Examples: ceedling test:foo.c or ceedling + test:test_foo.c + +* `ceedling test:pattern[*]`: + + Execute any tests whose name and/or path match the regular expression + pattern (case sensitive). Example: ceedling "test:pattern[(I|i)nit]" will + execute all tests named for initialization testing. Note: quotes may + be necessary around the ceedling parameter to distinguish regex characters + from command line operators. + +* `ceedling test:path[*]`: + + Execute any tests whose path contains the given string (case + sensitive). Example: ceedling test:path[foo/bar] will execute all tests + whose path contains foo/bar. Note: both directory separator characters + / and \ are valid. + +* `ceedling release`: + + Build all source into a release artifact (if the release build option + is configured). + +* `ceedling release:compile:*`: + + Sometimes you just need to compile a single file dagnabit. Example: + ceedling release:compile:foo.c + +* `ceedling release:assemble:*`: + + Sometimes you just need to assemble a single file doggonit. Example: + ceedling release:assemble:foo.s + +* `ceedling module:create[Filename]`: +* `ceedling module:create[Filename]`: + + It's often helpful to create a file automatically. What's better than + that? Creating a source file, a header file, and a corresponding test + file all in one step! + + There are also patterns which can be specified to automatically generate + a bunch of files. Try `ceedling module:create[Poodles,mch]` for example! + + The module generator has several options you can configure. + F.e. Generating the source/header/test file in a subdirectory (by adding when calling module:create). + For more info, refer to the [Module Generator](https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md#module-generator) section. + +* `ceedling module:stub[Filename]`: +* `ceedling module:stub[Filename]`: + + So what happens if you've created your API in your header (maybe even using + TDD to do so?) and now you need to start to implement the corresponding C + module? Why not get a head start by using `ceedilng module:stub[headername]` + to automatically create a function skeleton for every function declared in + that header? Better yet, you can call this again whenever you add new functions + to that header to add just the new functions, leaving the old ones alone! + +* `ceedling logging `: + + Enable logging to /logs. Must come before test and release + tasks to log their steps and output. Log names are a concatenation of + project, user, and option files loaded. User and option files are + documented in the advanced topics section of this document. + +* `ceedling verbosity[x] `: + + Change the default verbosity level. [x] ranges from 0 (quiet) to 4 + (obnoxious). Level [3] is the default. The verbosity task must precede + all tasks in the command line list for which output is desired to be + seen. Verbosity settings are generally most meaningful in conjunction + with test and release tasks. + +* `ceedling summary`: + + If plugins are enabled, this task will execute the summary method of + any plugins supporting it. This task is intended to provide a quick + roundup of build artifact metrics without re-running any part of the + build. + +* `ceedling clean`: + + Deletes all toolchain binary artifacts (object files, executables), + test results, and any temporary files. Clean produces no output at the + command line unless verbosity has been set to an appreciable level. + +* `ceedling clobber`: + + Extends clean task's behavior to also remove generated files: test + runners, mocks, preprocessor output. Clobber produces no output at the + command line unless verbosity has been set to an appreciable level. + +* `ceedling options:export`: + + This allows you to export a snapshot of your current tool configuration + as a yaml file. You can specify the name of the file in brackets `[blah.yml]` + or let it default to `tools.yml`. In either case, the produced file can be + used as the tool configuration for you project if desired, and modified as you + wish. + +To better understand Rake conventions, Rake execution, and +Rakefiles, consult the [Rake tutorial, examples, and user guide][guide]. + +[guide]: http://rubyrake.org/ + +At present, none of Ceedling's commands provide persistence. +That is, they must each be specified at the command line each time +they are needed. For instance, Ceedling's verbosity command +only affects output at the time it's run. + +Individual test and release file tasks +are not listed in `-T` output. Because so many files may be present +it's unwieldy to list them all. + +Multiple rake tasks can be executed at the command line (order +is executed as provided). For example, `ceed +clobber test:all release` will removed all generated files; +build and run all tests; and then build all source - in that order. +If any Rake task fails along the way, execution halts before the +next task. + +The `clobber` task removes certain build directories in the +course of deleting generated files. In general, it's best not +to add to source control any Ceedling generated directories +below the root of your top-level build directory. That is, leave +anything Ceedling & its accompanying tools generate out of source +control (but go ahead and add the top-level build directory that +holds all that stuff). Also, since Ceedling is pretty smart about +what it rebuilds and regenerates, you needn't clobber often. + +Important Conventions +===================== + +Directory Structure, Filenames & Extensions +------------------------------------------- + +Much of Ceedling's functionality is driven by collecting files +matching certain patterns inside the paths it's configured +to search. See the documentation for the [:extension] section +of your configuration file (found later in this document) to +configure the file extensions Ceedling uses to match and collect +files. Test file naming is covered later in this section. + +Test files and source files must be segregated by directories. +Any directory structure will do. Tests can be held in subdirectories +within source directories, or tests and source directories +can be wholly separated at the top of your project's directory +tree. + +Search Path Order +----------------- + +When Ceedling searches for files (e.g. looking for header files +to mock) or when it provides search paths to any of the default +gcc toolchain executables, it organizes / prioritizes its search +paths. The order is always: test paths, support paths, source +paths, and then include paths. This can be useful, for instance, +in certain testing scenarios where we desire Ceedling or a compiler +to find a stand-in header file in our support directory before +the actual source header file of the same name. + +This convention only holds when Ceedling is using its default +tool configurations and / or when tests are involved. If you define +your own tools in the configuration file (see the [:tools] section +documented later in this here document), you have complete control +over what directories are searched and in what order. Further, +test and support directories are only searched when appropriate. +That is, when running a release build, test and support directories +are not used at all. + +Source Files & Binary Release Artifacts +--------------------------------------- + +Your binary release artifact results from the compilation and +linking of all source files Ceedling finds in the specified source +directories. At present only source files with a single (configurable) +extension are recognized. That is, `*.c` and `*.cc` files will not +both be recognized - only one or the other. See the configuration +options and defaults in the documentation for the [:extension] +sections of your configuration file (found later in this document). + +Test Files & Executable Test Fixtures +------------------------------------- + +Ceedling builds each individual test file with its accompanying +source file(s) into a single, monolithic test fixture executable. +Test files are recognized by a naming convention: a (configurable) +prefix such as "`test_`" in the file name with the same file extension +as used by your C source files. See the configuration options +and defaults in the documentation for the [:project] and [:extension] +sections of your configuration file (found later in this document). +Depending on your configuration options, Ceedling can recognize +a variety of test file naming patterns in your test search paths. +For example: `test_some_super_functionality.c`, `TestYourSourceFile.cc`, +or `testing_MyAwesomeCode.C` could each be valid test file +names. Note, however, that Ceedling can recognize only one test +file naming convention per project. + +Ceedling knows what files to compile and link into each individual +test executable by way of the #include list contained in each +test file. Any C source files in the configured search directories +that correspond to the header files included in a test file will +be compiled and linked into the resulting test fixture executable. +From this same #include list, Ceedling knows which files to mock +and compile and link into the test executable (if you use mocks +in your tests). That was a lot of clauses and information in a very +few sentences; the example that follows in a bit will make it clearer. + +By naming your test functions according to convention, Ceedling +will extract and collect into a runner C file calls to all your +test case functions. This runner file handles all the execution +minutiae so that your test file can be quite simple and so that +you never forget to wire up a test function to be executed. In this +generated runner lives the `main()` entry point for the resulting +test executable. There are no configuration options for the +naming convention of your test case functions. A test case function +signature must have these three elements: void return, void +parameter list, and the function name prepended with lowercase +"`test`". In other words, a test function signature should look +like this: `void test``[any name you like]``(void)`. + +A commented sample test file follows on the next page. Also, see +the sample project contained in the Ceedling documentation +bundle. + +```c +// test_foo.c ----------------------------------------------- +#include "unity.h" // compile/link in Unity test framework +#include "types.h" // header file with no *.c file -- no compilation/linking +#include "foo.h" // source file foo.c under test +#include "mock_bar.h" // bar.h will be found and mocked as mock_bar.c + compiled/linked in; + // foo.c includes bar.h and uses functions declared in it +#include "mock_baz.h" // baz.h will be found and mocked as mock_baz.c + compiled/linked in + // foo.c includes baz.h and uses functions declared in it + + +void setUp(void) {} // every test file requires this function; + // setUp() is called by the generated runner before each test case function + +void tearDown(void) {} // every test file requires this function; + // tearDown() is called by the generated runner after each test case function + +// a test case function +void test_Foo_Function1_should_Call_Bar_AndGrill(void) +{ + Bar_AndGrill_Expect(); // setup function from mock_bar.c that instructs our + // framework to expect Bar_AndGrill() to be called once + TEST_ASSERT_EQUAL(0xFF, Foo_Function1()); // assertion provided by Unity + // Foo_Function1() calls Bar_AndGrill() & returns a byte +} + +// another test case function +void test_Foo_Function2_should_Call_Baz_Tec(void) +{ + Baz_Tec_ExpectAnd_Return(1); // setup function provided by mock_baz.c that instructs our + // framework to expect Baz_Tec() to be called once and return 1 + TEST_ASSERT_TRUE(Foo_Function2()); // assertion provided by Unity +} + +// end of test_foo.c ---------------------------------------- +``` + +From the test file specified above Ceedling will generate `test_foo_runner.c`; +this runner file will contain `main()` and call both of the example +test case functions. + +The final test executable will be `test_foo.exe` (for Windows +machines or `test_foo.out` for linux systems - depending on default +or configured file extensions). Based on the #include list above, +the test executable will be the output of the linker having processed +`unity.o`, `foo.o`, `mock_bar.o`, `mock_baz.o`, `test_foo.o`, +and `test_foo_runner.o`. Ceedling finds the files, generates +mocks, generates a runner, compiles all the files, and links +everything into the test executable. Ceedling will then run +the test executable and collect test results from it to be reported +to the developer at the command line. + +For more on the assertions and mocks shown, consult the documentation +for Unity and CMock. + +The Magic of Dependency Tracking +-------------------------------- + +Ceedling is pretty smart in using Rake to build up your project's +dependencies. This means that Ceedling automagically rebuilds +all the appropriate files in your project when necessary: when +your configuration changes, Ceedling or any of the other tools +are updated, or your source or test files change. For instance, +if you modify a header file that is mocked, Ceedling will ensure +that the mock is regenerated and all tests that use that mock are +rebuilt and re-run when you initiate a relevant testing task. +When you see things rebuilding, it's for a good reason. Ceedling +attempts to regenerate and rebuild only what's needed for a given +execution of a task. In the case of large projects, assembling +dependencies and acting upon them can cause some delay in executing +tasks. + +With one exception, the trigger to rebuild or regenerate a file +is always a disparity in timestamps between a target file and +its source - if an input file is newer than its target dependency, +the target is rebuilt or regenerated. For example, if the C source +file from which an object file is compiled is newer than that object +file on disk, recompilation will occur (of course, if no object +file exists on disk, compilation will always occur). The one +exception to this dependency behavior is specific to your input +configuration. Only if your logical configuration changes +will a system-wide rebuild occur. Reorganizing your input configuration +or otherwise updating its file timestamp without modifying +the values within the file will not trigger a rebuild. This behavior +handles the various ways in which your input configuration can +change (discussed later in this document) without having changed +your actual project YAML file. + +Ceedling needs a bit of help to accomplish its magic with deep +dependencies. Shallow dependencies are straightforward: +a mock is dependent on the header file from which it's generated, +a test file is dependent upon the source files it includes (see +the preceding conventions section), etc. Ceedling handles +these "out of the box." Deep dependencies are specifically a +C-related phenomenon and occur as a consequence of include statements +within C source files. Say a source file includes a header file +and that header file in turn includes another header file which +includes still another header file. A change to the deepest header +file should trigger a recompilation of the source file, a relinking +of all the object files comprising a test fixture, and a new execution +of that test fixture. + +Ceedling can handle deep dependencies but only with the help +of a C preprocessor. Ceedling is quite capable, but a full C preprocessor +it ain't. Your project can be configured to use a C preprocessor +or not. Simple projects or large projects constructed so as to +be quite flat in their include structure generally don't need +deep dependency preprocessing - and can enjoy the benefits of +faster execution. Legacy code, on the other hand, will almost +always want to be tested with deep preprocessing enabled. Set +up of the C preprocessor is covered in the documentation for the +[:project] and [:tools] section of the configuration file (later +in this document). Ceedling contains all the configuration +necessary to use the gcc preprocessor by default. That is, as +long as gcc is in your system search path, deep preprocessing +of deep dependencies is available to you by simply enabling it +in your project configuration file. + +Ceedling's Build Output +----------------------- + +Ceedling requires a top-level build directory for all the stuff +that it, the accompanying test tools, and your toolchain generate. +That build directory's location is configured in the [:project] +section of your configuration file (discussed later). There +can be a ton of generated files. By and large, you can live a full +and meaningful life knowing absolutely nothing at all about +the files and directories generated below the root build directory. + +As noted already, it's good practice to add your top-level build +directory to source control but nothing generated beneath it. +You'll spare yourself headache if you let Ceedling delete and +regenerate files and directories in a non-versioned corner +of your project's filesystem beneath the top-level build directory. + +The `artifacts` directory is the one and only directory you may +want to know about beneath the top-level build directory. The +subdirectories beneath `artifacts` will hold your binary release +target output (if your project is configured for release builds) +and will serve as the conventional location for plugin output. +This directory structure was chosen specifically because it +tends to work nicely with Continuous Integration setups that +recognize and list build artifacts for retrieval / download. + +The Almighty Project Configuration File (in Glorious YAML) +---------------------------------------------------------- + +Please consult YAML documentation for the finer points of format +and to understand details of our YAML-based configuration file. +We recommend [Wikipedia's entry on YAML](http://en.wikipedia.org/wiki/Yaml) +for this. A few highlights from that reference page: + +* YAML streams are encoded using the set of printable Unicode + characters, either in UTF-8 or UTF-16 + +* Whitespace indentation is used to denote structure; however + tab characters are never allowed as indentation + +* Comments begin with the number sign ( # ), can start anywhere + on a line, and continue until the end of the line unless enclosed + by quotes + +* List members are denoted by a leading hyphen ( - ) with one member + per line, or enclosed in square brackets ( [ ] ) and separated + by comma space ( , ) + +* Hashes are represented using the colon space ( : ) in the form + key: value, either one per line or enclosed in curly braces + ( { } ) and separated by comma space ( , ) + +* Strings (scalars) are ordinarily unquoted, but may be enclosed + in double-quotes ( " ), or single-quotes ( ' ) + +* YAML requires that colons and commas used as list separators + be followed by a space so that scalar values containing embedded + punctuation can generally be represented without needing + to be enclosed in quotes + +* Repeated nodes are initially denoted by an ampersand ( & ) and + thereafter referenced with an asterisk ( * ) + +Notes on what follows: + +* Each of the following sections represent top-level entries + in the YAML configuration file. + +* Unless explicitly specified in the configuration file, default + values are used by Ceedling. + +* These three settings, at minimum, must be specified: + * [:project][:build_root] + * [:paths][:source] + * [:paths][:test] + +* As much as is possible, Ceedling validates your settings in + properly formed YAML. + +* Improperly formed YAML will cause a Ruby error when the YAML + is parsed. This is usually accompanied by a complaint with + line and column number pointing into the project file. + +* Certain advanced features rely on gcc and cpp as preprocessing + tools. In most linux systems, these tools are already available. + For Windows environments, we recommend the [mingw project](http://www.mingw.org/) + (Minimalist GNU for Windows). + +* Ceedling is primarily meant as a build tool to support automated + unit testing. All the heavy lifting is involved there. Creating + a simple binary release build artifact is quite trivial in + comparison. Consequently, most default options and the construction + of Ceedling itself is skewed towards supporting testing though + Ceedling can, of course, build your binary release artifact + as well. Note that complex binary release artifacts (e.g. + application + bootloader or multiple libraries) are beyond + Ceedling's release build ability. + +Conventions / features of Ceedling-specific YAML: + +* Any second tier setting keys anywhere in YAML whose names end + in `_path` or `_paths` are automagically processed like all + Ceedling-specific paths in the YAML to have consistent directory + separators (i.e. "/") and to take advantage of inline Ruby + string expansion (see [:environment] setting below for further + explanation of string expansion). + +**Let's Be Careful Out There:** Ceedling performs validation +on the values you set in your configuration file (this assumes +your YAML is correct and will not fail format parsing, of course). +That said, validation is limited to only those settings Ceedling +uses and those that can be reasonably validated. Ceedling does +not limit what can exist within your configuration file. In this +way, you can take full advantage of YAML as well as add sections +and values for use in your own custom plugins (documented later). +The consequence of this is simple but important. A misspelled +configuration section name or value name is unlikely to cause +Ceedling any trouble. Ceedling will happily process that section +or value and simply use the properly spelled default maintained +internally - thus leading to unexpected behavior without warning. + +project: global project settings + +* `build_root`: + + Top level directory into which generated path structure and files are + placed. Note: this is one of the handful of configuration values that + must be set. The specified path can be absolute or relative to your + working directory. + + **Default**: (none) + +* `use_exceptions`: + + Configures the build environment to make use of CException. Note that + if you do not use exceptions, there's no harm in leaving this as its + default value. + + **Default**: TRUE + +* `use_mocks`: + + Configures the build environment to make use of CMock. Note that if + you do not use mocks, there's no harm in leaving this setting as its + default value. + + **Default**: TRUE + +* `use_test_preprocessor`: + + This option allows Ceedling to work with test files that contain + conditional compilation statements (e.g. #ifdef) and header files you + wish to mock that contain conditional preprocessor statements and/or + macros. + + Ceedling and CMock are advanced tools with sophisticated parsers. + However, they do not include entire C language preprocessors. + Consequently, with this option enabled, Ceedling will use gcc's + preprocessing mode and the cpp preprocessor tool to strip down / + expand test files and headers to their applicable content which can + then be processed by Ceedling and CMock. + + With this option enabled, the gcc & cpp tools must exist in an + accessible system search path and test runner files are always + regenerated. + + **Default**: FALSE + +* `use_preprocessor_directives`: + + After standard preprocessing when `use_test_preprocessor` is used + macros are fully expanded to C code. Some features, for example + TEST_CASE() or TEST_RANGE() from Unity require not-fully preprocessed + file to be detected by Ceedling. To do this gcc directives-only + option is used to expand only conditional compilation statements, + handle directives, but do not expand macros preprocessor and leave + the other content of file untouched. + + With this option enabled, `use_test_preprocessor` must be also enabled + and gcc must exist in an accessible system search path. For other + compilers behavior can be changed by `test_file_preprocessor_directives` + compiler tool. + + **Default**: FALSE + +* `use_deep_dependencies`: + + The base rules and tasks that Ceedling creates using Rake capture most + of the dependencies within a standard project (e.g. when the source + file accompanying a test file changes, the corresponding test fixture + executable will be rebuilt when tests are re-run). However, deep + dependencies cannot be captured this way. If a typedef or macro + changes in a header file three levels of #include statements deep, + this option allows the appropriate incremental build actions to occur + for both test execution and release builds. + + This is accomplished by using the dependencies discovery mode of gcc. + With this option enabled, gcc must exist in an accessible system + search path. + + **Default**: FALSE + +* `generate_deep_dependencies`: + + When `use_deep_dependencies` is set to TRUE, Ceedling will run a separate + build step to generate the deep dependencies. If you are using gcc as your + primary compiler, or another compiler that can generate makefile rules as + a side effect of compilation, then you can set this to FALSE to avoid the + extra build step but still use the deep dependencies data when deciding + which source files to rebuild. + + **Default**: TRUE + +* `test_file_prefix`: + + Ceedling collects test files by convention from within the test file + search paths. The convention includes a unique name prefix and a file + extension matching that of source files. + + Why not simply recognize all files in test directories as test files? + By using the given convention, we have greater flexibility in what we + do with C files in the test directories. + + **Default**: "test_" + +* `options_paths`: + + Just as you may have various build configurations for your source + codebase, you may need variations of your project configuration. + + By specifying options paths, Ceedling will search for other project + YAML files, make command line tasks available (ceedling options:variation + for a variation.yml file), and merge the project configuration of + these option files in with the main project file at runtime. See + advanced topics. + + Note these Rake tasks at the command line - like verbosity or logging + control - must come before the test or release task they are meant to + modify. + + **Default**: `[]` (empty) + +* `release_build`: + + When enabled, a release Rake task is exposed. This configuration + option requires a corresponding release compiler and linker to be + defined (gcc is used as the default). + + More release configuration options are available in the release_build + section. + + **Default**: FALSE + + +Example `[:project]` YAML blurb + +```yaml +:project: + :build_root: project_awesome/build + :use_exceptions: FALSE + :use_test_preprocessor: TRUE + :use_deep_dependencies: TRUE + :options_paths: + - project/options + - external/shared/options + :release_build: TRUE +``` + +Ceedling is primarily concerned with facilitating the somewhat +complicated mechanics of automating unit tests. The same mechanisms +are easily capable of building a final release binary artifact +(i.e. non test code; the thing that is your final working software +that you execute on target hardware). + + +* `output`: + + The name of your release build binary artifact to be found in /artifacts/release. Ceedling sets the default artifact file + extension to that as is explicitly specified in the [:extension] + section or as is system specific otherwise. + + **Default**: `project.exe` or `project.out` + +* `use_assembly`: + + If assembly code is present in the source tree, this option causes + Ceedling to create appropriate build directories and use an assembler + tool (default is the GNU tool as - override available in the [:tools] + section. + + **Default**: FALSE + +* `artifacts`: + + By default, Ceedling copies to the /artifacts/release + directory the output of the release linker and (optionally) a map + file. Many toolchains produce other important output files as well. + Adding a file path to this list will cause Ceedling to copy that file + to the artifacts directory. The artifacts directory is helpful for + organizing important build output files and provides a central place + for tools such as Continuous Integration servers to point to build + output. Selectively copying files prevents incidental build cruft from + needlessly appearing in the artifacts directory. Note that inline Ruby + string replacement is available in the artifacts paths (see discussion + in the [:environment] section). + + **Default**: `[]` (empty) + +Example `[:release_build]` YAML blurb + +```yaml +:release_build: + :output: top_secret.bin + :use_assembly: TRUE + :artifacts: + - build/release/out/c/top_secret.s19 +``` + +**paths**: options controlling search paths for source and header +(and assembly) files + +* `test`: + + All C files containing unit test code. Note: this is one of the + handful of configuration values that must be set. + + **Default**: `[]` (empty) + +* `source`: + + All C files containing release code (code to be tested). Note: this is + one of the handful of configuration values that must be set. + + **Default**: `[]` (empty) + +* `support`: + + Any C files you might need to aid your unit testing. For example, on + occasion, you may need to create a header file containing a subset of + function signatures matching those elsewhere in your code (e.g. a + subset of your OS functions, a portion of a library API, etc.). Why? + To provide finer grained control over mock function substitution or + limiting the size of the generated mocks. + + **Default**: `[]` (empty) + +* `include`: + + Any header files not already in the source search path. Note there's + no practical distinction between this search path and the source + search path; it's merely to provide options or to support any + peculiar source tree organization. + + **Default**: `[]` (empty) + +* `test_toolchain_include`: + + System header files needed by the test toolchain - should your + compiler be unable to find them, finds the wrong system include search + path, or you need a creative solution to a tricky technical problem. + Note that if you configure your own toolchain in the [:tools] section, + this search path is largely meaningless to you. However, this is a + convenient way to control the system include path should you rely on + the default gcc tools. + + **Default**: `[]` (empty) + +* `release_toolchain_include`: + + Same as preceding albeit related to the release toolchain. + + **Default**: `[]` (empty) + +* `` + + Any paths you specify for custom list. List is available to tool + configurations and/or plugins. Note a distinction. The preceding names + are recognized internally to Ceedling and the path lists are used to + build collections of files contained in those paths. A custom list is + just that - a custom list of paths. + +Notes on path grammar within the [:paths] section: + +* Order of search paths listed in [:paths] is preserved when used by an + entry in the [:tools] section + +* Wherever multiple path lists are combined for use Ceedling prioritizes + path groups as follows: + test paths, support paths, source paths, include paths. + + This can be useful, for instance, in certain testing scenarios where + we desire Ceedling or the compiler to find a stand-in header file before + the actual source header file of the same name. + +* Paths: + + 1. can be absolute or relative + + 2. can be singly explicit - a single fully specified path + + 3. can include a glob operator (more on this below) + + 4. can use inline Ruby string replacement (see [:environment] + section for more) + + 5. default as an addition to a specific search list (more on this + in the examples) + + 6. can act to subtract from a glob included in the path list (more + on this in the examples) + +[Globs](http://ruby.about.com/od/beginningruby/a/dir2.htm) +as used by Ceedling are wildcards for specifying directories +without the need to list each and every required search path. +Ceedling globs operate just as Ruby globs except that they are +limited to matching directories and not files. Glob operators +include the following * ** ? [-] {,} (note: this list is space separated +and not comma separated as commas are used within the bracket +operators). + +* `*`: + + All subdirectories of depth 1 below the parent path and including the + parent path + +* `**`: + + All subdirectories recursively discovered below the parent path and + including the parent path + +* `?`: + + Single alphanumeric character wildcard + +* `[x-y]`: + + Single alphanumeric character as found in the specified range + +* `{x,y}`: + + Single alphanumeric character from the specified list + +Example [:paths] YAML blurbs + +```yaml +:paths: + :source: #together the following comprise all source search paths + - project/source/* #expansion yields all subdirectories of depth 1 plus parent directory + - project/lib #single path + :test: #all test search paths + - project/**/test? #expansion yields any subdirectory found anywhere in the project that + #begins with "test" and contains 5 characters + +:paths: + :source: #all source search paths + - +:project/source/** #all subdirectories recursively discovered plus parent directory + - -:project/source/os/generated #subtract os/generated directory from expansion of above glob + #note that '+:' notation is merely aesthetic; default is to add + + :test: #all test search paths + - project/test/bootloader #explicit, single search paths (searched in the order specified) + - project/test/application + - project/test/utilities + + :custom: #custom path list + - "#{PROJECT_ROOT}/other" #inline Ruby string expansion +``` + +Globs and inline Ruby string expansion can require trial and +error to arrive at your intended results. Use the `ceedling paths:*` +command line options (documented in preceding section) to verify +your settings. + +Ceedling relies on file collections automagically assembled +from paths, globs, and file extensions. File collections greatly +simplify project set up. However, sometimes you need to remove +from or add individual files to those collections. + + +* `test`: + + Modify the collection of unit test C files. + + **Default**: `[]` (empty) + +* `source`: + + Modify the collection of all source files used in unit test builds and release builds. + + **Default**: `[]` (empty) + +* `assembly`: + + Modify the (optional) collection of assembly files used in release builds. + + **Default**: `[]` (empty) + +* `include`: + + Modify the collection of all source header files used in unit test builds (e.g. for mocking) and release builds. + + **Default**: `[]` (empty) + +* `support`: + + Modify the collection of supporting C files available to unit tests builds. + + **Default**: `[]` (empty) + +* `libraries`: + + Add a collection of library paths to be included when linking. + + **Default**: `[]` (empty) + + +Note: All path grammar documented in [:paths] section applies +to [:files] path entries - albeit at the file path level and not +the directory level. + +Example [:files] YAML blurb + +```yaml +:files: + :source: + - callbacks/comm.c # entry defaults to file addition + - +:callbacks/comm*.c # add all comm files matching glob pattern + - -:source/board/atm134.c # not our board + :test: + - -:test/io/test_output_manager.c # remove unit tests from test build +``` + +**environment:** inserts environment variables into the shell +instance executing configured tools + +Ceedling creates environment variables from any key / value +pairs in the environment section. Keys become an environment +variable name in uppercase. The values are strings assigned +to those environment variables. These value strings are either +simple string values in YAML or the concatenation of a YAML array. + +Ceedling is able to execute inline Ruby string substitution +code to set environment variables. This evaluation occurs when +the project file is first processed for any environment pair's +value string including the Ruby string substitution pattern +`#{…}`. Note that environment value strings that _begin_ with +this pattern should always be enclosed in quotes. YAML defaults +to processing unquoted text as a string; quoting text is optional. +If an environment pair's value string begins with the Ruby string +substitution pattern, YAML will interpret the string as a Ruby +comment (because of the `#`). Enclosing each environment value +string in quotes is a safe practice. + +[:environment] entries are processed in the configured order +(later entries can reference earlier entries). + +Special case: PATH handling + +In the specific case of specifying an environment key named _path_, +an array of string values will be concatenated with the appropriate +platform-specific path separation character (e.g. ':' on linux, +';' on Windows). All other instances of environment keys assigned +YAML arrays use simple concatenation. + +Example [:environment] YAML blurb + +```yaml +:environment: + - :license_server: gizmo.intranet #LICENSE_SERVER set with value "gizmo.intranet" + - :license: "#{`license.exe`}" #LICENSE set to string generated from shelling out to + #execute license.exe; note use of enclosing quotes + + - :path: #concatenated with path separator (see special case above) + - Tools/gizmo/bin #prepend existing PATH with gizmo path + - "#{ENV['PATH']}" #pattern #{…} triggers ruby evaluation string substitution + #note: value string must be quoted because of '#' + + - :logfile: system/logs/thingamabob.log #LOGFILE set with path for a log file +``` + +**extension**: configure file name extensions used to collect lists of files searched in [:paths] + +* `header`: + + C header files + + **Default**: .h + +* `source`: + + C code files (whether source or test files) + + **Default**: .c + +* `assembly`: + + Assembly files (contents wholly assembly instructions) + + **Default**: .s + +* `object`: + + Resulting binary output of C code compiler (and assembler) + + **Default**: .o + +* `executable`: + + Binary executable to be loaded and executed upon target hardware + + **Default**: .exe or .out (Win or linux) + +* `testpass`: + + Test results file (not likely to ever need a new value) + + **Default**: .pass + +* `testfail`: + + Test results file (not likely to ever need a new value) + + **Default**: .fail + +* `dependencies`: + + File containing make-style dependency rules created by gcc preprocessor + + **Default**: .d + + +Example [:extension] YAML blurb + + :extension: + :source: .cc + :executable: .bin + +**defines**: command line defines used in test and release compilation by configured tools + +* `test`: + + Defines needed for testing. Useful for: + + 1. test files containing conditional compilation statements (i.e. + tests active in only certain contexts) + + 2. testing legacy source wherein the isolation of source under test + afforded by Ceedling and its complementary tools leaves certain + symbols unset when source files are compiled in isolation + + **Default**: `[]` (empty) + +* `test_preprocess`: + + If [:project][:use_test_preprocessor] or + [:project][:use_deep_dependencies] is set and code is structured in a + certain way, the gcc preprocessor may need symbol definitions to + properly preprocess files to extract function signatures for mocking + and extract deep dependencies for incremental builds. + + **Default**: `[]` (empty) + +* ``: + + Replace standard `test` definitions for specified ``definitions. For example: +```yaml + :defines: + :test: + - FOO_STANDARD_CONFIG + :test_foo_config: + - FOO_SPECIFIC_CONFIG +``` + `ceedling test:foo_config` will now have `FOO_SPECIFIC_CONFIG` defined instead of + `FOO_STANDARD_CONFIG`. None of the other tests will have `FOO_SPECIFIC_SPECIFIC`. + + **Default**: `[]` (empty) + +* `release`: + + Defines needed for the release build binary artifact. + + **Default**: `[]` (empty) + +* `release_preprocess`: + + If [:project][:use_deep_dependencies] is set and code is structured in + a certain way, the gcc preprocessor may need symbol definitions to + properly preprocess files for incremental release builds due to deep + dependencies. + + **Default**: `[]` (empty) + +* `use_test_definition`: + + When this option is used the `-D` flag is added to the build option. + + **Default**: FALSE + +Example [:defines] YAML blurb + +```yaml +:defines: + :test: + - UNIT_TESTING #for select cases in source to allow testing with a changed behavior or interface + - OFF=0 + - ON=1 + - FEATURE_X=ON + :source: + - FEATURE_X=ON +``` + + +**libraries**: command line defines used in test and release compilation by configured tools + +Ceedling allows you to pull in specific libraries for the purpose of release and test builds. +It has a few levels of support for this. Start by adding a :libraries main section in your +configuration. In this section, you can optionally have the following subsections: + +* `test`: + + Library files that should be injected into your tests when linking occurs. + These can be specified as either relative or absolute paths. These files MUST + exist when the test attempts to build. + +* `release`: + + Library files that should be injected into your release when linking occurs. These + can be specified as either relative or absolute paths. These files MUST exist when + the release attempts to build UNLESS you are using the subprojects plugin. In that + case, it will attempt to build that library for you as a dynamic dependency. + +* `system`: + + These libraries are assumed to be in the tool path somewhere and shouldn't need to be + specified. The libraries added here will be injected into releases and tests. For example + if you specify `-lm` you can include the math library. The `-l` portion is only necessary + if the `:flag` prefix below doesn't specify it already for you other libraries. + +* `flag`: + + This is the method of adding an argument for each library. For example, gcc really likes + it when you specify “-l${1}” + +* `path_flag`: + + This is the method of adding an path argument for each library path. For example, gcc really + likes it when you specify “-L \"${1}\"” + +Notes: + +* If you've specified your own link step, you are going to want to add ${4} to your argument +list in the place where library files should be added to the command call. For gcc, this is +often the very end. Other tools may vary. + + +**flags**: configure per-file compilation and linking flags + +Ceedling tools (see later [:tools] section) are used to configure +compilation and linking of test and source files. These tool +configurations are a one-size-fits-all approach. Should individual files +require special compilation or linking flags, the settings in the +[:flags] section work in conjunction with tool definitions by way of +argument substitution to achieve this. + +* `release`: + + [:compile] or [:link] flags for release build + +* `test`: + + [:compile] or [:link] flags for test build + +Notes: + +* Ceedling works with the [:release] and [:test] build contexts + as-is; plugins can add additional contexts + +* Only [:compile] and [:link] are recognized operations beneath + a context + +* File specifiers do not include a path or file extension + +* File specifiers are case sensitive (must match original file + name) + +* File specifiers do support regular expressions if encased in quotes + +* '`*`' is a special (optional) file specifier to provide flags + to all files not otherwise specified + + +Example [:flags] YAML blurb + +```yaml +:flags: + :release: + :compile: + :main: # add '-Wall' to compilation of main.c + - -Wall + :fan: # add '--O2' to compilation of fan.c + - --O2 + :'test_.+': # add '-pedantic' to all test-files + - -pedantic + :*: # add '-foo' to compilation of all files not main.c or fan.c + - -foo + :test: + :compile: + :main: # add '--O1' to compilation of main.c as part of test builds including main.c + - --O1 + :link: + :test_main: # add '--bar --baz' to linking of test_main.exe + - --bar + - --baz +``` + +**import**: Load additional config files + +In some cases it is nice to have config files (project.yml, options files) which can +load other config files, for commonly re-used definitions (target processor, +common code modules, etc). + +These can be recursively nested, the included files can include other files. + +To import config files, either provide an array of files to import, or use hashes to set imports. The former is useful if you do not anticipate needing to replace a given file for different configurations (project: or options:). If you need to replace/remove imports based on different configuration files, use the hashed version. The two methods cannot be mixed in the same .yml. + +Example [:import] YAML blurb using array + +```yaml +:import: + - path/to/config.yml + - path/to/another/config.yml +``` +Example [:import] YAML blurb using hashes + +```yaml +:import: + :configA: path/to/config.yml + :configB: path/to/another/config.yml +``` + + +Ceedling sets values for a subset of CMock settings. All CMock +options are available to be set, but only those options set by +Ceedling in an automated fashion are documented below. See CMock +documentation. + +**cmock**: configure CMock's code generation options and set symbols used to modify CMock's compiled features +Ceedling sets values for a subset of CMock settings. All CMock options are available to be set, but only those options set by Ceedling in an automated fashion are documented below. See CMock documentation. + +* `enforce_strict_ordering`: + + Tests fail if expected call order is not same as source order + + **Default**: TRUE + +* `mock_path`: + + Path for generated mocks + + **Default**: /tests/mocks + +* `defines`: + + List of conditional compilation symbols used to configure CMock's + compiled features. See CMock documentation to understand available + options. No symbols must be set unless defaults are inappropriate for + your specific environment. All symbols are used only by Ceedling to + compile CMock C code; contents of [:defines] are ignored by CMock's + Ruby code when instantiated. + + **Default**: `[]` (empty) + +* `verbosity`: + + If not set, defaults to Ceedling's verbosity level + +* `plugins`: + + If [:project][:use_exceptions] is enabled, the internal plugins list is pre-populated with 'cexception'. + + Whether or not you have included [:cmock][:plugins] in your + configuration file, Ceedling automatically adds 'cexception' to the + plugin list if exceptions are enabled. To add to the list Ceedling + provides CMock, simply add [:cmock][:plugins] to your configuration + and specify your desired additional plugins. + + Each of the plugins have their own additional documentation. + + +* `includes`: + + If [:cmock][:unity_helper] set, pre-populated with unity_helper file + name (no path). + + The [:cmock][:includes] list works identically to the plugins list + above with regard to adding additional files to be inserted within + mocks as #include statements. + + +The last four settings above are directly tied to other Ceedling +settings; hence, why they are listed and explained here. The +first setting above, [:enforce_strict_ordering], defaults +to FALSE within CMock. It is set to TRUE by default in Ceedling +as our way of encouraging you to use strict ordering. It's a teeny +bit more expensive in terms of code generated, test execution +time, and complication in deciphering test failures. However, +it's good practice. And, of course, you can always disable it +by overriding the value in the Ceedling YAML configuration file. + + +**cexception**: configure symbols used to modify CException's compiled features + +* `defines`: + + List of conditional compilation symbols used to configure CException's + features in its source and header files. See CException documentation + to understand available options. No symbols must be set unless the + defaults are inappropriate for your specific environment. + + **Default**: `[]` (empty) + + +**unity**: configure symbols used to modify Unity's compiled features + +* `defines`: + + List of conditional compilation symbols used to configure Unity's + features in its source and header files. See Unity documentation to + understand available options. No symbols must be set unless the + defaults are inappropriate for your specific environment. Most Unity + defines can be easily configured through the YAML file. + + **Default**: `[]` (empty) + +Example [:unity] YAML blurbs +```yaml +:unity: #itty bitty processor & toolchain with limited test execution options + :defines: + - UNITY_INT_WIDTH=16 #16 bit processor without support for 32 bit instructions + - UNITY_EXCLUDE_FLOAT #no floating point unit + +:unity: #great big gorilla processor that grunts and scratches + :defines: + - UNITY_SUPPORT_64 #big memory, big counters, big registers + - UNITY_LINE_TYPE=\"unsigned int\" #apparently we're using really long test files, + - UNITY_COUNTER_TYPE=\"unsigned int\" #and we've got a ton of test cases in those test files + - UNITY_FLOAT_TYPE=\"double\" #you betcha +``` + + +Notes on Unity configuration: + +* **Verification** - Ceedling does no verification of your configuration + values. In a properly configured setup, your Unity configuration + values are processed, collected together with any test define symbols + you specify elsewhere, and then passed to your toolchain during test + compilation. Unity's conditional compilation statements, your + toolchain's preprocessor, and/or your toolchain's compiler will + complain appropriately if your specified configuration values are + incorrect, incomplete, or incompatible. + +* **Routing $stdout** - Unity defaults to using `putchar()` in C's + standard library to display test results. For more exotic environments + than a desktop with a terminal (e.g. running tests directly on a + non-PC target), you have options. For example, you could create a + routine that transmits a character via RS232 or USB. Once you have + that routine, you can replace `putchar()` calls in Unity by overriding + the function-like macro `UNITY_OUTPUT_CHAR`. Consult your toolchain + and shell documentation. Eventhough this can also be defined in the YAML file + most shell environments do not handle parentheses as command line arguments + very well. To still be able to add this functionality all necessary + options can be defined in the `unity_config.h`. Unity needs to be told to look for + the `unity_config.h` in the YAML file, though. + +Example [:unity] YAML blurbs +```yaml +:unity: + :defines: + - UNITY_INCLUDE_CONFIG_H +``` + +Example unity_config.h +``` +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +#include "uart_output.h" //Helper library for your custom environment + +#define UNITY_INT_WIDTH 16 +#define UNITY_OUTPUT_START() uart_init(F_CPU, BAUD) //Helperfunction to init UART +#define UNITY_OUTPUT_CHAR(a) uart_putchar(a) //Helperfunction to forward char via UART +#define UNITY_OUTPUT_COMPLETE() uart_complete() //Helperfunction to inform that test has ended + +#endif +``` + + +**tools**: a means for representing command line tools for use under +Ceedling's automation framework + +Ceedling requires a variety of tools to work its magic. By default, +the GNU toolchain (`gcc`, `cpp`, `as`) are configured and ready for +use with no additions to the project configuration YAML file. +However, as most work will require a project-specific toolchain, +Ceedling provides a generic means for specifying / overriding +tools. + +* `test_compiler`: + + Compiler for test & source-under-test code + + - `${1}`: input source + - `${2}`: output object + - `${3}`: optional output list + - `${4}`: optional output dependencies file + + **Default**: `gcc` + +* `test_linker`: + + Linker to generate test fixture executables + + - `${1}`: input objects + - `${2}`: output binary + - `${3}`: optional output map + - `${4}`: optional library list + - `${5}`: optional library path list + + **Default**: `gcc` + +* `test_fixture`: + + Executable test fixture + + - `${1}`: simulator as executable with`${1}` as input binary file argument or native test executable + + **Default**: `${1}` + +* `test_includes_preprocessor`: + + Extractor of #include statements + + - `${1}`: input source file + + **Default**: `cpp` + +* `test_file_preprocessor`: + + Preprocessor of test files (macros, conditional compilation statements) + - `${1}`: input source file + - `${2}`: preprocessed output source file + + **Default**: `gcc` + +* `test_file_preprocessor_directives`: + + Preprocessor of test files to expand only conditional compilation statements, + handle directives, but do not expand macros + + - `${1}`: input source file + - `${2}`: not-fully preprocessed output source file + + **Default**: `gcc` + +* `test_dependencies_generator`: + + Discovers deep dependencies of source & test (for incremental builds) + + - `${1}`: input source file + - `${2}`: compiled object filepath + - `${3}`: output dependencies file + + **Default**: `gcc` + +* `release_compiler`: + + Compiler for release source code + + - `${1}`: input source + - `${2}`: output object + - `${3}`: optional output list + - `${4}`: optional output dependencies file + + **Default**: `gcc` + +* `release_assembler`: + + Assembler for release assembly code + + - `${1}`: input assembly source file + - `${2}`: output object file + + **Default**: `as` + +* `release_linker`: + + Linker for release source code + + - `${1}`: input objects + - `${2}`: output binary + - `${3}`: optional output map + - `${4}`: optional library list + - `${5}`: optional library path list + + **Default**: `gcc` + +* `release_dependencies_generator`: + + Discovers deep dependencies of source files (for incremental builds) + + - `${1}`: input source file + - `${2}`: compiled object filepath + - `${3}`: output dependencies file + + **Default**: `gcc` + + +A Ceedling tool has a handful of configurable elements: + +1. [:executable] - Command line executable (required) + +2. [:arguments] - List of command line arguments + and substitutions (required) + +3. [:name] - Simple name (e.g. "nickname") of tool beyond its + executable name (if not explicitly set then Ceedling will + form a name from the tool's YAML entry name) + +4. [:stderr_redirect] - Control of capturing $stderr messages + {:none, :auto, :win, :unix, :tcsh}. + Defaults to :none if unspecified; create a custom entry by + specifying a simple string instead of any of the available + symbols. + +5. [:background_exec] - Control execution as background process + {:none, :auto, :win, :unix}. + Defaults to :none if unspecified. + +6. [:optional] - By default a tool is required for operation, which + means tests will be aborted if the tool is not present. However, + you can set this to `TRUE` if it's not needed for testing. + + +Tool Element Runtime Substitution +--------------------------------- + +To accomplish useful work on multiple files, a configured tool will most +often require that some number of its arguments or even the executable +itself change for each run. Consequently, every tool's argument list and +executable field possess two means for substitution at runtime. Ceedling +provides two kinds of inline Ruby execution and a notation for +populating elements with dynamically gathered values within the build +environment. + +Tool Element Runtime Substitution: Inline Ruby Execution +-------------------------------------------------------- + +In-line Ruby execution works similarly to that demonstrated for the +[:environment] section except that substitution occurs as the tool is +executed and not at the time the configuration file is first scanned. + +* `#{...}`: + + Ruby string substitution pattern wherein the containing string is + expanded to include the string generated by Ruby code between the + braces. Multiple instances of this expansion can occur within a single + tool element entry string. Note that if this string substitution + pattern occurs at the very beginning of a string in the YAML + configuration the entire string should be enclosed in quotes (see the + [:environment] section for further explanation on this point). + +* `{...} `: + + If an entire tool element string is enclosed with braces, it signifies + that Ceedling should execute the Ruby code contained within those + braces. Say you have a collection of paths on disk and some of those + paths include spaces. Further suppose that a single tool that must use + those paths requires those spaces to be escaped, but all other uses of + those paths requires the paths to remain unchanged. You could use this + Ceedling feature to insert Ruby code that iterates those paths and + escapes those spaces in the array as used by the tool of this example. + +Tool Element Runtime Substitution: Notational Substitution +---------------------------------------------------------- + +A Ceedling tool's other form of dynamic substitution relies on a '$' +notation. These '$' operators can exist anywhere in a string and can be +decorated in any way needed. To use a literal '$', escape it as '\\$'. + +* `$`: + + Simple substitution for value(s) globally available within the runtime + (most often a string or an array). + +* `${#}`: + + When a Ceedling tool's command line is expanded from its configured + representation and used within Ceedling Ruby code, certain calls to + that tool will be made with a parameter list of substitution values. + Each numbered substitution corresponds to a position in a parameter + list. Ceedling Ruby code expects that configured compiler and linker + tools will contain ${1} and ${2} replacement arguments. In the case of + a compiler ${1} will be a C code file path, and ${2} will be the file + path of the resulting object file. For a linker ${1} will be an array + of object files to link, and ${2} will be the resulting binary + executable. For an executable test fixture ${1} is either the binary + executable itself (when using a local toolchain such as gcc) or a + binary input file given to a simulator in its arguments. + + +Example [:tools] YAML blurbs + +```yaml +:tools: + :test_compiler: + :executable: compiler #exists in system search path + :name: 'acme test compiler' + :arguments: + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE #expands to -I search paths + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR #expands to -I search paths + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR #expands to all -D defined symbols + - --network-license #simple command line argument + - -optimize-level 4 #simple command line argument + - "#{`args.exe -m acme.prj`}" #in-line ruby sub to shell out & build string of arguments + - -c ${1} #source code input file (Ruby method call param list sub) + - -o ${2} #object file output (Ruby method call param list sub) + :test_linker: + :executable: /programs/acme/bin/linker.exe #absolute file path + :name: 'acme test linker' + :arguments: + - ${1} #list of object files to link (Ruby method call param list sub) + - -l$-lib: #inline yaml array substitution to link in foo-lib and bar-lib + - foo + - bar + - -o ${2} #executable file output (Ruby method call param list sub) + :test_fixture: + :executable: tools/bin/acme_simulator.exe #relative file path to command line simulator + :name: 'acme test fixture' + :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use + :arguments: + - -mem large #simple command line argument + - -f "${1}" #binary executable input file to simulator (Ruby method call param list sub) +``` + +Resulting command line constructions from preceding example [:tools] YAML blurbs + + > compiler -I"/usr/include” -I”project/tests” + -I"project/tests/support” -I”project/source” -I”project/include” + -DTEST -DLONG_NAMES -network-license -optimize-level 4 arg-foo + arg-bar arg-baz -c project/source/source.c -o + build/tests/out/source.o + +[notes: (1.) "arg-foo arg-bar arg-baz" is a fabricated example +string collected from $stdout as a result of shell execution +of args.exe +(2.) the -c and -o arguments are +fabricated examples simulating a single compilation step for +a test; ${1} & ${2} are single files] + + > \programs\acme\bin\linker.exe thing.o unity.o + test_thing_runner.o test_thing.o mock_foo.o mock_bar.o -lfoo-lib + -lbar-lib -o build\tests\out\test_thing.exe + +[note: in this scenario ${1} is an array of all the object files +needed to link a test fixture executable] + + > tools\bin\acme_simulator.exe -mem large -f "build\tests\out\test_thing.bin 2>&1” + +[note: (1.) :executable could have simply been ${1} - if we were compiling +and running native executables instead of cross compiling (2.) we're using +$stderr redirection to allow us to capture simulator error messages to +$stdout for display at the run's conclusion] + + +Notes: + +* The upper case names are Ruby global constants that Ceedling + builds + +* "COLLECTION_" indicates that Ceedling did some work to assemble + the list. For instance, expanding path globs, combining multiple + path globs into a convenient summation, etc. + +* At present, $stderr redirection is primarily used to capture + errors from test fixtures so that they can be displayed at the + conclusion of a test run. For instance, if a simulator detects + a memory access violation or a divide by zero error, this notice + might go unseen in all the output scrolling past in a terminal. + +* The preprocessing tools can each be overridden with non-gcc + equivalents. However, this is an advanced feature not yet + documented and requires that the replacement toolchain conform + to the same conventions used by gcc. + +**Ceedling Collection Used in Compilation**: + +* `COLLECTION_PATHS_TEST`: + + All test paths + +* `COLLECTION_PATHS_SOURCE`: + + All source paths + +* `COLLECTION_PATHS_INCLUDE`: + + All include paths + +* `COLLECTION_PATHS_SUPPORT`: + + All test support paths + +* `COLLECTION_PATHS_SOURCE_AND_INCLUDE`: + + All source and include paths + +* `COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR`: + + All source and include paths + applicable vendor paths (e.g. + CException's source path if exceptions enabled) + +* `COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE`: + + All test toolchain include paths + +* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE`: + + All test, source, and include paths + +* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR`: + + All test, source, include, and applicable vendor paths (e.g. Unity's + source path plus CMock and CException's source paths if mocks and + exceptions are enabled) + +* `COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE`: + + All release toolchain include paths + +* `COLLECTION_DEFINES_TEST_AND_VENDOR`: + + All symbols specified in [:defines][:test] + symbols defined for + enabled vendor tools - e.g. [:unity][:defines], [:cmock][:defines], + and [:cexception][:defines] + +* `COLLECTION_DEFINES_RELEASE_AND_VENDOR`: + + All symbols specified in [:defines][:release] plus symbols defined by +[:cexception][:defines] if exceptions are enabled + + +Notes: + +* Other collections exist within Ceedling. However, they are + only useful for advanced features not yet documented. + +* Wherever multiple path lists are combined for use Ceedling prioritizes + path groups as follows: test paths, support paths, source paths, include + paths. + This can be useful, for instance, in certain testing scenarios + where we desire Ceedling or the compiler to find a stand-in header file + before the actual source header file of the same name. + + +**plugins**: Ceedling extensions + +* `load_paths`: + + Base paths to search for plugin subdirectories or extra ruby functionalit + + **Default**: `[]` (empty) + +* `enabled`: + + List of plugins to be used - a plugin's name is identical to the + subdirectory that contains it (and the name of certain files within + that subdirectory) + + **Default**: `[]` (empty) + + +Plugins can provide a variety of added functionality to Ceedling. In +general use, it's assumed that at least one reporting plugin will be +used to format test results. However, if no reporting plugins are +specified, Ceedling will print to `$stdout` the (quite readable) raw +test results from all test fixtures executed. + +Example [:plugins] YAML blurb + +```yaml +:plugins: + :load_paths: + - project/tools/ceedling/plugins #home to your collection of plugin directories + - project/support #maybe home to some ruby code your custom plugins share + :enabled: + - stdout_pretty_tests_report #nice test results at your command line + - our_custom_code_metrics_report #maybe you needed line count and complexity metrics, so you + #created a plugin to scan all your code and collect that info +``` + +* `stdout_pretty_tests_report`: + + Prints to $stdout a well-formatted list of ignored and failed tests, + final test counts, and any extraneous output (e.g. printf statements + or simulator memory errors) collected from executing the test + fixtures. Meant to be used with runs at the command line. + +* `stdout_ide_tests_report`: + + Prints to $stdout simple test results formatted such that an IDE + executing test-related Rake tasks can recognize file paths and line + numbers in test failures, etc. Thus, you can click a test result in + your IDE's execution window and jump to the failure (or ignored test) + in your test file (obviously meant to be used with an [IDE like + Eclipse][ide], etc). + + [ide]: http://throwtheswitch.org/white-papers/using-with-ides.html + +* `xml_tests_report`: + + Creates an XML file of test results in the xUnit format (handy for + Continuous Integration build servers or as input to other reporting + tools). Produces a file report.xml in /artifacts/tests. + +* `bullseye`: + + Adds additional Rake tasks to execute tests with the commercial code + coverage tool provided by [Bullseye][]. See readme.txt inside the bullseye + plugin directory for configuration and use instructions. Note: + Bullseye only works with certain compilers and linkers (healthy list + of supported toolchains though). + + [bullseye]: http://www.bullseye.com + +* `gcov`: + + Adds additional Rake tasks to execute tests with the GNU code coverage + tool [gcov][]. See readme.txt inside the gcov directory for configuration + and use instructions. Only works with GNU compiler and linker. + + [gcov]: http://gcc.gnu.org/onlinedocs/gcc/Gcov.html + +* `warnings_report`: + + Scans compiler and linker `$stdout / $stderr` output for the word + 'warning' (case insensitive). All code warnings (or tool warnings) are + logged to a file warnings.log in the appropriate `/artifacts` directory (e.g. test/ for test tasks, `release/` for a + release build, or even `bullseye/` for bullseye runs). + +Module Generator +======================== +Ceedling includes a plugin called module_generator that will create a source, header and test file for you. +There are several possibilities to configure this plugin through your project.yml to suit your project's needs. + +Directory Structure +------------------------------------------- + +The default configuration for directory/project structure is: +```yaml +:module_generator: + :project_root: ./ + :source_root: src/ + :test_root: test/ +``` +You can change these variables in your project.yml file to comply with your project's directory structure. + +If you call `ceedling module:create`, it will create three files: +1. A source file in the source_root +2. A header file in the source_root +3. A test file in the test_root + +If you want your header file to be in another location, +you can specify the ':inc_root:" in your project.yml file: +```yaml +:module_generator: + :inc_root: inc/ +``` +The module_generator will then create the header file in your defined ':inc_root:'. +By default, ':inc_root:' is not defined so the module_generator will use the source_root. + +Sometimes, your project can't be divided into a single src, inc, and test folder. You have several directories +with sources/..., something like this for example: + + - myDriver + - src + - inc + - test + - myOtherDriver + - src + - inc + - test + - ... + +Don't worry, you don't have to manually create the source/header/test files. +The module_generator can accept a path to create a source_root/inc_root/test_root folder with your files: +`ceedling module:create[:]` + +F.e., applied to the above project structure: +`ceedling module:create[myOtherDriver:driver]` +This will make the module_generator run in the subdirectory 'myOtherDriver' and generate the module files +for you in that directory. So, this command will generate the following files: +1. A source file 'driver.c' in /myOtherDriver/ +2. A header file 'driver.h' in /myOtherDriver/ (or if specified) +3. A test file 'test_driver.c' in /myOtherDriver/ + +Naming +------------------------------------------- +By default, the module_generator will generate your files in lowercase. +`ceedling module:create[mydriver]` and `ceedling module:create[myDriver]`(note the uppercase) will generate the same files: +1. mydriver.c +2. mydriver.h +3. test_mydriver.c + +You can configure the module_generator to use a differect naming mechanism through the project.yml: +```yaml +:module_generator: + :naming: "camel" +``` +There are other possibilities as well (bumpy, camel, snake, caps). +Refer to the unity module generator for more info (the unity module generator is used under the hood by module_generator). + + +Boilerplate header +------------------------------------------- +There are two ways of adding a boilerplate header comment to your generated files: +* With a defined string in the project.yml file: + +```yaml +:module_generator: + :boilerplates: + :src: '/* This is Boilerplate code. */' +``` + +Using the command **ceedling module:create[foo]** it creates the source module as follows: + +```c +/* This is Boilerplate code. */ +#include "foo.h" +``` + +It would be the same for **:tst:** and **:inc:** adding its respective options. + +* Defining an external file with boileplate code: + +```yml +:module_generator: + :boilerplate_files: + :src: '\src_boilerplate.txt' + :inc: '\inc_boilerplate.txt' + :tst: '\tst_boilerplate.txt' +``` + +For whatever file names in whichever folder you desire. + + +Advanced Topics (Coming) +======================== + +Modifying Your Configuration without Modifying Your Project File: Option Files & User Files +------------------------------------------------------------------------------------------- + +Modifying your project file without modifying your project file + +Debugging and/or printf() +------------------------- + +When you gotta get your hands dirty... + +Ceedling Plays Nice with Others - Using Ceedling for Tests Alongside Another Release Build Setup +------------------------------------------------------------------------------------------------ + +You've got options. + +Adding Handy Rake Tasks for Your Project (without Fancy Pants Custom Plugins) +----------------------------------------------------------------------------- + +Add a file `rakefile.rb` at the root of your project that loads Ceedling. This +differs whether you are using the gem version or a local Ceedling version. + +Gem Version: +```ruby +require('Ceedling') +Ceedling.load_project +``` + +Local Ceedling Version (assuming local ceedling is in `vendor/ceedling`): +```ruby +PROJECT_CEEDLING_ROOT = "vendor/ceedling" +load "#{PROJECT_CEEDLING_ROOT}/lib/ceedling.rb" +Ceedling.load_project +``` + +Now you simply add your rake task to the file e.g.: +```ruby +desc "Print hello world in sh" # Only tasks with description are listed by ceedling -T +task :hello_world do + sh "echo Hello World!" +end +``` + +The task can now be called with: `ceedling hello_world` + +Working with Non-Desktop Testing Environments +--------------------------------------------- + +For those crazy platforms lacking command line simulators and for which +cross-compiling on the desktop just ain't gonna get it done. + +Creating Custom Plugins +----------------------- + +Oh boy. This is going to take some explaining. diff --git a/CAN_App/vendor/ceedling/docs/CeedlingUpgrade.md b/CAN_App/vendor/ceedling/docs/CeedlingUpgrade.md new file mode 100644 index 0000000..aeab590 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/CeedlingUpgrade.md @@ -0,0 +1,83 @@ +# Upgrading Ceedling + +You'd like to stay in sync with the latest Ceedling... and who wouldn't? Depending on +how you've made use of Ceedling, that may vary slightly. No matter what, though, our first +step is to update Ceedling itself. + +## Step 1: Update Ceedling Itself + +``` +gem update ceedling +``` + +That should do it... unless you don't have a valid connection to the internet. In that case, +you might have to download the gem from rubygems.org and then install it manually: + +``` +gem update ceedling --local=ceedling-filename.zip +``` + +## Step 2: Udpate Projects Using Ceedling + +When you set up your project(s), it was either configured to use the gem directly, or it was +configured to install itself locally (often into a vendor directory). + +For projects that are of the first type, congratulations, you're finished. The project will +automatically use the new ceedling. There MAY be things that need to be tweaked if features have +moved significantly. (And we apologize if that's your situation... as we get to version 1, we're +going to have a stronger focus on backwards compatibility). If your project isn't working perfectly, +skip down to Step 3. + +If the project was installed to have a copy of ceedling locally, you have a choice. You may +choose to continue to run THIS project on the old version of Ceedling. Often this is the +preferred method for legacy projects which only get occasional focus. Why go through the effort +of updating for new tools if it's serving its purpose and you're unlikely to actually use the new +features? + +The other choice, of course, is to update it. To do so, we open a command prompt and address ceedling +from *outside* the project. For example, let's say we have the following structure: + + - projects + - myproject + - project.yml + - src + - tgt + - vendor + +In this case, we'd want to be in the `projects` directory. At that point, we can ask Ceedling to +update our project. + +``` +ceedling upgrade myproject +``` + +Ceedling will automatically look for your project yaml file and do its best to determine what needs +to be updated. If installed locally, this will mean copying the latest copy of Unity, CMock, and +Ceedling. It will also involve copying documentation, if you had that installed. + +## Step 3: Solving Problems + +We wish every project would update seamlessly... unfortunately there is a lot of customization that +goes into each project, and Ceedling often isn't aware of all of these. To make matter worse, Ceedling +has been in pre-release for awhile, meaning it occasionally has significant changes that may break +current installations. We've tried to capture the common ones here: + +### rakefile + +Ceedling is built in a utility called Rake. In the past, rake was the method that the user actually +interacted with Ceedling. That's no longer the case. Using a modern version of Ceedling means that +you issue commands like `ceedling test:all` instead of `rake test:all`. If you have a continuous +integration server or other calling service, it may need to be updated to comply. + +Similarly, older versions of Ceedling actually placed a rakefile in the project directory, allowing +the project to customize its own flow. For the most part this went unused and better ways were later +introduced. At this point, the `rakefile` is more trouble than its worth and often should just be +removed. + +### plugins + +If you have custom plugins installed to your project, the plugin architecture has gone through some +revisions and it may or may not be compatible at this time. Again, this is a problem which should +not exist soon. + + diff --git a/CAN_App/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md b/CAN_App/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md new file mode 100644 index 0000000..a85adef --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md @@ -0,0 +1,207 @@ +# ThrowTheSwitch.org Coding Standard + +Hi. Welcome to the coding standard for ThrowTheSwitch.org. For the most part, +we try to follow these standards to unify our contributors' code into a cohesive +unit (puns intended). You might find places where these standards aren't +followed. We're not perfect. Please be polite where you notice these discrepancies +and we'll try to be polite when we notice yours. + +;) + + +## Why Have A Coding Standard? + +Being consistent makes code easier to understand. We've made an attempt to keep +our standard simple because we also believe that we can only expect someone to +follow something that is understandable. Please do your best. + + +## Our Philosophy + +Before we get into details on syntax, let's take a moment to talk about our +vision for these tools. We're C developers and embedded software developers. +These tools are great to test any C code, but catering to embedded software has +made us more tolerant of compiler quirks. There are a LOT of quirky compilers +out there. By quirky I mean "doesn't follow standards because they feel like +they have a license to do as they wish." + +Our philosophy is "support every compiler we can". Most often, this means that +we aim for writing C code that is standards compliant (often C89... that seems +to be a sweet spot that is almost always compatible). But it also means these +tools are tolerant of things that aren't common. Some that aren't even +compliant. There are configuration options to override the size of standard +types. There are configuration options to force Unity to not use certain +standard library functions. A lot of Unity is configurable and we have worked +hard to make it not TOO ugly in the process. + +Similarly, our tools that parse C do their best. They aren't full C parsers +(yet) and, even if they were, they would still have to accept non-standard +additions like gcc extensions or specifying `@0x1000` to force a variable to +compile to a particular location. It's just what we do, because we like +everything to Just Work™. + +Speaking of having things Just Work™, that's our second philosophy. By that, we +mean that we do our best to have EVERY configuration option have a logical +default. We believe that if you're working with a simple compiler and target, +you shouldn't need to configure very much... we try to make the tools guess as +much as they can, but give the user the power to override it when it's wrong. + + +## Naming Things + +Let's talk about naming things. Programming is all about naming things. We name +files, functions, variables, and so much more. While we're not always going to +find the best name for something, we actually put quite a bit of effort into +finding *What Something WANTS to be Called*™. + +When naming things, we more or less follow this hierarchy, the first being the +most important to us (but we do all four whenever possible): +1. Readable +2. Descriptive +3. Consistent +4. Memorable + + +#### Readable + +We want to read our code. This means we like names and flow that are more +naturally read. We try to avoid double negatives. We try to avoid cryptic +abbreviations (sticking to ones we feel are common). + + +#### Descriptive + +We like descriptive names for things, especially functions and variables. +Finding the right name for something is an important endeavor. You might notice +from poking around our code that this often results in names that are a little +longer than the average. Guilty. We're okay with a tiny bit more typing if it +means our code is easier to understand. + +There are two exceptions to this rule that we also stick to as religiously as +possible: + +First, while we realize hungarian notation (and similar systems for encoding +type information into variable names) is providing a more descriptive name, we +feel that (for the average developer) it takes away from readability and +therefore is to be avoided. + +Second, loop counters and other local throw-away variables often have a purpose +which is obvious. There's no need, therefore, to get carried away with complex +naming. We find i, j, and k are better loop counters than loopCounterVar or +whatnot. We only break this rule when we see that more description could improve +understanding of an algorithm. + + +#### Consistent + +We like consistency, but we're not really obsessed with it. We try to name our +configuration macros in a consistent fashion... you'll notice a repeated use of +UNITY_EXCLUDE_BLAH or UNITY_USES_BLAH macros. This helps users avoid having to +remember each macro's details. + + +#### Memorable + +Where ever it doesn't violate the above principles, we try to apply memorable +names. Sometimes this means using something that is simply descriptive, but +often we strive for descriptive AND unique... we like quirky names that stand +out in our memory and are easier to search for. Take a look through the file +names in Ceedling and you'll get a good idea of what we are talking about here. +Why use preprocess when you can use preprocessinator? Or what better describes a +module in charge of invoking tasks during releases than release_invoker? Don't +get carried away. The names are still descriptive and fulfill the above +requirements, but they don't feel stale. + + +## C and C++ Details + +We don't really want to add to the style battles out there. Tabs or spaces? +How many spaces? Where do the braces go? These are age-old questions that will +never be answered... or at least not answered in a way that will make everyone +happy. + +We've decided on our own style preferences. If you'd like to contribute to these +projects (and we hope that you do), then we ask if you do your best to follow +the same. It will only hurt a little. We promise. + + +#### Whitespace + +Our C-style is to use spaces and to use 4 of them per indent level. It's a nice +power-of-2 number that looks decent on a wide screen. We have no more reason +than that. We break that rule when we have lines that wrap (macros or function +arguments or whatnot). When that happens, we like to indent further to line +things up in nice tidy columns. + +```C + if (stuff_happened) + { + do_something(); + } +``` + + +#### Case + +- Files - all lower case with underscores. +- Variables - all lower case with underscores +- Macros - all caps with underscores. +- Typedefs - all caps with underscores. (also ends with _T). +- Functions - camel cased. Usually named ModuleName_FuncName +- Constants and Globals - camel cased. + + +#### Braces + +The left brace is on the next line after the declaration. The right brace is +directly below that. Everything in between in indented one level. If you're +catching an error and you have a one-line, go ahead and to it on the same line. + +```C + while (blah) + { + //Like so. Even if only one line, we use braces. + } +``` + + +#### Comments + +Do you know what we hate? Old-school C block comments. BUT, we're using them +anyway. As we mentioned, our goal is to support every compiler we can, +especially embedded compilers. There are STILL C compilers out there that only +support old-school block comments. So that is what we're using. We apologize. We +think they are ugly too. + + +## Ruby Details + +Is there really such thing as a Ruby coding standard? Ruby is such a free form +language, it seems almost sacrilegious to suggest that people should comply to +one method! We'll keep it really brief! + + +#### Whitespace + +Our Ruby style is to use spaces and to use 2 of them per indent level. It's a +nice power-of-2 number that really grooves with Ruby's compact style. We have no +more reason than that. We break that rule when we have lines that wrap. When +that happens, we like to indent further to line things up in nice tidy columns. + + +#### Case + +- Files - all lower case with underscores. +- Variables - all lower case with underscores +- Classes, Modules, etc - Camel cased. +- Functions - all lower case with underscores +- Constants - all upper case with underscores + + +## Documentation + +Egad. Really? We use markdown and we like pdf files because they can be made to +look nice while still being portable. Good enough? + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/CAN_App/vendor/ceedling/docs/UnityAssertionsReference.md b/CAN_App/vendor/ceedling/docs/UnityAssertionsReference.md new file mode 100644 index 0000000..f618c72 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/UnityAssertionsReference.md @@ -0,0 +1,787 @@ +# Unity Assertions Reference + +## Background and Overview + +### Super Condensed Version + +- An assertion establishes truth (i.e. boolean True) for a single condition. +Upon boolean False, an assertion stops execution and reports the failure. +- Unity is mainly a rich collection of assertions and the support to gather up +and easily execute those assertions. +- The structure of Unity allows you to easily separate test assertions from +source code in, well, test code. +- Unity's assertions: +- Come in many, many flavors to handle different C types and assertion cases. +- Use context to provide detailed and helpful failure messages. +- Document types, expected values, and basic behavior in your source code for +free. + +### Unity Is Several Things But Mainly It's Assertions + +One way to think of Unity is simply as a rich collection of assertions you can +use to establish whether your source code behaves the way you think it does. +Unity provides a framework to easily organize and execute those assertions in +test code separate from your source code. + +### What's an Assertion? + +At their core, assertions are an establishment of truth - boolean truth. Was this +thing equal to that thing? Does that code doohickey have such-and-such property +or not? You get the idea. Assertions are executable code (to appreciate the big +picture on this read up on the difference between +[link:Dynamic Verification and Static Analysis]). A failing assertion stops +execution and reports an error through some appropriate I/O channel (e.g. +stdout, GUI, file, blinky light). + +Fundamentally, for dynamic verification all you need is a single assertion +mechanism. In fact, that's what the [assert() macro][] in C's standard library +is for. So why not just use it? Well, we can do far better in the reporting +department. C's `assert()` is pretty dumb as-is and is particularly poor for +handling common data types like arrays, structs, etc. And, without some other +support, it's far too tempting to litter source code with C's `assert()`'s. It's +generally much cleaner, manageable, and more useful to separate test and source +code in the way Unity facilitates. + +### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation + +Asserting a simple truth condition is valuable, but using the context of the +assertion is even more valuable. For instance, if you know you're comparing bit +flags and not just integers, then why not use that context to give explicit, +readable, bit-level feedback when an assertion fails? + +That's what Unity's collection of assertions do - capture context to give you +helpful, meaningful assertion failure messages. In fact, the assertions +themselves also serve as executable documentation about types and values in your +source code. So long as your tests remain current with your source and all those +tests pass, you have a detailed, up-to-date view of the intent and mechanisms in +your source code. And due to a wondrous mystery, well-tested code usually tends +to be well designed code. + +## Assertion Conventions and Configurations + +### Naming and Parameter Conventions + +The convention of assertion parameters generally follows this order: + +```c +TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) +``` + +The very simplest assertion possible uses only a single `actual` parameter (e.g. +a simple null check). + +- `Actual` is the value being tested and unlike the other parameters in an + assertion construction is the only parameter present in all assertion variants. +- `Modifiers` are masks, ranges, bit flag specifiers, floating point deltas. +- `Expected` is your expected value (duh) to compare to an `actual` value; it's + marked as an optional parameter because some assertions only need a single + `actual` parameter (e.g. null check). +- `Size/count` refers to string lengths, number of array elements, etc. + +Many of Unity's assertions are clear duplications in that the same data type +is handled by several assertions. The differences among these are in how failure +messages are presented. For instance, a `_HEX` variant of an assertion prints +the expected and actual values of that assertion formatted as hexadecimal. + +#### TEST_ASSERT_X_MESSAGE Variants + +_All_ assertions are complemented with a variant that includes a simple string +message as a final parameter. The string you specify is appended to an assertion +failure message in Unity output. + +For brevity, the assertion variants with a message parameter are not listed +below. Just tack on `_MESSAGE` as the final component to any assertion name in +the reference list below and add a string as the final parameter. + +_Example:_ + +```c +TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) +``` + +becomes messageified like thus... + +```c +TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message ) +``` + +Notes: + +- The `_MESSAGE` variants intentionally do not support `printf` style formatting + since many embedded projects don't support or avoid `printf` for various reasons. + It is possible to use `sprintf` before the assertion to assemble a complex fail + message, if necessary. +- If you want to output a counter value within an assertion fail message (e.g. from + a loop) , building up an array of results and then using one of the `_ARRAY` + assertions (see below) might be a handy alternative to `sprintf`. + +#### TEST_ASSERT_X_ARRAY Variants + +Unity provides a collection of assertions for arrays containing a variety of +types. These are documented in the Array section below. These are almost on par +with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity +type assertion you can tack on `_ARRAY` and run assertions on an entire block of +memory. + +```c + TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} ) +``` + +- `Expected` is an array itself. +- `Size/count` is one or two parameters necessary to establish the number of array + elements and perhaps the length of elements within the array. + +Notes: + +- The `_MESSAGE` variant convention still applies here to array assertions. The + `_MESSAGE` variants of the `_ARRAY` assertions have names ending with + `_ARRAY_MESSAGE`. +- Assertions for handling arrays of floating point values are grouped with float + and double assertions (see immediately following section). + +### TEST_ASSERT_EACH_EQUAL_X Variants + +Unity provides a collection of assertions for arrays containing a variety of +types which can be compared to a single value as well. These are documented in +the Each Equal section below. these are almost on par with the `_MESSAGE` +variants of Unity's Asserts in that for pretty much any Unity type assertion you +can inject `_EACH_EQUAL` and run assertions on an entire block of memory. + +```c +TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} ) +``` + +- `Expected` is a single value to compare to. +- `Actual` is an array where each element will be compared to the expected value. +- `Size/count` is one of two parameters necessary to establish the number of array + elements and perhaps the length of elements within the array. + +Notes: + +- The `_MESSAGE` variant convention still applies here to Each Equal assertions. +- Assertions for handling Each Equal of floating point values are grouped with + float and double assertions (see immediately following section). + +### Configuration + +#### Floating Point Support Is Optional + +Support for floating point types is configurable. That is, by defining the +appropriate preprocessor symbols, floats and doubles can be individually enabled +or disabled in Unity code. This is useful for embedded targets with no floating +point math support (i.e. Unity compiles free of errors for fixed point only +platforms). See Unity documentation for specifics. + +#### Maximum Data Type Width Is Configurable + +Not all targets support 64 bit wide types or even 32 bit wide types. Define the +appropriate preprocessor symbols and Unity will omit all operations from +compilation that exceed the maximum width of your target. See Unity +documentation for specifics. + +## The Assertions in All Their Blessed Glory + +### Basic Fail, Pass and Ignore + +#### `TEST_FAIL()` + +#### `TEST_FAIL_MESSAGE("message")` + +This fella is most often used in special conditions where your test code is +performing logic beyond a simple assertion. That is, in practice, `TEST_FAIL()` +will always be found inside a conditional code block. + +_Examples:_ + +- Executing a state machine multiple times that increments a counter your test +code then verifies as a final step. +- Triggering an exception and verifying it (as in Try / Catch / Throw - see the +[CException](https://github.com/ThrowTheSwitch/CException) project). + +#### `TEST_PASS()` + +#### `TEST_PASS_MESSAGE("message")` + +This will abort the remainder of the test, but count the test as a pass. Under +normal circumstances, it is not necessary to include this macro in your tests... +a lack of failure will automatically be counted as a `PASS`. It is occasionally +useful for tests with `#ifdef`s and such. + +#### `TEST_IGNORE()` + +#### `TEST_IGNORE_MESSAGE("message")` + +Marks a test case (i.e. function meant to contain test assertions) as ignored. +Usually this is employed as a breadcrumb to come back and implement a test case. +An ignored test case has effects if other assertions are in the enclosing test +case (see Unity documentation for more). + +#### `TEST_MESSAGE(message)` + +This can be useful for outputting `INFO` messages into the Unity output stream +without actually ending the test. Like pass and fail messages, it will be output +with the filename and line number. + +### Boolean + +#### `TEST_ASSERT (condition)` + +#### `TEST_ASSERT_TRUE (condition)` + +#### `TEST_ASSERT_FALSE (condition)` + +#### `TEST_ASSERT_UNLESS (condition)` + +A simple wording variation on `TEST_ASSERT_FALSE`.The semantics of +`TEST_ASSERT_UNLESS` aid readability in certain test constructions or +conditional statements. + +#### `TEST_ASSERT_NULL (pointer)` + +#### `TEST_ASSERT_NOT_NULL (pointer)` + +Verify if a pointer is or is not NULL. + +#### `TEST_ASSERT_EMPTY (pointer)` + +#### `TEST_ASSERT_NOT_EMPTY (pointer)` + +Verify if the first element dereferenced from a pointer is or is not zero. This +is particularly useful for checking for empty (or non-empty) null-terminated +C strings, but can be just as easily used for other null-terminated arrays. + +### Signed and Unsigned Integers (of all sizes) + +Large integer sizes can be disabled for build targets that do not support them. +For example, if your target only supports up to 16 bit types, by defining the +appropriate symbols Unity can be configured to omit 32 and 64 bit operations +that would break compilation (see Unity documentation for more). Refer to +Advanced Asserting later in this document for advice on dealing with other word +sizes. + +#### `TEST_ASSERT_EQUAL_INT (expected, actual)` + +#### `TEST_ASSERT_EQUAL_INT8 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_INT16 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_INT32 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_INT64 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_UINT (expected, actual)` + +#### `TEST_ASSERT_EQUAL_UINT8 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_UINT16 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_UINT32 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_UINT64 (expected, actual)` + +### Unsigned Integers (of all sizes) in Hexadecimal + +All `_HEX` assertions are identical in function to unsigned integer assertions +but produce failure messages with the `expected` and `actual` values formatted +in hexadecimal. Unity output is big endian. + +#### `TEST_ASSERT_EQUAL_HEX (expected, actual)` + +#### `TEST_ASSERT_EQUAL_HEX8 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_HEX16 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_HEX32 (expected, actual)` + +#### `TEST_ASSERT_EQUAL_HEX64 (expected, actual)` + +### Characters + +While you can use the 8-bit integer assertions to compare `char`, another option is +to use this specialized assertion which will show printable characters as printables, +otherwise showing the HEX escape code for the characters. + +#### `TEST_ASSERT_EQUAL_CHAR (expected, actual)` + +### Masked and Bit-level Assertions + +Masked and bit-level assertions produce output formatted in hexadecimal. Unity +output is big endian. + +#### `TEST_ASSERT_BITS (mask, expected, actual)` + +Only compares the masked (i.e. high) bits of `expected` and `actual` parameters. + +#### `TEST_ASSERT_BITS_HIGH (mask, actual)` + +Asserts the masked bits of the `actual` parameter are high. + +#### `TEST_ASSERT_BITS_LOW (mask, actual)` + +Asserts the masked bits of the `actual` parameter are low. + +#### `TEST_ASSERT_BIT_HIGH (bit, actual)` + +Asserts the specified bit of the `actual` parameter is high. + +#### `TEST_ASSERT_BIT_LOW (bit, actual)` + +Asserts the specified bit of the `actual` parameter is low. + +### Integer Less Than / Greater Than + +These assertions verify that the `actual` parameter is less than or greater +than `threshold` (exclusive). For example, if the threshold value is 0 for the +greater than assertion will fail if it is 0 or less. There are assertions for +all the various sizes of ints, as for the equality assertions. Some examples: + +#### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)` + +#### `TEST_ASSERT_GREATER_OR_EQUAL_INT16 (threshold, actual)` + +#### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)` + +#### `TEST_ASSERT_LESS_OR_EQUAL_UINT (threshold, actual)` + +#### `TEST_ASSERT_NOT_EQUAL_UINT8 (threshold, actual)` + +### Integer Ranges (of all sizes) + +These assertions verify that the `expected` parameter is within +/- `delta` +(inclusive) of the `actual` parameter. For example, if the expected value is 10 +and the delta is 3 then the assertion will fail for any value outside the range +of 7 - 13. + +#### `TEST_ASSERT_INT_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_INT8_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_INT16_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_INT32_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_INT64_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_UINT_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_UINT8_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_UINT16_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_UINT32_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_UINT64_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_HEX_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_HEX8_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_HEX16_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_HEX32_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_HEX64_WITHIN (delta, expected, actual)` + +#### `TEST_ASSERT_CHAR_WITHIN (delta, expected, actual)` + +### Structs and Strings + +#### `TEST_ASSERT_EQUAL_PTR (expected, actual)` + +Asserts that the pointers point to the same memory location. + +#### `TEST_ASSERT_EQUAL_STRING (expected, actual)` + +Asserts that the null terminated (`'\0'`)strings are identical. If strings are +of different lengths or any portion of the strings before their terminators +differ, the assertion fails. Two NULL strings (i.e. zero length) are considered +equivalent. + +#### `TEST_ASSERT_EQUAL_MEMORY (expected, actual, len)` + +Asserts that the contents of the memory specified by the `expected` and `actual` +pointers is identical. The size of the memory blocks in bytes is specified by +the `len` parameter. + +### Arrays + +`expected` and `actual` parameters are both arrays. `num_elements` specifies the +number of elements in the arrays to compare. + +`_HEX` assertions produce failure messages with expected and actual array +contents formatted in hexadecimal. + +For array of strings comparison behavior, see comments for +`TEST_ASSERT_EQUAL_STRING` in the preceding section. + +Assertions fail upon the first element in the compared arrays found not to +match. Failure messages specify the array index of the failed comparison. + +#### `TEST_ASSERT_EQUAL_INT_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_INT8_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_INT16_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_INT32_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_INT64_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_UINT_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_UINT8_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_UINT16_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_UINT32_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_UINT64_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_HEX_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_HEX8_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_HEX16_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_HEX32_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_HEX64_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_CHAR_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_PTR_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_STRING_ARRAY (expected, actual, num_elements)` + +#### `TEST_ASSERT_EQUAL_MEMORY_ARRAY (expected, actual, len, num_elements)` + +`len` is the memory in bytes to be compared at each array element. + +### Integer Array Ranges (of all sizes) + +These assertions verify that the `expected` array parameter is within +/- `delta` +(inclusive) of the `actual` array parameter. For example, if the expected value is +\[10, 12\] and the delta is 3 then the assertion will fail for any value +outside the range of \[7 - 13, 9 - 15\]. + +#### `TEST_ASSERT_INT_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_INT8_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_INT16_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_INT32_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_INT64_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_UINT_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_UINT8_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_UINT16_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_UINT32_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_UINT64_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_HEX_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_HEX8_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_HEX16_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_HEX32_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_HEX64_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +#### `TEST_ASSERT_CHAR_ARRAY_WITHIN (delta, expected, actual, num_elements)` + +### Each Equal (Arrays to Single Value) + +`expected` are single values and `actual` are arrays. `num_elements` specifies +the number of elements in the arrays to compare. + +`_HEX` assertions produce failure messages with expected and actual array +contents formatted in hexadecimal. + +Assertions fail upon the first element in the compared arrays found not to +match. Failure messages specify the array index of the failed comparison. + +#### `TEST_ASSERT_EACH_EQUAL_INT (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_CHAR (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_PTR (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_STRING (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_MEMORY (expected, actual, len, num_elements)` + +`len` is the memory in bytes to be compared at each array element. + +### Floating Point (If enabled) + +#### `TEST_ASSERT_FLOAT_WITHIN (delta, expected, actual)` + +Asserts that the `actual` value is within +/- `delta` of the `expected` value. +The nature of floating point representation is such that exact evaluations of +equality are not guaranteed. + +#### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)` + +Asserts that the ?actual?value is "close enough to be considered equal" to the +`expected` value. If you are curious about the details, refer to the Advanced +Asserting section for more details on this. Omitting a user-specified delta in a +floating point assertion is both a shorthand convenience and a requirement of +code generation conventions for CMock. + +#### `TEST_ASSERT_EQUAL_FLOAT_ARRAY (expected, actual, num_elements)` + +See Array assertion section for details. Note that individual array element +float comparisons are executed using T?EST_ASSERT_EQUAL_FLOAT?.That is, user +specified delta comparison values requires a custom-implemented floating point +array assertion. + +#### `TEST_ASSERT_FLOAT_IS_INF (actual)` + +Asserts that `actual` parameter is equivalent to positive infinity floating +point representation. + +#### `TEST_ASSERT_FLOAT_IS_NEG_INF (actual)` + +Asserts that `actual` parameter is equivalent to negative infinity floating +point representation. + +#### `TEST_ASSERT_FLOAT_IS_NAN (actual)` + +Asserts that `actual` parameter is a Not A Number floating point representation. + +#### `TEST_ASSERT_FLOAT_IS_DETERMINATE (actual)` + +Asserts that ?actual?parameter is a floating point representation usable for +mathematical operations. That is, the `actual` parameter is neither positive +infinity nor negative infinity nor Not A Number floating point representations. + +#### `TEST_ASSERT_FLOAT_IS_NOT_INF (actual)` + +Asserts that `actual` parameter is a value other than positive infinity floating +point representation. + +#### `TEST_ASSERT_FLOAT_IS_NOT_NEG_INF (actual)` + +Asserts that `actual` parameter is a value other than negative infinity floating +point representation. + +#### `TEST_ASSERT_FLOAT_IS_NOT_NAN (actual)` + +Asserts that `actual` parameter is a value other than Not A Number floating +point representation. + +#### `TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE (actual)` + +Asserts that `actual` parameter is not usable for mathematical operations. That +is, the `actual` parameter is either positive infinity or negative infinity or +Not A Number floating point representations. + +### Double (If enabled) + +#### `TEST_ASSERT_DOUBLE_WITHIN (delta, expected, actual)` + +Asserts that the `actual` value is within +/- `delta` of the `expected` value. +The nature of floating point representation is such that exact evaluations of +equality are not guaranteed. + +#### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)` + +Asserts that the `actual` value is "close enough to be considered equal" to the +`expected` value. If you are curious about the details, refer to the Advanced +Asserting section for more details. Omitting a user-specified delta in a +floating point assertion is both a shorthand convenience and a requirement of +code generation conventions for CMock. + +#### `TEST_ASSERT_EQUAL_DOUBLE_ARRAY (expected, actual, num_elements)` + +See Array assertion section for details. Note that individual array element +double comparisons are executed using `TEST_ASSERT_EQUAL_DOUBLE`.That is, user +specified delta comparison values requires a custom implemented double array +assertion. + +#### `TEST_ASSERT_DOUBLE_IS_INF (actual)` + +Asserts that `actual` parameter is equivalent to positive infinity floating +point representation. + +#### `TEST_ASSERT_DOUBLE_IS_NEG_INF (actual)` + +Asserts that `actual` parameter is equivalent to negative infinity floating point +representation. + +#### `TEST_ASSERT_DOUBLE_IS_NAN (actual)` + +Asserts that `actual` parameter is a Not A Number floating point representation. + +#### `TEST_ASSERT_DOUBLE_IS_DETERMINATE (actual)` + +Asserts that `actual` parameter is a floating point representation usable for +mathematical operations. That is, the ?actual?parameter is neither positive +infinity nor negative infinity nor Not A Number floating point representations. + +#### `TEST_ASSERT_DOUBLE_IS_NOT_INF (actual)` + +Asserts that `actual` parameter is a value other than positive infinity floating +point representation. + +#### `TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF (actual)` + +Asserts that `actual` parameter is a value other than negative infinity floating +point representation. + +#### `TEST_ASSERT_DOUBLE_IS_NOT_NAN (actual)` + +Asserts that `actual` parameter is a value other than Not A Number floating +point representation. + +#### `TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE (actual)` + +Asserts that `actual` parameter is not usable for mathematical operations. That +is, the `actual` parameter is either positive infinity or negative infinity or +Not A Number floating point representations. + +## Advanced Asserting: Details On Tricky Assertions + +This section helps you understand how to deal with some of the trickier +assertion situations you may run into. It will give you a glimpse into some of +the under-the-hood details of Unity's assertion mechanisms. If you're one of +those people who likes to know what is going on in the background, read on. If +not, feel free to ignore the rest of this document until you need it. + +### How do the EQUAL assertions work for FLOAT and DOUBLE? + +As you may know, directly checking for equality between a pair of floats or a +pair of doubles is sloppy at best and an outright no-no at worst. Floating point +values can often be represented in multiple ways, particularly after a series of +operations on a value. Initializing a variable to the value of 2.0 is likely to +result in a floating point representation of 2 x 20,but a series of +mathematical operations might result in a representation of 8 x 2-2 +that also evaluates to a value of 2. At some point repeated operations cause +equality checks to fail. + +So Unity doesn't do direct floating point comparisons for equality. Instead, it +checks if two floating point values are "really close." If you leave Unity +running with defaults, "really close" means "within a significant bit or two." +Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN` +with the `delta` parameter calculated on the fly. For single precision, delta is +the expected value multiplied by 0.00001, producing a very small proportional +range around the expected value. + +If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So +any value between 19,999.8 and 20,000.2 will satisfy the equality check. This +works out to be roughly a single bit of range for a single-precision number, and +that's just about as tight a tolerance as you can reasonably get from a floating +point value. + +So what happens when it's zero? Zero - even more than other floating point +values - can be represented many different ways. It doesn't matter if you have +0 x 20 or 0 x 263.It's still zero, right? Luckily, if you +subtract these values from each other, they will always produce a difference of +zero, which will still fall between 0 plus or minus a delta of 0. So it still +works! + +Double precision floating point numbers use a much smaller multiplier, again +approximating a single bit of error. + +If you don't like these ranges and you want to make your floating point equality +assertions less strict, you can change these multipliers to whatever you like by +defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity +documentation for more. + +### How do we deal with targets with non-standard int sizes? + +It's "fun" that C is a standard where something as fundamental as an integer +varies by target. According to the C standard, an `int` is to be the target's +natural register size, and it should be at least 16-bits and a multiple of a +byte. It also guarantees an order of sizes: + +```C +char <= short <= int <= long <= long long +``` + +Most often, `int` is 32-bits. In many cases in the embedded world, `int` is +16-bits. There are rare microcontrollers out there that have 24-bit integers, +and this remains perfectly standard C. + +To make things even more interesting, there are compilers and targets out there +that have a hard choice to make. What if their natural register size is 10-bits +or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least +16-bits AND the requirement to match the natural register size. In these +situations, they often choose the natural register size, leaving us with +something like this: + +```C +char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit) +``` + +Um... yikes. It's obviously breaking a rule or two... but they had to break SOME +rules, so they made a choice. + +When the C99 standard rolled around, it introduced alternate standard-size types. +It also introduced macros for pulling in MIN/MAX values for your integer types. +It's glorious! Unfortunately, many embedded compilers can't be relied upon to +use the C99 types (Sometimes because they have weird register sizes as described +above. Sometimes because they don't feel like it?). + +A goal of Unity from the beginning was to support every combination of +microcontroller or microprocessor and C compiler. Over time, we've gotten really +close to this. There are a few tricks that you should be aware of, though, if +you're going to do this effectively on some of these more idiosyncratic targets. + +First, when setting up Unity for a new target, you're going to want to pay +special attention to the macros for automatically detecting types +(where available) or manually configuring them yourself. You can get information +on both of these in Unity's documentation. + +What about the times where you suddenly need to deal with something odd, like a +24-bit `int`? The simplest solution is to use the next size up. If you have a +24-bit `int`, configure Unity to use 32-bit integers. If you have a 12-bit +`int`, configure Unity to use 16 bits. There are two ways this is going to +affect you: + +1. When Unity displays errors for you, it's going to pad the upper unused bits +with zeros. +2. You're going to have to be careful of assertions that perform signed +operations, particularly `TEST_ASSERT_INT_WITHIN`.Such assertions might wrap +your `int` in the wrong place, and you could experience false failures. You can +always back down to a simple `TEST_ASSERT` and do the operations yourself. + +*Find The Latest of This And More at [ThrowTheSwitch.org][]* + +[assert() macro]: http://en.wikipedia.org/en/wiki/Assert.h +[ThrowTheSwitch.org]: https://throwtheswitch.org diff --git a/CAN_App/vendor/ceedling/docs/UnityConfigurationGuide.md b/CAN_App/vendor/ceedling/docs/UnityConfigurationGuide.md new file mode 100644 index 0000000..493b142 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/UnityConfigurationGuide.md @@ -0,0 +1,505 @@ +# Unity Configuration Guide + +## C Standards, Compilers and Microcontrollers + +The embedded software world contains its challenges. +Compilers support different revisions of the C Standard. +They ignore requirements in places, sometimes to make the language more usable in some special regard. +Sometimes it's to simplify their support. +Sometimes it's due to specific quirks of the microcontroller they are targeting. +Simulators add another dimension to this menagerie. + +Unity is designed to run on almost anything that is targeted by a C compiler. +It would be awesome if this could be done with zero configuration. +While there are some targets that come close to this dream, it is sadly not universal. +It is likely that you are going to need at least a couple of the configuration options described in this document. + +All of Unity's configuration options are `#defines`. +Most of these are simple definitions. +A couple are macros with arguments. +They live inside the unity_internals.h header file. +We don't necessarily recommend opening that file unless you really need to. +That file is proof that a cross-platform library is challenging to build. +From a more positive perspective, it is also proof that a great deal of complexity can be centralized primarily to one place to provide a more consistent and simple experience elsewhere. + +### Using These Options + +It doesn't matter if you're using a target-specific compiler and a simulator or a native compiler. +In either case, you've got a couple choices for configuring these options: + +1. Because these options are specified via C defines, you can pass most of these options to your compiler through command line compiler flags. Even if you're using an embedded target that forces you to use their overbearing IDE for all configuration, there will be a place somewhere in your project to configure defines for your compiler. +2. You can create a custom `unity_config.h` configuration file (present in your toolchain's search paths). + In this file, you will list definitions and macros specific to your target. All you must do is define `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any further definitions it may need. + +Unfortunately, it doesn't usually work well to just #define these things in the test itself. +These defines need to take effect where ever unity.h is included. +This would be test test, the test runner (if you're generating one), and from unity.c when it's compiled. + +## The Options + +### Integer Types + +If you've been a C developer for long, you probably already know that C's concept of an integer varies from target to target. +The C Standard has rules about the `int` matching the register size of the target microprocessor. +It has rules about the `int` and how its size relates to other integer types. +An `int` on one target might be 16 bits while on another target it might be 64. +There are more specific types in compilers compliant with C99 or later, but that's certainly not every compiler you are likely to encounter. +Therefore, Unity has a number of features for helping to adjust itself to match your required integer sizes. +It starts off by trying to do it automatically. + +#### `UNITY_EXCLUDE_STDINT_H` + +The first thing that Unity does to guess your types is check `stdint.h`. +This file includes defines like `UINT_MAX` that Unity can use to learn a lot about your system. +It's possible you don't want it to do this (um. why not?) or (more likely) it's possible that your system doesn't support `stdint.h`. +If that's the case, you're going to want to define this. +That way, Unity will know to skip the inclusion of this file and you won't be left with a compiler error. + +_Example:_ + +```C +#define UNITY_EXCLUDE_STDINT_H +``` + +#### `UNITY_EXCLUDE_LIMITS_H` + +The second attempt to guess your types is to check `limits.h`. +Some compilers that don't support `stdint.h` could include `limits.h` instead. +If you don't want Unity to check this file either, define this to make it skip the inclusion. + +_Example:_ + +```C +#define UNITY_EXCLUDE_LIMITS_H +``` + +If you've disabled both of the automatic options above, you're going to have to do the configuration yourself. +Don't worry. +Even this isn't too bad... there are just a handful of defines that you are going to specify if you don't like the defaults. + +#### `UNITY_INT_WIDTH` + +Define this to be the number of bits an `int` takes up on your system. +The default, if not autodetected, is 32 bits. + +_Example:_ + +```C +#define UNITY_INT_WIDTH 16 +``` + +#### `UNITY_LONG_WIDTH` + +Define this to be the number of bits a `long` takes up on your system. +The default, if not autodetected, is 32 bits. +This is used to figure out what kind of 64-bit support your system can handle. +Does it need to specify a `long` or a `long long` to get a 64-bit value. +On 16-bit systems, this option is going to be ignored. + +_Example:_ + +```C +#define UNITY_LONG_WIDTH 16 +``` + +#### `UNITY_POINTER_WIDTH` + +Define this to be the number of bits a pointer takes up on your system. +The default, if not autodetected, is 32-bits. +If you're getting ugly compiler warnings about casting from pointers, this is the one to look at. + +_Hint:_ In order to support exotic processors (for example TI C55x with a pointer width of 23-bit), choose the next power of two (in this case 32-bit). + +_Supported values:_ 16, 32 and 64 + +_Example:_ + +```C +// Choose on of these #defines to set your pointer width (if not autodetected) +//#define UNITY_POINTER_WIDTH 16 +//#define UNITY_POINTER_WIDTH 32 +#define UNITY_POINTER_WIDTH 64 // Set UNITY_POINTER_WIDTH to 64-bit +``` + +#### `UNITY_SUPPORT_64` + +Unity will automatically include 64-bit support if it auto-detects it, or if your `int`, `long`, or pointer widths are greater than 32-bits. +Define this to enable 64-bit support if none of the other options already did it for you. +There can be a significant size and speed impact to enabling 64-bit support on small targets, so don't define it if you don't need it. + +_Example:_ + +```C +#define UNITY_SUPPORT_64 +``` + +### Floating Point Types + +In the embedded world, it's not uncommon for targets to have no support for floating point operations at all or to have support that is limited to only single precision. +We are able to guess integer sizes on the fly because integers are always available in at least one size. +Floating point, on the other hand, is sometimes not available at all. +Trying to include `float.h` on these platforms would result in an error. This leaves manual configuration as the only option. + +#### `UNITY_INCLUDE_FLOAT` + +#### `UNITY_EXCLUDE_FLOAT` + +#### `UNITY_INCLUDE_DOUBLE` + +#### `UNITY_EXCLUDE_DOUBLE` + +By default, Unity guesses that you will want single precision floating point support, but not double precision. +It's easy to change either of these using the include and exclude options here. +You may include neither, either, or both, as suits your needs. +For features that are enabled, the following floating point options also become available. + +_Example:_ + +```C +//what manner of strange processor is this? +#define UNITY_EXCLUDE_FLOAT +#define UNITY_INCLUDE_DOUBLE +``` + +#### `UNITY_EXCLUDE_FLOAT_PRINT` + +Unity aims for as small of a footprint as possible and avoids most standard library calls (some embedded platforms don’t have a standard library!). +Because of this, its routines for printing integer values are minimalist and hand-coded. +Therefore, the display of floating point values during a failure are optional. +By default, Unity will print the actual results of floating point assertion failure (e.g. ”Expected 4.56 Was 4.68”). +To not include this extra support, you can use this define to instead respond to a failed assertion with a message like ”Values Not Within Delta”. +If you would like verbose failure messages for floating point assertions, use these options to give more explicit failure messages. + +_Example:_ + +```C +#define UNITY_EXCLUDE_FLOAT_PRINT +``` + +#### `UNITY_FLOAT_TYPE` + +If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C floats. +If your compiler supports a specialty floating point type, you can always override this behavior by using this definition. + +_Example:_ + +```C +#define UNITY_FLOAT_TYPE float16_t +``` + +#### `UNITY_DOUBLE_TYPE` + +If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard C doubles. +If you would like to change this, you can specify something else by using this option. +For example, defining `UNITY_DOUBLE_TYPE` to `long double` could enable gargantuan floating point types on your 64-bit processor instead of the standard `double`. + +_Example:_ + +```C +#define UNITY_DOUBLE_TYPE long double +``` + +#### `UNITY_FLOAT_PRECISION` + +#### `UNITY_DOUBLE_PRECISION` + +If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as documented in the big daddy Unity Assertion Guide, you will learn that they are not really asserting that two values are equal but rather that two values are "close enough" to equal. +"Close enough" is controlled by these precision configuration options. +If you are working with 32-bit floats and/or 64-bit doubles (the normal on most processors), you should have no need to change these options. +They are both set to give you approximately 1 significant bit in either direction. +The float precision is 0.00001 while the double is 10-12. +For further details on how this works, see the appendix of the Unity Assertion Guide. + +_Example:_ + +```C +#define UNITY_FLOAT_PRECISION 0.001f +``` + +### Miscellaneous + +#### `UNITY_EXCLUDE_STDDEF_H` + +Unity uses the `NULL` macro, which defines the value of a null pointer constant, defined in `stddef.h` by default. +If you want to provide your own macro for this, you should exclude the `stddef.h` header file by adding this define to your configuration. + +_Example:_ + +```C +#define UNITY_EXCLUDE_STDDEF_H +``` + +#### `UNITY_INCLUDE_PRINT_FORMATTED` + +Unity provides a simple (and very basic) printf-like string output implementation, which is able to print a string modified by the following format string modifiers: + +- __%d__ - signed value (decimal) +- __%i__ - same as __%i__ +- __%u__ - unsigned value (decimal) +- __%f__ - float/Double (if float support is activated) +- __%g__ - same as __%f__ +- __%b__ - binary prefixed with "0b" +- __%x__ - hexadecimal (upper case) prefixed with "0x" +- __%X__ - same as __%x__ +- __%p__ - pointer (same as __%x__ or __%X__) +- __%c__ - a single character +- __%s__ - a string (e.g. "string") +- __%%__ - The "%" symbol (escaped) + +_Example:_ + +```C +#define UNITY_INCLUDE_PRINT_FORMATTED + +int a = 0xfab1; +TEST_PRINTF("Decimal %d\n", -7); +TEST_PRINTF("Unsigned %u\n", 987); +TEST_PRINTF("Float %f\n", 3.1415926535897932384); +TEST_PRINTF("Binary %b\n", 0xA); +TEST_PRINTF("Hex %X\n", 0xFAB); +TEST_PRINTF("Pointer %p\n", &a); +TEST_PRINTF("Character %c\n", 'F'); +TEST_PRINTF("String %s\n", "My string"); +TEST_PRINTF("Percent %%\n"); +TEST_PRINTF("Color Red \033[41mFAIL\033[00m\n"); +TEST_PRINTF("\n"); +TEST_PRINTF("Multiple (%d) (%i) (%u) (%x)\n", -100, 0, 200, 0x12345); +``` + +### Toolset Customization + +In addition to the options listed above, there are a number of other options which will come in handy to customize Unity's behavior for your specific toolchain. +It is possible that you may not need to touch any of these... but certain platforms, particularly those running in simulators, may need to jump through extra hoops to run properly. +These macros will help in those situations. + +#### `UNITY_OUTPUT_CHAR(a)` + +#### `UNITY_OUTPUT_FLUSH()` + +#### `UNITY_OUTPUT_START()` + +#### `UNITY_OUTPUT_COMPLETE()` + +By default, Unity prints its results to `stdout` as it runs. +This works perfectly fine in most situations where you are using a native compiler for testing. +It works on some simulators as well so long as they have `stdout` routed back to the command line. +There are times, however, where the simulator will lack support for dumping results or you will want to route results elsewhere for other reasons. +In these cases, you should define the `UNITY_OUTPUT_CHAR` macro. +This macro accepts a single character at a time (as an `int`, since this is the parameter type of the standard C `putchar` function most commonly used). +You may replace this with whatever function call you like. + +_Example:_ +Say you are forced to run your test suite on an embedded processor with no `stdout` option. +You decide to route your test result output to a custom serial `RS232_putc()` function you wrote like thus: + +```C +#include "RS232_header.h" +... +#define UNITY_OUTPUT_CHAR(a) RS232_putc(a) +#define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) +#define UNITY_OUTPUT_FLUSH() RS232_flush() +#define UNITY_OUTPUT_COMPLETE() RS232_close() +``` + +_Note:_ +`UNITY_OUTPUT_FLUSH()` can be set to the standard out flush function simply by specifying `UNITY_USE_FLUSH_STDOUT`. +No other defines are required. + +#### `UNITY_OUTPUT_FOR_ECLIPSE` + +#### `UNITY_OUTPUT_FOR_IAR_WORKBENCH` + +#### `UNITY_OUTPUT_FOR_QT_CREATOR` + +When managing your own builds, it is often handy to have messages output in a format which is recognized by your IDE. +These are some standard formats which can be supported. +If you're using Ceedling to manage your builds, it is better to stick with the standard format (leaving these all undefined) and allow Ceedling to use its own decorators. + +#### `UNITY_PTR_ATTRIBUTE` + +Some compilers require a custom attribute to be assigned to pointers, like `near` or `far`. +In these cases, you can give Unity a safe default for these by defining this option with the attribute you would like. + +_Example:_ + +```C +#define UNITY_PTR_ATTRIBUTE __attribute__((far)) +#define UNITY_PTR_ATTRIBUTE near +``` + +#### `UNITY_PRINT_EOL` + +By default, Unity outputs \n at the end of each line of output. +This is easy to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR system. +Feel free to override this and to make it whatever you wish. + +_Example:_ + +```C +#define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n'); } +``` + +#### `UNITY_EXCLUDE_DETAILS` + +This is an option for if you absolutely must squeeze every byte of memory out of your system. +Unity stores a set of internal scratchpads which are used to pass extra detail information around. +It's used by systems like CMock in order to report which function or argument flagged an error. +If you're not using CMock and you're not using these details for other things, then you can exclude them. + +_Example:_ + +```C +#define UNITY_EXCLUDE_DETAILS +``` + +#### `UNITY_PRINT_TEST_CONTEXT` + +This option allows you to specify your own function to print additional context as part of the error message when a test has failed. +It can be useful if you want to output some specific information about the state of the test at the point of failure, and `UNITY_SET_DETAILS` isn't flexible enough for your needs. + +_Example:_ + +```C +#define UNITY_PRINT_TEST_CONTEXT PrintIterationCount + +extern int iteration_count; + +void PrintIterationCount(void) +{ + UnityPrintFormatted("At iteration #%d: ", iteration_count); +} +``` + +#### `UNITY_EXCLUDE_SETJMP` + +If your embedded system doesn't support the standard library setjmp, you can exclude Unity's reliance on this by using this define. +This dropped dependence comes at a price, though. +You will be unable to use custom helper functions for your tests, and you will be unable to use tools like CMock. +Very likely, if your compiler doesn't support setjmp, you wouldn't have had the memory space for those things anyway, though... so this option exists for those situations. + +_Example:_ + +```C +#define UNITY_EXCLUDE_SETJMP +``` + +#### `UNITY_OUTPUT_COLOR` + +If you want to add color using ANSI escape codes you can use this define. + +_Example:_ + +```C +#define UNITY_OUTPUT_COLOR +``` + +#### `UNITY_SHORTHAND_AS_INT` + +#### `UNITY_SHORTHAND_AS_MEM` + +#### `UNITY_SHORTHAND_AS_RAW` + +#### `UNITY_SHORTHAND_AS_NONE` + +These options give you control of the `TEST_ASSERT_EQUAL` and the `TEST_ASSERT_NOT_EQUAL` shorthand assertions. +Historically, Unity treated the former as an alias for an integer comparison. +It treated the latter as a direct comparison using `!=`. +This asymmetry was confusing, but there was much disagreement as to how best to treat this pair of assertions. +These four options will allow you to specify how Unity will treat these assertions. + +- AS INT - the values will be cast to integers and directly compared. + Arguments that don't cast easily to integers will cause compiler errors. +- AS MEM - the address of both values will be taken and the entire object's memory footprint will be compared byte by byte. + Directly placing constant numbers like `456` as expected values will cause errors. +- AS_RAW - Unity assumes that you can compare the two values using `==` and `!=` and will do so. + No details are given about mismatches, because it doesn't really know what type it's dealing with. +- AS_NONE - Unity will disallow the use of these shorthand macros altogether, insisting that developers choose a more descriptive option. + +#### `UNITY_SUPPORT_VARIADIC_MACROS` + +This will force Unity to support variadic macros when using its own built-in RUN_TEST macro. +This will rarely be necessary. Most often, Unity will automatically detect if the compiler supports variadic macros by checking to see if it's C99+ compatible. +In the event that the compiler supports variadic macros, but is primarily C89 (ANSI), defining this option will allow you to use them. +This option is also not necessary when using Ceedling or the test runner generator script. + +## Getting Into The Guts + +There will be cases where the options above aren't quite going to get everything perfect. +They are likely sufficient for any situation where you are compiling and executing your tests with a native toolchain (e.g. clang on Mac). +These options may even get you through the majority of cases encountered in working with a target simulator run from your local command line. +But especially if you must run your test suite on your target hardware, your Unity configuration will +require special help. +This special help will usually reside in one of two places: the `main()` function or the `RUN_TEST` macro. +Let's look at how these work. + +### `main()` + +Each test module is compiled and run on its own, separate from the other test files in your project. +Each test file, therefore, has a `main` function. +This `main` function will need to contain whatever code is necessary to initialize your system to a workable state. +This is particularly true for situations where you must set up a memory map or initialize a communication channel for the output of your test results. + +A simple main function looks something like this: + +```C +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_TheFirst); + RUN_TEST(test_TheSecond); + RUN_TEST(test_TheThird); + return UNITY_END(); +} +``` + +You can see that our main function doesn't bother taking any arguments. +For our most barebones case, we'll never have arguments because we just run all the tests each time. +Instead, we start by calling `UNITY_BEGIN`. +We run each test (in whatever order we wish). +Finally, we call `UNITY_END`, returning its return value (which is the total number of failures). + +It should be easy to see that you can add code before any test cases are run or after all the test cases have completed. +This allows you to do any needed system-wide setup or teardown that might be required for your special circumstances. + +#### `RUN_TEST` + +The `RUN_TEST` macro is called with each test case function. +Its job is to perform whatever setup and teardown is necessary for executing a single test case function. +This includes catching failures, calling the test module's `setUp()` and `tearDown()` functions, and calling `UnityConcludeTest()`. +If using CMock or test coverage, there will be additional stubs in use here. +A simple minimalist RUN_TEST macro looks something like this: + +```C +#define RUN_TEST(testfunc) \ + UNITY_NEW_TEST(#testfunc) \ + if (TEST_PROTECT()) { \ + setUp(); \ + testfunc(); \ + } \ + if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \ + tearDown(); \ + UnityConcludeTest(); +``` + +So that's quite a macro, huh? +It gives you a glimpse of what kind of stuff Unity has to deal with for every single test case. +For each test case, we declare that it is a new test. +Then we run `setUp` and our test function. +These are run within a `TEST_PROTECT` block, the function of which is to handle failures that occur during the test. +Then, assuming our test is still running and hasn't been ignored, we run `tearDown`. +No matter what, our last step is to conclude this test before moving on to the next. + +Let's say you need to add a call to `fsync` to force all of your output data to flush to a file after each test. +You could easily insert this after your `UnityConcludeTest` call. +Maybe you want to write an xml tag before and after each result set. +Again, you could do this by adding lines to this macro. +Updates to this macro are for the occasions when you need an action before or after every single test case throughout your entire suite of tests. + +## Happy Porting + +The defines and macros in this guide should help you port Unity to just about any C target we can imagine. +If you run into a snag or two, don't be afraid of asking for help on the forums. +We love a good challenge! + +*Find The Latest of This And More at [ThrowTheSwitch.org][]* + +[ThrowTheSwitch.org]: https://throwtheswitch.org diff --git a/CAN_App/vendor/ceedling/docs/UnityGettingStartedGuide.md b/CAN_App/vendor/ceedling/docs/UnityGettingStartedGuide.md new file mode 100644 index 0000000..b951c60 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/UnityGettingStartedGuide.md @@ -0,0 +1,242 @@ +# Unity - Getting Started + +## Welcome + +Congratulations. +You're now the proud owner of your very own pile of bits! +What are you going to do with all these ones and zeros? +This document should be able to help you decide just that. + +Unity is a unit test framework. +The goal has been to keep it small and functional. +The core Unity test framework is three files: a single C file and a couple header files. +These team up to provide functions and macros to make testing easier. + +Unity was designed to be cross-platform. +It works hard to stick with C standards while still providing support for the many embedded C compilers that bend the rules. +Unity has been used with many compilers, including GCC, IAR, Clang, Green Hills, Microchip, and MS Visual Studio. +It's not much work to get it to work with a new target. + +### Overview of the Documents + +#### Unity Assertions reference + +This document will guide you through all the assertion options provided by Unity. +This is going to be your unit testing bread and butter. +You'll spend more time with assertions than any other part of Unity. + +#### Unity Assertions Cheat Sheet + +This document contains an abridged summary of the assertions described in the previous document. +It's perfect for printing and referencing while you familiarize yourself with Unity's options. + +#### Unity Configuration Guide + +This document is the one to reference when you are going to use Unity with a new target or compiler. +It'll guide you through the configuration options and will help you customize your testing experience to meet your needs. + +#### Unity Helper Scripts + +This document describes the helper scripts that are available for simplifying your testing workflow. +It describes the collection of optional Ruby scripts included in the auto directory of your Unity installation. +Neither Ruby nor these scripts are necessary for using Unity. +They are provided as a convenience for those who wish to use them. + +#### Unity License + +What's an open source project without a license file? +This brief document describes the terms you're agreeing to when you use this software. +Basically, we want it to be useful to you in whatever context you want to use it, but please don't blame us if you run into problems. + +### Overview of the Folders + +If you have obtained Unity through Github or something similar, you might be surprised by just how much stuff you suddenly have staring you in the face. +Don't worry, Unity itself is very small. +The rest of it is just there to make your life easier. +You can ignore it or use it at your convenience. +Here's an overview of everything in the project. + +- `src` - This is the code you care about! This folder contains a C file and two header files. + These three files _are_ Unity. +- `docs` - You're reading this document, so it's possible you have found your way into this folder already. + This is where all the handy documentation can be found. +- `examples` - This contains a few examples of using Unity. +- `extras` - These are optional add ons to Unity that are not part of the core project. + If you've reached us through James Grenning's book, you're going to want to look here. +- `test` - This is how Unity and its scripts are all tested. + If you're just using Unity, you'll likely never need to go in here. + If you are the lucky team member who gets to port Unity to a new toolchain, this is a good place to verify everything is configured properly. +- `auto` - Here you will find helpful Ruby scripts for simplifying your test workflow. + They are purely optional and are not required to make use of Unity. + +## How to Create A Test File + +Test files are C files. +Most often you will create a single test file for each C module that you want to test. +The test file should include unity.h and the header for your C module to be tested. + +Next, a test file will include a `setUp()` and `tearDown()` function. +The setUp function can contain anything you would like to run before each test. +The tearDown function can contain anything you would like to run after each test. +Both functions accept no arguments and return nothing. +You may leave either or both of these blank if you have no need for them. + +If you're using Ceedling or the test runner generator script, you may leave these off completely. +Not sure? +Give it a try. +If your compiler complains that it can't find setUp or tearDown when it links, you'll know you need to at least include an empty function for these. + +The majority of the file will be a series of test functions. +Test functions follow the convention of starting with the word "test_" or "spec_". +You don't HAVE to name them this way, but it makes it clear what functions are tests for other developers. +Also, the automated scripts that come with Unity or Ceedling will default to looking for test functions to be prefixed this way. +Test functions take no arguments and return nothing. All test accounting is handled internally in Unity. + +Finally, at the bottom of your test file, you will write a `main()` function. +This function will call `UNITY_BEGIN()`, then `RUN_TEST` for each test, and finally `UNITY_END()`. +This is what will actually trigger each of those test functions to run, so it is important that each function gets its own `RUN_TEST` call. + +Remembering to add each test to the main function can get to be tedious. +If you enjoy using helper scripts in your build process, you might consider making use of our handy [generate_test_runner.rb][] script. +This will create the main function and all the calls for you, assuming that you have followed the suggested naming conventions. +In this case, there is no need for you to include the main function in your test file at all. + +When you're done, your test file will look something like this: + +```C +#include "unity.h" +#include "file_to_test.h" + +void setUp(void) { + // set stuff up here +} + +void tearDown(void) { + // clean stuff up here +} + +void test_function_should_doBlahAndBlah(void) { + //test stuff +} + +void test_function_should_doAlsoDoBlah(void) { + //more test stuff +} + +// not needed when using generate_test_runner.rb +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_function_should_doBlahAndBlah); + RUN_TEST(test_function_should_doAlsoDoBlah); + return UNITY_END(); +} +``` + +It's possible that you will need more customization than this, eventually. +For that sort of thing, you're going to want to look at the configuration guide. +This should be enough to get you going, though. + +### Running Test Functions + +When writing your own `main()` functions, for a test-runner. +There are two ways to execute the test. + +The classic variant + +``` c +RUN_TEST(func, linenum) +``` + +Or its simpler replacement that starts at the beginning of the function. + +``` c +RUN_TEST(func) +``` + +These macros perform the necessary setup before the test is called and handles clean-up and result tabulation afterwards. + +### Ignoring Test Functions + +There are times when a test is incomplete or not valid for some reason. +At these times, TEST_IGNORE can be called. +Control will immediately be returned to the caller of the test, and no failures will be returned. +This is useful when your test runners are automatically generated. + +``` c +TEST_IGNORE() +``` + +Ignore this test and return immediately + +```c +TEST_IGNORE_MESSAGE (message) +``` + +Ignore this test and return immediately. +Output a message stating why the test was ignored. + +### Aborting Tests + +There are times when a test will contain an infinite loop on error conditions, or there may be reason to escape from the test early without executing the rest of the test. +A pair of macros support this functionality in Unity. +The first `TEST_PROTECT` sets up the feature, and handles emergency abort cases. +`TEST_ABORT` can then be used at any time within the tests to return to the last `TEST_PROTECT` call. + +```c + TEST_PROTECT() +``` + +Setup and Catch macro + +```c + TEST_ABORT() +``` + +Abort Test macro + +Example: + +```c + main() + { + if (TEST_PROTECT()) + { + MyTest(); + } + } +``` + +If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero. + +## How to Build and Run A Test File + +This is the single biggest challenge to picking up a new unit testing framework, at least in a language like C or C++. +These languages are REALLY good at getting you "close to the metal" (why is the phrase metal? Wouldn't it be more accurate to say "close to the silicon"?). +While this feature is usually a good thing, it can make testing more challenging. + +You have two really good options for toolchains. +Depending on where you're coming from, it might surprise you that neither of these options is running the unit tests on your hardware. +There are many reasons for this, but here's a short version: + +- On hardware, you have too many constraints (processing power, memory, etc), +- On hardware, you don't have complete control over all registers, +- On hardware, unit testing is more challenging, +- Unit testing isn't System testing. Keep them separate. + +Instead of running your tests on your actual hardware, most developers choose to develop them as native applications (using gcc or MSVC for example) or as applications running on a simulator. +Either is a good option. +Native apps have the advantages of being faster and easier to set up. +Simulator apps have the advantage of working with the same compiler as your target application. +The options for configuring these are discussed in the configuration guide. + +To get either to work, you might need to make a few changes to the file containing your register set (discussed later). + +In either case, a test is built by linking unity, the test file, and the C file(s) being tested. +These files create an executable which can be run as the test set for that module. +Then, this process is repeated for the next test file. +This flexibility of separating tests into individual executables allows us to much more thoroughly unit test our system and it keeps all the test code out of our final release! + +*Find The Latest of This And More at [ThrowTheSwitch.org][]* + +[generate_test_runner.rb]: ../auto/generate_test_runner.rb +[ThrowTheSwitch.org]: https://throwtheswitch.org diff --git a/CAN_App/vendor/ceedling/docs/UnityHelperScriptsGuide.md b/CAN_App/vendor/ceedling/docs/UnityHelperScriptsGuide.md new file mode 100644 index 0000000..ecbf55e --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/UnityHelperScriptsGuide.md @@ -0,0 +1,245 @@ +# Unity Helper Scripts + +## With a Little Help From Our Friends + +Sometimes what it takes to be a really efficient C programmer is a little non-C. +The Unity project includes a couple of Ruby scripts for making your life just a tad easier. +They are completely optional. +If you choose to use them, you'll need a copy of Ruby, of course. +Just install whatever the latest version is, and it is likely to work. You can find Ruby at [ruby-lang.org][]. + +### `generate_test_runner.rb` + +Are you tired of creating your own `main` function in your test file? +Do you keep forgetting to add a `RUN_TEST` call when you add a new test case to your suite? +Do you want to use CMock or other fancy add-ons but don't want to figure out how to create your own `RUN_TEST` macro? + +Well then we have the perfect script for you! + +The `generate_test_runner` script processes a given test file and automatically creates a separate test runner file that includes ?main?to execute the test cases within the scanned test file. +All you do then is add the generated runner to your list of files to be compiled and linked, and presto you're done! + +This script searches your test file for void function signatures having a function name beginning with "test" or "spec". +It treats each of these functions as a test case and builds up a test suite of them. +For example, the following includes three test cases: + +```C +void testVerifyThatUnityIsAwesomeAndWillMakeYourLifeEasier(void) +{ + ASSERT_TRUE(1); +} +void test_FunctionName_should_WorkProperlyAndReturn8(void) { + ASSERT_EQUAL_INT(8, FunctionName()); +} +void spec_Function_should_DoWhatItIsSupposedToDo(void) { + ASSERT_NOT_NULL(Function(5)); +} +``` + +You can run this script a couple of ways. +The first is from the command line: + +```Shell +ruby generate_test_runner.rb TestFile.c NameOfRunner.c +``` + +Alternatively, if you include only the test file parameter, the script will copy the name of the test file and automatically append `_Runner` to the name of the generated file. +The example immediately below will create TestFile_Runner.c. + +```Shell +ruby generate_test_runner.rb TestFile.c +``` + +You can also add a [YAML][] file to configure extra options. +Conveniently, this YAML file is of the same format as that used by Unity and CMock. +So if you are using YAML files already, you can simply pass the very same file into the generator script. + +```Shell +ruby generate_test_runner.rb TestFile.c my_config.yml +``` + +The contents of the YAML file `my_config.yml` could look something like the example below. +If you're wondering what some of these options do, you're going to love the next section of this document. + +```YAML +:unity: + :includes: + - stdio.h + - microdefs.h + :cexception: 1 + :suite_setup: "blah = malloc(1024);" + :suite_teardown: "free(blah);" +``` + +If you would like to force your generated test runner to include one or more header files, you can just include those at the command line too. +Just make sure these are _after_ the YAML file, if you are using one: + +```Shell +ruby generate_test_runner.rb TestFile.c my_config.yml extras.h +``` + +Another option, particularly if you are already using Ruby to orchestrate your builds - or more likely the Ruby-based build tool Rake - is requiring this script directly. +Anything that you would have specified in a YAML file can be passed to the script as part of a hash. +Let's push the exact same requirement set as we did above but this time through Ruby code directly: + +```Ruby +require "generate_test_runner.rb" +options = { + :includes => ["stdio.h", "microdefs.h"], + :cexception => 1, + :suite_setup => "blah = malloc(1024);", + :suite_teardown => "free(blah);" +} +UnityTestRunnerGenerator.new.run(testfile, runner_name, options) +``` + +If you have multiple files to generate in a build script (such as a Rakefile), you might want to instantiate a generator object with your options and call it to generate each runner afterwards. +Like thus: + +```Ruby +gen = UnityTestRunnerGenerator.new(options) +test_files.each do |f| + gen.run(f, File.basename(f,'.c')+"Runner.c" +end +``` + +#### Options accepted by generate_test_runner.rb + +The following options are available when executing `generate_test_runner`. +You may pass these as a Ruby hash directly or specify them in a YAML file, both of which are described above. +In the `examples` directory, Example 3's Rakefile demonstrates using a Ruby hash. + +##### `:includes` + +This option specifies an array of file names to be `#include`'d at the top of your runner C file. +You might use it to reference custom types or anything else universally needed in your generated runners. + +##### `:suite_setup` + +Define this option with C code to be executed _before any_ test cases are run. + +Alternatively, if your C compiler supports weak symbols, you can leave this option unset and instead provide a `void suiteSetUp(void)` function in your test suite. +The linker will look for this symbol and fall back to a Unity-provided stub if it is not found. + +##### `:suite_teardown` + +Define this option with C code to be executed _after all_ test cases have finished. +An integer variable `num_failures` is available for diagnostics. +The code should end with a `return` statement; the value returned will become the exit code of `main`. +You can normally just return `num_failures`. + +Alternatively, if your C compiler supports weak symbols, you can leave this option unset and instead provide a `int suiteTearDown(int num_failures)` function in your test suite. +The linker will look for this symbol and fall back to a Unity-provided stub if it is not found. + +##### `:enforce_strict_ordering` + +This option should be defined if you have the strict order feature enabled in CMock (see CMock documentation). +This generates extra variables required for everything to run smoothly. +If you provide the same YAML to the generator as used in CMock's configuration, you've already configured the generator properly. + +##### `:externc` + +This option should be defined if you are mixing C and CPP and want your test runners to automatically include extern "C" support when they are generated. + +##### `:mock_prefix` and `:mock_suffix` + +Unity automatically generates calls to Init, Verify and Destroy for every file included in the main test file that starts with the given mock prefix and ends with the given mock suffix, file extension not included. +By default, Unity assumes a `Mock` prefix and no suffix. + +##### `:plugins` + +This option specifies an array of plugins to be used (of course, the array can contain only a single plugin). +This is your opportunity to enable support for CException support, which will add a check for unhandled exceptions in each test, reporting a failure if one is detected. +To enable this feature using Ruby: + +```Ruby +:plugins => [ :cexception ] +``` + +Or as a yaml file: + +```YAML +:plugins: + -:cexception +``` + +If you are using CMock, it is very likely that you are already passing an array of plugins to CMock. +You can just use the same array here. +This script will just ignore the plugins that don't require additional support. + +##### `:include_extensions` + +This option specifies the pattern for matching acceptable header file extensions. +By default it will accept hpp, hh, H, and h files. +If you need a different combination of files to search, update this from the default `'(?:hpp|hh|H|h)'`. + +##### `:source_extensions` + +This option specifies the pattern for matching acceptable source file extensions. +By default it will accept cpp, cc, C, c, and ino files. +If you need a different combination of files to search, update this from the default `'(?:cpp|cc|ino|C|c)'`. + +### `unity_test_summary.rb` + +A Unity test file contains one or more test case functions. +Each test case can pass, fail, or be ignored. +Each test file is run individually producing results for its collection of test cases. +A given project will almost certainly be composed of multiple test files. +Therefore, the suite of tests is comprised of one or more test cases spread across one or more test files. +This script aggregates individual test file results to generate a summary of all executed test cases. +The output includes how many tests were run, how many were ignored, and how many failed. In addition, the output includes a listing of which specific tests were ignored and failed. +A good example of the breadth and details of these results can be found in the `examples` directory. +Intentionally ignored and failing tests in this project generate corresponding entries in the summary report. + +If you're interested in other (prettier?) output formats, check into the [Ceedling][] build tool project that works with Unity and CMock and supports xunit-style xml as well as other goodies. + +This script assumes the existence of files ending with the extensions `.testpass` and `.testfail`. +The contents of these files includes the test results summary corresponding to each test file executed with the extension set according to the presence or absence of failures for that test file. +The script searches a specified path for these files, opens each one it finds, parses the results, and aggregates and prints a summary. +Calling it from the command line looks like this: + +```Shell +ruby unity_test_summary.rb build/test/ +``` + +You can optionally specify a root path as well. +This is really helpful when you are using relative paths in your tools' setup, but you want to pull the summary into an IDE like Eclipse for clickable shortcuts. + +```Shell +ruby unity_test_summary.rb build/test/ ~/projects/myproject/ +``` + +Or, if you're more of a Windows sort of person: + +```Shell +ruby unity_test_summary.rb build\teat\ C:\projects\myproject\ +``` + +When configured correctly, you'll see a final summary, like so: + +```Shell +-------------------------- +UNITY IGNORED TEST SUMMARY +-------------------------- +blah.c:22:test_sandwiches_should_HaveBreadOnTwoSides:IGNORE + +------------------------- +UNITY FAILED TEST SUMMARY +------------------------- +blah.c:87:test_sandwiches_should_HaveCondiments:FAIL:Expected 1 was 0 +meh.c:38:test_soda_should_BeCalledPop:FAIL:Expected "pop" was "coke" + +-------------------------- +OVERALL UNITY TEST SUMMARY +-------------------------- +45 TOTAL TESTS 2 TOTAL FAILURES 1 IGNORED +``` + +How convenient is that? + +*Find The Latest of This And More at [ThrowTheSwitch.org][]* + +[ruby-lang.org]: https://ruby-labg.org/ +[YAML]: http://www.yaml.org/ +[Ceedling]: http://www.throwtheswitch.org/ceedling +[ThrowTheSwitch.org]: https://throwtheswitch.org diff --git a/CAN_App/vendor/ceedling/docs/plugin_beep.md b/CAN_App/vendor/ceedling/docs/plugin_beep.md new file mode 100644 index 0000000..e59d881 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_beep.md @@ -0,0 +1,22 @@ +ceedling-beep +============= + +This is a simple plugin that just beeps at the end of a build and/or test sequence. Are you getting too distracted surfing +the internet, chatting with coworkers, or swordfighting while it's building or testing? The friendly beep will let you know +it's time to pay attention again. + +This plugin has very few configuration options. At this time it can beep on completion of a task and/or on an error condition. +For each of these, you can configure the method that it should beep. + +``` +:tools: + :beep_on_done: :bell + :beep_on_error: :bell +``` + +Each of these have the following options: + + - :bell - this option uses the ASCII bell character out stdout + - :speaker_test - this uses the linux speaker-test command if installed + +Very likely, we'll be adding to this list if people find this to be useful. diff --git a/CAN_App/vendor/ceedling/docs/plugin_bullseye.md b/CAN_App/vendor/ceedling/docs/plugin_bullseye.md new file mode 100644 index 0000000..ab0b53b --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_bullseye.md @@ -0,0 +1,76 @@ +ceedling-bullseye +================= + +# Plugin Overview + +Plugin for integrating Bullseye code coverage tool into Ceedling projects. +This plugin requires a working license to Bullseye code coverage tools. The tools +must be within the path or the path should be added to the environment in the +`project.yml file`. + +## Configuration + +The bullseye plugin supports configuration options via your `project.yml` provided +by Ceedling. The following is a typical configuration example: + +``` +:bullseye: + :auto_license: TRUE +:plugins: + :bullseye_lib_path: [] +:paths: + :bullseye_toolchain_include: [] + +:tools: + :bullseye_instrumentation: + :executable: covc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - ${1} + :bullseye_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_BULLSEYE_TOOLCHAIN_INCLUDE + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR + - -DBULLSEYE_COMPILER + - -c "${1}" + - -o "${2}" + :bullseye_linker: + :executable: gcc + :arguments: + - ${1} + - -o ${2} + - -L$: PLUGINS_BULLSEYE_LIB_PATH + - -lcov + :bullseye_fixture: + :executable: ${1} + :bullseye_report_covsrc: + :executable: covsrc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - -w140 + :bullseye_report_covfn: + :executable: covfn + :stderr_redirect: :auto + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - --width 120 + - --no-source + - '"${1}"' + :bullseye_browser: + :executable: CoverageBrowser + :background_exec: :auto + :optional: TRUE + :arguments: + - '"$"': ENVIRONMENT_COVFILE +``` + +## Example Usage + +```sh +ceedling bullseye:all utils:bullseye +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_colour_report.md b/CAN_App/vendor/ceedling/docs/plugin_colour_report.md new file mode 100644 index 0000000..4e0fcd4 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_colour_report.md @@ -0,0 +1,20 @@ +ceedling-colour-report +====================== + +## Overview + +The colour_report replaces the normal ceedling "pretty" output with +a colorized variant, in order to make the results easier to read from +a standard command line. This is very useful on developer machines, but +can occasionally cause problems with parsing on CI servers. + +## Setup + +Enable the plugin in your project.yml by adding `colour_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - colour_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_command_hooks.md b/CAN_App/vendor/ceedling/docs/plugin_command_hooks.md new file mode 100644 index 0000000..8ac64af --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_command_hooks.md @@ -0,0 +1,53 @@ +ceedling-command-hooks +====================== + +Plugin for easily calling command line tools at various points in the build process + +Define any of these sections in :tools: to provide additional hooks to be called on demand: + +``` + :pre_mock_generate + :post_mock_generate + :pre_runner_generate + :post_runner_generate + :pre_compile_execute + :post_compile_execute + :pre_link_execute + :post_link_execute + :pre_test_fixture_execute + :pre_test + :post_test + :pre_release + :post_release + :pre_build + :post_build +``` + +Each of these tools can support an :executable string and an :arguments list, like so: + +``` +:tools: + :post_link_execute: + :executable: objcopy.exe + :arguments: + - ${1} #This is replaced with the executable name + - output.srec + - --strip-all +``` + +You may also specify an array of executables to be called in a particular place, like so: + +``` +:tools: + :post_test: + - :executable: echo + :arguments: "${1} was glorious!" + - :executable: echo + :arguments: + - it kinda made me cry a little. + - you? +``` + +Please note that it varies which arguments are being parsed down to the +hooks. For now see `command_hooks.rb` to figure out which suits you best. +Happy Tweaking! diff --git a/CAN_App/vendor/ceedling/docs/plugin_compile_commands_json.md b/CAN_App/vendor/ceedling/docs/plugin_compile_commands_json.md new file mode 100644 index 0000000..ea80b73 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_compile_commands_json.md @@ -0,0 +1,29 @@ +compile_commands_json +===================== + +## Overview + +Syntax highlighting and code completion are hard. Historically each editor or IDE has implemented their own and then competed amongst themselves to offer the best experience for developers. Often developers would still to an IDE that felt cumbersome and slow just because it had the best syntax highlighting on the market. If doing it for one language is hard (and it is) imagine doing it for dozens of them. Imagine a full stack developer who has to work with CSS, HTML, JavaScript and some Ruby - they need excellent support in all those languages which just made things even harder. + +In June of 2016, Microsoft with Red Hat and Codenvy got together to create a standard called the Language Server Protocol (LSP). The idea was simple, by standardising on one protocol, all the IDEs and editors out there would only have to support LSP, and not have custom plugins for each language. In turn, the backend code that actually does the highlighting can be written once and used by any IDE that supports LSP. Many editors already support it such as Sublime Text, vim and emacs. This means that if you're using a crufty old IDE or worse, you're using a shiny new editor without code completion, then this could be just the upgrade you're looking for! + +For C and C++ projects, many people use the `clangd` backend. So that it can do things like "go to definition", `clangd` needs to know how to build the project so that it can figure out all the pieces to the puzzle. There are manual tools such as `bear` which can be run with `gcc` or `clang` to extract this information it has a big limitation in that if run with `ceedling release` you won't get any auto completion for Unity and you'll also get error messages reported by your IDE because of what it perceives as missing headers. If you do the same with `ceedling test` now you get Unity but you might miss things that are only seen in the release build. + +This plugin resolves that issue. As it is run by Ceedling, it has access to all the build information it needs to create the perfect `compile_commands.json`. Once enabled, this plugin will generate that file and place it in `./build/artifacts/compile_commands.json`. `clangd` will search your project for this file, but it is easier to symlink it into the root directory (for example `ln -s ./build/artifacts/compile_commands.json`. + +For more information on LSP and to find out if your editor supports it, check out https://langserver.org/ + +## Setup + +Enable the plugin in your project.yml by adding `compile_commands_json` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - compile_commands_json +``` + +## Configuration + +There is no additional configuration necessary to run this plugin. diff --git a/CAN_App/vendor/ceedling/docs/plugin_dependencies.md b/CAN_App/vendor/ceedling/docs/plugin_dependencies.md new file mode 100644 index 0000000..256467d --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_dependencies.md @@ -0,0 +1,254 @@ +ceedling-dependencies +===================== + +Plugin for supporting release dependencies. It's rare for an embedded project to +be built completely free of other libraries and modules. Some of these may be +standard internal libraries. Some of these may be 3rd party libraries. In either +case, they become part of the project's ecosystem. + +This plugin is intended to make that relationship easier. It allows you to specify +a source for dependencies. If required, it will automatically grab the appropriate +version of that dependency. + +Most 3rd party libraries have a method of building already in place. While we'd +love to convert the world to a place where everything downloads with a test suite +in Ceedling, that's not likely to happen anytime soon. Until then, this plugin +will allow the developer to specify what calls Ceedling should make to oversee +the build process of those third party utilities. Are they using Make? CMake? A +custom series of scripts that only a mad scientist could possibly understand? No +matter. Ceedling has you covered. Just specify what should be called, and Ceedling +will make it happen whenever it notices that the output artifacts are missing. + +Output artifacts? Sure! Things like static and dynamic libraries, or folders +containing header files that might want to be included by your release project. + +So how does all this magic work? + +First, you need to add the `:dependencies` plugin to your list. Then, we'll add a new +section called :dependencies. There, you can list as many dependencies as you desire. Each +has a series of fields which help Ceedling to understand your needs. Many of them are +optional. If you don't need that feature, just don't include it! In the end, it'll look +something like this: + +``` +:dependencies: + :libraries: + - :name: WolfSSL + :source_path: third_party/wolfssl/source + :build_path: third_party/wolfssl/build + :artifact_path: third_party/wolfssl/install + :fetch: + :method: :zip + :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip + :environment: + - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE + :build: + - "autoreconf -i" + - "./configure --enable-tls13 --enable-singlethreaded" + - make + - make install + :artifacts: + :static_libraries: + - lib/wolfssl.a + :dynamic_libraries: + - lib/wolfssl.so + :includes: + - include/** +``` + +Let's take a deeper look at each of these features. + +The Starting Dash & Name +------------------------ + +Yes, that opening dash tells the dependencies plugin that the rest of these fields +belong to our first dependency. If we had a second dependency, we'd have another +dash, lined up with the first, and followed by all the fields indented again. + +By convention, we use the `:name` field as the first field for each tool. Ceedling +honestly doesn't care which order the fields are given... but as humans, it makes +it easier for us to see the name of each dependency with starting dash. + +The name field is only used to print progress while we're running Ceedling. You may +call the name of the field whatever you wish. + +Working Folders +--------------- + +The `:source_path` field allows us to specify where the source code for each of our +dependencies is stored. If fetching the dependency from elsewhere, it will be fetched +to this location. All commands to build this dependency will be executed from +this location (override this by specifying a `:build_path`). Finally, the output +artifacts will be referenced to this location (override this by specifying a `:artifact_path`) + +If unspecified, the `:source_path` will be `dependencies\dep_name` where `dep_name` +is the name specified in `:name` above (with special characters removed). It's best, +though, if you specify exactly where you want your dependencies to live. + +If the dependency is directly included in your project (you've specified `:none` as the +`:method` for fetching), then `:source_path` should be where your Ceedling can find the +source for your dependency in you repo. + +All artifacts are relative to the `:artifact_path` (which defaults to be the same as +`:source_path`) + +Fetching Dependencies +--------------------- + +The `:dependencies` plugin supports the ability to automatically fetch your dependencies +for you... using some common methods of fetching source. This section contains only a +couple of fields: + +- `:method` -- This is the method that this dependency is fetched. + - `:none` -- This tells Ceedling that the code is already included in the project. + - `:zip` -- This tells Ceedling that we want to unpack a zip file to our source path. + - `:git` -- This tells Ceedling that we want to clone a git repo to our source path. + - `:svn` -- This tells Ceedling that we want to checkout a subversion repo to our source path. + - `:custom` -- This tells Ceedling that we want to use a custom command or commands to fetch the code. +- `:source` -- This is the path or url to fetch code when using the zip or git method. +- `:tag`/`:branch` -- This is the specific tag or branch that you wish to retrieve (git only. optional). +- `:hash` -- This is the specific SHA1 hash you want to fetch (git only. optional, requires a deep clone). +- `:revision` -- This is the specific revision you want to fetch (svn only. optional). +- `:executable` -- This is a list of commands to execute when using the `:custom` method + + +Environment Variables +--------------------- + +Many build systems support customization through environment variables. By specifying +an array of environment variables, Ceedling will customize the shell environment before +calling the build process. + +Environment variables may be specified in three ways. Let's look at one of each: + +``` + :environment: + - ARCHITECTURE=ARM9 + - CFLAGS+=-DADD_AWESOMENESS + - CFLAGS-=-DWASTE +``` + +In the first example, you see the most straightforward method. The environment variable +`ARCHITECTURE` is set to the value `ARM9`. That's it. Simple. + +The next two options modify an existing symbol. In the first one, we use `+=`, which tells +Ceedling to add the define `ADD_AWESOMENESS` to the environment variable `CFLAGS`. The second +tells Ceedling to remove the define `WASTE` from the same environment variable. + +There are a couple of things to note here. + +First, when adding to a variable, Ceedling has no way of knowing +what delimiter you are expecting. In this example you can see we manually added some whitespace. +If we had been modifying `PATH` instead, we might have had to use a `:` on a unux or `;` on +Windows. + +Second, removing an argument will have no effect on the argument if that argument isn't found +precisely. It's case sensitive and the entire string must match. If symbol doesn't already exist, +it WILL after executing this command... however it will be assigned to nothing. + +Building Dependencies +--------------------- + +The heart of the `:dependencies` plugin is the ability for you, the developer, to specify the +build process for each of your dependencies. You will need to have any required tools installed +before using this feature. + +The steps are specified as an array of strings. Ceedling will execute those steps in the order +specified, moving from step to step unless an error is encountered. By the end of the process, +the artifacts should have been created by your process... otherwise an error will be produced. + +Artifacts +--------- + +These are the outputs of the build process. There are there types of artifacts. Any dependency +may have none or some of these. Calling out these files tells Ceedling that they are important. +Your dependency's build process may produce many other files... but these are the files that +Ceedling understands it needs to act on. + +### `static_libraries` + +Specifying one or more static libraries will tell Ceedling where it should find static libraries +output by your build process. These libraries are automatically added to the list of dependencies +and will be linked with the rest of your code to produce the final release. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `dynamic_libraries` + +Specifying one or more dynamic libraries will tell Ceedling where it should find dynamic libraries +output by your build process. These libraries are automatically copied to the same folder as your +final release binary. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `includes` + +Often when libraries are built, the same process will output a collection of includes so that +your release code knows how to interact with that library. It's the public API for that library. +By specifying the directories that will contain these includes (don't specify the files themselves, +Ceedling only needs the directories), Ceedling is able to automatically add these to its internal +include list. This allows these files to be used while building your release code, as well we making +them mockable during unit testing. + +### `source` + +It's possible that your external dependency will just produce additional C files as its output. +In this case, Ceedling is able to automatically add these to its internal source list. This allows +these files to be used while building your release code. + +Tasks +----- + +Once configured correctly, the `:dependencies` plugin should integrate seamlessly into your +workflow and you shouldn't have to think about it. In the real world, that doesn't always happen. +Here are a number of tasks that are added or modified by this plugin. + +### `ceedling dependencies:clean` + +This can be issued in order to completely remove the dependency from its source path. On the +next build, it will be refetched and rebuilt from scratch. This can also apply to a particular +dependency. For example, by specifying `dependencies:clean:DepName`. + +### `ceedling dependencies:fetch` + +This can be issued in order to fetch each dependency from its origin. This will have no effect on +dependencies that don't have fetch instructions specified. This can also apply to a particular +dependency. For example, by specifying `dependencies:fetch:DepName`. + +### `ceedling dependencies:make` + +This will force the dependencies to all build. This should happen automatically when a release +has been triggered... but if you're just getting your dependency configured at this moment, you +may want to just use this feature instead. A single dependency can also be built by specifying its +name, like `dependencies:make:MyTunaBoat`. + +### `ceedling dependencies:deploy` + +This will force any dynamic libraries produced by your dependencies to be copied to your release +build directory... just in case you clobbered them. + +### `paths:include` + +Maybe you want to verify that all the include paths are correct. If you query Ceedling with this +request, it will list all the header file paths that it's found, including those produced by +dependencies. + +### `files:include` + +Maybe you want to take that query further and actually get a list of ALL the header files +Ceedling has found, including those belonging to your dependencies. + +Testing +======= + +Hopefully all your dependencies are fully tested... but we can't always depend on that. +In the event that they are tested with Ceedling, you'll probably want to consider using +the `:subprojects` plugin instead of this one. The purpose of this plugin is to pull in +third party code for release... and to provide a mockable interface for Ceedling to use +during its tests of other modules. + +If that's what you're after... you've found the right plugin! + +Happy Testing! diff --git a/CAN_App/vendor/ceedling/docs/plugin_fake_function_framework.md b/CAN_App/vendor/ceedling/docs/plugin_fake_function_framework.md new file mode 100644 index 0000000..8042775 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_fake_function_framework.md @@ -0,0 +1,250 @@ +# A Fake Function Framework Plug-in for Ceedling + +This is a plug-in for [Ceedling](https://github.com/ThrowTheSwitch/Ceedling) to use the [Fake Function Framework](https://github.com/meekrosoft/fff) for mocking instead of CMock. + +Using fff provides less strict mocking than CMock, and allows for more loosely-coupled tests. +And, when tests fail -- since you get the actual line number of the failure -- it's a lot easier to figure out what went wrong. + +## Installing the plug-in + +To use the plugin you need to 1) get the contents of this repo and 2) configure your project to use it. + +### Get the source + +The easiest way to get the source is to just clone this repo into the Ceedling plugin folder for your existing Ceedling project. +(Don't have a Ceedling project already? [Here are instructions to create one.](http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling)) +From within `/vendor/ceedling/plugins`, run: + +`git clone https://github.com/ElectronVector/fake_function_framework.git` + +This will create a new folder named `fake_function_framework` in the plugins folder. + +### Enable the plug-in. + +The plug-in is enabled from within your project.yml file. + +In the `:plugins` configuration, add `fake_function_framework` to the list of enabled plugins: + +```yaml +:plugins: + :load_paths: + - vendor/ceedling/plugins + :enabled: + - stdout_pretty_tests_report + - module_generator + - fake_function_framework +``` +*Note that you could put the plugin source in some other loaction. +In that case you'd need to add a new path the `:load_paths`.* + +## How to use it + +You use fff with Ceedling the same way you used to use CMock. +Modules can still be generated with the default module generator: `rake module:create[my_module]`. +If you want to "mock" `some_module.h` in your tests, just `#include "mock_some_module.h"`. +This creates a fake function for each of the functions defined in `some_module.h`. + +The name of each fake is the original function name with an appended `_fake`. +For example, if we're generating fakes for a stack module with `push` and `pop` functions, we would have the fakes `push_fake` and `pop_fake`. +These fakes are linked into our test executable so that any time our unit under test calls `push` or `pop` our fakes are called instead. + +Each of these fakes is actually a structure containing information about how the function was called, and what it might return. +We can use Unity to inspect these fakes in our tests, and verify the interactions of our units. +There is also a global structure named `fff` which we can use to check the sequence of calls. + +The fakes can also be configured to return particular values, so you can exercise the unit under test however you want. + +The examples below explain how to use fff to test a variety of module interactions. +Each example uses fakes for a "display" module, created from a display.h file with `#include "mock_display.h"`. The `display.h` file must exist and must contain the prototypes for the functions to be faked. + +### Test that a function was called once + +```c +void +test_whenTheDeviceIsReset_thenTheStatusLedIsTurnedOff() +{ + // When + event_deviceReset(); + + // Then + TEST_ASSERT_EQUAL(1, display_turnOffStatusLed_fake.call_count); +} +``` + +### Test that a function was NOT called + +```c +void +test_whenThePowerReadingIsLessThan5_thenTheStatusLedIsNotTurnedOn(void) +{ + // When + event_powerReadingUpdate(4); + + // Then + TEST_ASSERT_EQUAL(0, display_turnOnStatusLed_fake.call_count); +} +``` + +## Test that a single function was called with the correct argument + +```c +void +test_whenTheVolumeKnobIsMaxed_thenVolumeDisplayIsSetTo11(void) +{ + // When + event_volumeKnobMaxed(); + + // Then + TEST_ASSERT_EQUAL(1, display_setVolume_fake.call_count); + TEST_ASSERT_EQUAL(11, display_setVolume_fake.arg0_val); +} +``` + +## Test that calls are made in a particular sequence + +```c +void +test_whenTheModeSelectButtonIsPressed_thenTheDisplayModeIsCycled(void) +{ + // When + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + + // Then + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToMinimum, fff.call_history[0]); + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToMaximum, fff.call_history[1]); + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToAverage, fff.call_history[2]); +} +``` + +## Fake a return value from a function + +```c +void +test_givenTheDisplayHasAnError_whenTheDeviceIsPoweredOn_thenTheDisplayIsPoweredDown(void) +{ + // Given + display_isError_fake.return_val = true; + + // When + event_devicePoweredOn(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); +} +``` + +## Fake a function with a value returned by reference + +```c +void +test_givenTheUserHasTypedSleep_whenItIsTimeToCheckTheKeyboard_theDisplayIsPoweredDown(void) +{ + // Given + char mockedEntry[] = "sleep"; + void return_mock_value(char * entry, int length) + { + if (length > strlen(mockedEntry)) + { + strncpy(entry, mockedEntry, length); + } + } + display_getKeyboardEntry_fake.custom_fake = return_mock_value; + + // When + event_keyboardCheckTimerExpired(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); +} +``` + +## Fake a function with a function pointer parameter + +``` +void +test_givenNewDataIsAvailable_whenTheDisplayHasUpdated_thenTheEventIsComplete(void) +{ + // A mock function for capturing the callback handler function pointer. + void(*registeredCallback)(void) = 0; + void mock_display_updateData(int data, void(*callback)(void)) + { + //Save the callback function. + registeredCallback = callback; + } + display_updateData_fake.custom_fake = mock_display_updateData; + + // Given + event_newDataAvailable(10); + + // When + if (registeredCallback != 0) + { + registeredCallback(); + } + + // Then + TEST_ASSERT_EQUAL(true, eventProcessor_isLastEventComplete()); +} +``` + +## Helper macros + +For convenience, there are also some helper macros that create new Unity-style asserts: + +- `TEST_ASSERT_CALLED(function)`: Asserts that a function was called once. +- `TEST_ASSERT_NOT_CALLED(function)`: Asserts that a function was never called. +- `TEST_ASSERT_CALLED_TIMES(times, function)`: Asserts that a function was called a particular number of times. +- `TEST_ASSERT_CALLED_IN_ORDER(order, function)`: Asserts that a function was called in a particular order. + +Here's how you might use one of these instead of simply checking the call_count value: + +```c +void +test_whenTheDeviceIsReset_thenTheStatusLedIsTurnedOff() +{ + // When + event_deviceReset(); + + // Then + // This how to directly use fff... + TEST_ASSERT_EQUAL(1, display_turnOffStatusLed_fake.call_count); + // ...and this is how to use the helper macro. + TEST_ASSERT_CALLED(display_turnOffStatusLed); +} +``` + +## Test setup + +All of the fake functions, and any fff global state are all reset automatically between each test. + +## CMock configuration + +Use still use some of the CMock configuration options for setting things like the mock prefix, and for including additional header files in the mock files. + +```yaml +:cmock: + :mock_prefix: mock_ + :includes: + - + :includes_h_pre_orig_header: + - + :includes_h_post_orig_header: + - + :includes_c_pre_header: + - + :includes_c_post_header: +``` + +## Running the tests + +There are unit and integration tests for the plug-in itself. +These are run with the default `rake` task. +The integration test runs the tests for the example project in examples/fff_example. +For the integration tests to succeed, this repository must be placed in a Ceedling tree in the plugins folder. + +## More examples + +There is an example project in examples/fff_example. +It shows how to use the plug-in with some full-size examples. diff --git a/CAN_App/vendor/ceedling/docs/plugin_gcov.md b/CAN_App/vendor/ceedling/docs/plugin_gcov.md new file mode 100644 index 0000000..b144e3b --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_gcov.md @@ -0,0 +1,433 @@ +ceedling-gcov +============= + +# Plugin Overview + +Plugin for integrating GNU GCov code coverage tool into Ceedling projects. +Currently only designed for the gcov command (like LCOV for example). In the +future we could configure this to work with other code coverage tools. + +This plugin currently uses [gcovr](https://www.gcovr.com/) and / or +[ReportGenerator](https://danielpalme.github.io/ReportGenerator/) +as utilities to generate HTML, XML, JSON, or Text reports. The normal gcov +plugin _must_ be run first for these reports to generate. + +## Installation + +gcovr can be installed via pip like so: + +```sh +pip install gcovr +``` + +ReportGenerator can be installed via .NET Core like so: + +```sh +dotnet tool install -g dotnet-reportgenerator-globaltool +``` + +It is not required to install both `gcovr` and `ReportGenerator`. Either utility +may be installed to create reports. + +## Configuration + +The gcov plugin supports configuration options via your `project.yml` provided +by Ceedling. + +### Utilities + +Gcovr and / or ReportGenerator may be enabled to create coverage reports. + +```yaml +:gcov: + :utilities: + - gcovr # Use gcovr to create the specified reports (default). + - ReportGenerator # Use ReportGenerator to create the specified reports. +``` + +### Reports + +Various reports are available and may be enabled with the following +configuration item. See the specific report sections in this README +for additional options and information. All generated reports will be found in `build/artifacts/gcov`. + +```yaml +:gcov: + # Specify one or more reports to generate. + # Defaults to HtmlBasic. + :reports: + # Make an HTML summary report. + # Supported utilities: gcovr, ReportGenerator + - HtmlBasic + + # Make an HTML report with line by line coverage of each source file. + # Supported utilities: gcovr, ReportGenerator + - HtmlDetailed + + # Make a Text report, which may be output to the console with gcovr or a file in both gcovr and ReportGenerator. + # Supported utilities: gcovr, ReportGenerator + - Text + + # Make a Cobertura XML report. + # Supported utilities: gcovr, ReportGenerator + - Cobertura + + # Make a SonarQube XML report. + # Supported utilities: gcovr, ReportGenerator + - SonarQube + + # Make a JSON report. + # Supported utilities: gcovr + - JSON + + # Make a detailed HTML report with CSS and JavaScript included in every HTML page. Useful for build servers. + # Supported utilities: ReportGenerator + - HtmlInline + + # Make a detailed HTML report with a light theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzure + + # Make a detailed HTML report with a dark theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzureDark + + # Make a single HTML file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - HtmlChart + + # Make a detailed HTML report in a single file. + # Supported utilities: ReportGenerator + - MHtml + + # Make SVG and PNG files that show line and / or branch coverage information. + # Supported utilities: ReportGenerator + - Badges + + # Make a single CSV file containing coverage information per file. + # Supported utilities: ReportGenerator + - CsvSummary + + # Make a single TEX file containing a summary for all files and detailed reports for each files. + # Supported utilities: ReportGenerator + - Latex + + # Make a single TEX file containing a summary for all files. + # Supported utilities: ReportGenerator + - LatexSummary + + # Make a single PNG file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - PngChart + + # Command line output interpreted by TeamCity. + # Supported utilities: ReportGenerator + - TeamCitySummary + + # Make a text file in lcov format. + # Supported utilities: ReportGenerator + - lcov + + # Make a XML file containing a summary for all classes and detailed reports for each class. + # Supported utilities: ReportGenerator + - Xml + + # Make a single XML file containing a summary for all files. + # Supported utilities: ReportGenerator + - XmlSummary +``` + +### Gcovr HTML Reports + +Generation of Gcovr HTML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable HTML reports or set to 'false' to disable. + # Defaults to enabled. (gcovr --html) + # Deprecated - See the :reports: configuration option. + :html_report: [true|false] + + # Gcovr supports generating two types of HTML reports. Use 'basic' to create + # an HTML report with only the overall file information. Use 'detailed' to create + # an HTML report with line by line coverage of each source file. + # Defaults to 'basic'. Set to 'detailed' for (gcovr --html-details). + # Deprecated - See the :reports: configuration option. + :html_report_type: [basic|detailed] + + + :gcovr: + # HTML report filename. + :html_artifact_filename: + + # Use 'title' as title for the HTML report. + # Default is 'Head'. (gcovr --html-title) + :html_title: + + # If the coverage is below MEDIUM, the value is marked as low coverage in the HTML report. + # MEDIUM has to be lower than or equal to value of html_high_threshold. + # If MEDIUM is equal to value of html_high_threshold the report has only high and low coverage. + # Default is 75.0. (gcovr --html-medium-threshold) + :html_medium_threshold: 75 + + # If the coverage is below HIGH, the value is marked as medium coverage in the HTML report. + # HIGH has to be greater than or equal to value of html_medium_threshold. + # If HIGH is equal to value of html_medium_threshold the report has only high and low coverage. + # Default is 90.0. (gcovr -html-high-threshold) + :html_high_threshold: 90 + + # Set to 'true' to use absolute paths to link the 'detailed' reports. + # Defaults to relative links. (gcovr --html-absolute-paths) + :html_absolute_paths: [true|false] + + # Override the declared HTML report encoding. Defaults to UTF-8. (gcovr --html-encoding) + :html_encoding: <html_encoding> +``` + +### Cobertura XML Reports + +Generation of Cobertura XML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable Cobertura XML reports or set to 'false' to disable. + # Defaults to disabled. (gcovr --xml) + # Deprecated - See the :reports: configuration option. + :xml_report: [true|false] + + + :gcovr: + # Set to 'true' to pretty-print the Cobertura XML report, otherwise set to 'false'. + # Defaults to disabled. (gcovr --xml-pretty) + :xml_pretty: [true|false] + :cobertura_pretty: [true|false] + + # Cobertura XML report filename. + :xml_artifact_filename: <output> + :cobertura_artifact_filename: <output> +``` + +### SonarQube XML Reports + +Generation of SonarQube XML reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # SonarQube XML report filename. + :sonarqube_artifact_filename: <output> +``` + +### JSON Reports + +Generation of JSON reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # Set to 'true' to pretty-print the JSON report, otherwise set 'false'. + # Defaults to disabled. (gcovr --json-pretty) + :json_pretty: [true|false] + + # JSON report filename. + :json_artifact_filename: <output> +``` + +### Text Reports + +Generation of text reports may be modified with the following configuration items. +Text reports may be printed to the console or output to a file. + +```yaml +:gcov: + :gcovr: + # Text report filename. + # The text report is printed to the console when no filename is provided. + :text_artifact_filename: <output> +``` + +### Common Report Options + +There are a number of options to control which files are considered part of +the coverage report. Most often, we only care about coverage on our source code, and not +on tests or automatically generated mocks, runners, etc. However, there are times +where this isn't true... or there are times where we've moved ceedling's directory +structure so that the project file isn't at the root of the project anymore. In these +cases, you may need to tweak `report_include`, `report_exclude`, and `exclude_directories`. + +One important note about `report_root`: gcovr will take only a single root folder, unlike +Ceedling's ability to take as many as you like. So you will need to choose a folder which is +a superset of ALL the folders you want, and then use the include or exclude options to set up +patterns of files to pay attention to or ignore. It's not ideal, but it works. + +Finally, there are a number of settings which can be specified to adjust the +default behaviors of gcovr: + +```yaml +:gcov: + :gcovr: + # The root directory of your source files. Defaults to ".", the current directory. + # File names are reported relative to this root. The report_root is the default report_include. + :report_root: "." + + # Load the specified configuration file. + # Defaults to gcovr.cfg in the report_root directory. (gcovr --config) + :config_file: <config_file> + + # Exit with a status of 2 if the total line coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_branch' option. (gcovr --fail-under-line) + :fail_under_line: 30 + + # Exit with a status of 4 if the total branch coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_line' option. (gcovr --fail-under-branch) + :fail_under_branch: 30 + + # Select the source file encoding. + # Defaults to the system default encoding (UTF-8). (gcovr --source-encoding) + :source_encoding: <source_encoding> + + # Report the branch coverage instead of the line coverage. For text report only. (gcovr --branches). + :branches: [true|false] + + # Sort entries by increasing number of uncovered lines. + # For text and HTML report. (gcovr --sort-uncovered) + :sort_uncovered: [true|false] + + # Sort entries by increasing percentage of uncovered lines. + # For text and HTML report. (gcovr --sort-percentage) + :sort_percentage: [true|false] + + # Print a small report to stdout with line & branch percentage coverage. + # This is in addition to other reports. (gcovr --print-summary). + :print_summary: [true|false] + + # Keep only source files that match this filter. (gcovr --filter). + :report_include: "^src" + + # Exclude source files that match this filter. (gcovr --exclude). + :report_exclude: "^vendor.*|^build.*|^test.*|^lib.*" + + # Keep only gcov data files that match this filter. (gcovr --gcov-filter). + :gcov_filter: <gcov_filter> + + # Exclude gcov data files that match this filter. (gcovr --gcov-exclude). + :gcov_exclude: <gcov_exclude> + + # Exclude directories that match this regex while searching + # raw coverage files. (gcovr --exclude-directories). + :exclude_directories: <exclude_dirs> + + # Use a particular gcov executable. (gcovr --gcov-executable). + :gcov_executable: <gcov_cmd> + + # Exclude branch coverage from lines without useful + # source code. (gcovr --exclude-unreachable-branches). + :exclude_unreachable_branches: [true|false] + + # For branch coverage, exclude branches that the compiler + # generates for exception handling. (gcovr --exclude-throw-branches). + :exclude_throw_branches: [true|false] + + # Use existing gcov files for analysis. Default: False. (gcovr --use-gcov-files) + :use_gcov_files: [true|false] + + # Skip lines with parse errors in GCOV files instead of + # exiting with an error. (gcovr --gcov-ignore-parse-errors). + :gcov_ignore_parse_errors: [true|false] + + # Override normal working directory detection. (gcovr --object-directory) + :object_directory: <objdir> + + # Keep gcov files after processing. (gcovr --keep). + :keep: [true|false] + + # Delete gcda files after processing. (gcovr --delete). + :delete: [true|false] + + # Set the number of threads to use in parallel. (gcovr -j). + :num_parallel_threads: <num_threads> + + # When scanning the code coverage, if any files are found that do not have + # associated coverage data, the command will abort with an error message. + :abort_on_uncovered: true + + # When using the ``abort_on_uncovered`` option, the files in this list will not + # trigger a failure. + # Ceedling globs described in the Ceedling packet ``Path`` section can be used + # when directories are placed on the list. Globs are limited to matching directories + # and not files. + :uncovered_ignore_list: [] +``` + +### ReportGenerator Configuration + +The ReportGenerator utility may be configured with the following configuration items. +All generated reports may be found in `build/artifacts/gcov/ReportGenerator`. + +```yaml +:gcov: + :report_generator: + # Optional directory for storing persistent coverage information. + # Can be used in future reports to show coverage evolution. + :history_directory: <history_directory> + + # Optional plugin files for custom reports or custom history storage (separated by semicolon). + :plugins: CustomReports.dll + + # Optional list of assemblies that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :assembly_filters: "+Included;-Excluded" + + # Optional list of classes that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :class_filters: "+Included;-Excluded" + + # Optional list of files that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :file_filters: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*" + + # The verbosity level of the log messages. + # Values: Verbose, Info, Warning, Error, Off + :verbosity: Warning + + # Optional tag or build version. + :tag: <tag> + + # Optional list of one or more regular expressions to exclude gcov notes files that match these filters. + :gcov_exclude: + - <exclude_regex1> + - <exclude_regex2> + + # Optionally use a particular gcov executable. Defaults to gcov. + :gcov_executable: <gcov_cmd> + + # Optionally set the number of threads to use in parallel. Defaults to 1. + :num_parallel_threads: <num_threads> + + # Optional list of one or more command line arguments to pass to Report Generator. + # Useful for configuring Risk Hotspots and Other Settings. + # https://github.com/danielpalme/ReportGenerator/wiki/Settings + :custom_args: + - <custom_arg1> + - <custom_arg2> +``` + +## Example Usage + +```sh +ceedling gcov:all utils:gcov +``` + +## To-Do list + +- Generate overall report (combined statistics from all files with coverage) + +## Citations + +Most of the comment text which describes the options was taken from the +[Gcovr User Guide](https://www.gcovr.com/en/stable/guide.html) and the +[ReportGenerator Wiki](https://github.com/danielpalme/ReportGenerator/wiki). +The text is repeated here to provide the most accurate option functionality. diff --git a/CAN_App/vendor/ceedling/docs/plugin_json_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_json_tests_report.md new file mode 100644 index 0000000..8e5a1e5 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_json_tests_report.md @@ -0,0 +1,36 @@ +json_tests_report +================= + +## Overview + +The json_tests_report plugin creates a JSON file of test results, which is +handy for Continuous Integration build servers or as input into other +reporting tools. The JSON file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `json_tests_report` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - json_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.json`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:json_tests_report: + :artifact_filename: report_spectuluarly.json +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_junit_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_junit_tests_report.md new file mode 100644 index 0000000..1259fd6 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_junit_tests_report.md @@ -0,0 +1,36 @@ +junit_tests_report +==================== + +## Overview + +The junit_tests_report plugin creates an XML file of test results in JUnit +format, which is handy for Continuous Integration build servers or as input +into other reporting tools. The XML file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `junit_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - junit_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.xml`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:junit_tests_report: + :artifact_filename: report_junit.xml +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_module_generator.md b/CAN_App/vendor/ceedling/docs/plugin_module_generator.md new file mode 100644 index 0000000..a3c2c7a --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_module_generator.md @@ -0,0 +1,119 @@ +ceedling-module-generator +========================= + +## Overview + +The module_generator plugin adds a pair of new commands to Ceedling, allowing +you to make or remove modules according to predefined templates. WIth a single call, +Ceedling can generate a source, header, and test file for a new module. If given a +pattern, it can even create a series of submodules to support specific design patterns. +Finally, it can just as easily remove related modules, avoiding the need to delete +each individually. + +Let's say, for example, that you want to create a single module named `MadScience`. + +``` +ceedling module:create[MadScience] +``` + +It says we're speaking to the module plugin, and we want to create a new module. The +name of that module is between the brackets. It will keep this case, unless you have +specified a different default (see configuration). It will create three files: +`MadScience.c`, `MadScience.h`, and `TestMadScience.c`. *NOTE* that it is important that +there are no spaces between the brackets. We know, it's annoying... but it's the rules. + +You can also create an entire pattern of files. To do that, just add a second argument +to the pattern ID. Something like this: + +``` +ceedling module:create[SecretLair,mch] +``` + +In this example, we'd create 9 files total: 3 headers, 3 source files, and 3 test files. These +files would be named `SecretLairModel`, `SecretLairConductor`, and `SecretLairHardware`. Isn't +that nice? + +Similarly, you can create stubs for all functions in a header file just by making a single call +to your handy `stub` feature, like this: + +``` +ceedling module:stub[SecretLair] +``` + +This call will look in SecretLair.h and will generate a file SecretLair.c that contains a stub +for each function declared in the header! Even better, if SecretLair.c already exists, it will +add only new functions, leaving your existing calls alone so that it doesn't cause any problems. + +## Configuration + +Enable the plugin in your project.yml by adding `module_generator` +to the list of enabled plugins. + +Then, like much of Ceedling, you can just run as-is with the defaults, or you can override those +defaults for your own needs. For example, new source and header files will be automatically +placed in the `src/` folder while tests will go in the `test/` folder. That's great if your project +follows the default ceedling structure... but what if you have a different structure? + +``` +:module_generator: + :project_root: ./ + :source_root: source/ + :inc_root: includes/ + :test_root: tests/ +``` + +Now I've redirected the location where modules are going to be generated. + +### Includes + +You can make it so that all of your files are generated with a standard include list. This is done +by adding to the `:includes` array. For example: + +``` +:module_generator: + :includes: + :tst: + - defs.h + - board.h + :src: + - board.h +``` + +### Boilerplates + +You can specify the actual boilerplate used for each of your files. This is the handy place to +put that corporate copyright notice (or maybe a copyleft notice, if that's your perference?) + +``` +:module_generator: + :boilerplates: | + /*************************** + * This file is Awesome. * + * That is All. * + ***************************/ +``` + +### Test Defines + +You can specify the "#ifdef TEST" at the top of the test files with a custom define. +This example will put a "#ifdef CEEDLING_TEST" at the top of the test files. + +``` +:module_generator: + :test_define: CEEDLING_TEST +``` + +### Naming Convention + +Finally, you can force a particular naming convention. Even if someone calls the generator +with something like `MyNewModule`, if they have the naming convention set to `:caps`, it will +generate files like `MY_NEW_MODULE.c`. This keeps everyone on your team behaving the same way. + +Your options are as follows: + + - `:bumpy` - BumpyFilesLooksLikeSo + - `:camel` - camelFilesAreSimilarButStartLow + - `:snake` - snake_case_is_all_lower_and_uses_underscores + - `:caps` - CAPS_FEELS_LIKE_YOU_ARE_SCREAMING + + diff --git a/CAN_App/vendor/ceedling/docs/plugin_raw_output_report.md b/CAN_App/vendor/ceedling/docs/plugin_raw_output_report.md new file mode 100644 index 0000000..330e87d --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_raw_output_report.md @@ -0,0 +1,19 @@ +ceedling-raw-output-report +========================== + +## Overview + +The raw-output-report allows you to capture all the output from the called +tools in a single document, so you can trace back through it later. This is +useful for debugging... but can eat through memory quickly if left running. + +## Setup + +Enable the plugin in your project.yml by adding `raw_output_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - raw_output_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_stdout_gtestlike_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_stdout_gtestlike_tests_report.md new file mode 100644 index 0000000..9ab6084 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_stdout_gtestlike_tests_report.md @@ -0,0 +1,19 @@ +ceedling-stdout-gtestlike-tests-report +====================== + +## Overview + +The stdout_gtestlike_tests_report replaces the normal ceedling "pretty" output with +a variant that resembles the output of gtest. This is most helpful when trying to +integrate into an IDE or CI that is meant to work with google test. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_gtestlike_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_gtestlike_tests_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_stdout_ide_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_stdout_ide_tests_report.md new file mode 100644 index 0000000..ed6c655 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_stdout_ide_tests_report.md @@ -0,0 +1,18 @@ +ceedling-stdout-ide-tests-report +================================ + +## Overview + +The stdout_ide_tests_report replaces the normal ceedling "pretty" output with +a simplified variant intended to be easily parseable. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_ide_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_ide_tests_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_stdout_pretty_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_stdout_pretty_tests_report.md new file mode 100644 index 0000000..7e1be23 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_stdout_pretty_tests_report.md @@ -0,0 +1,20 @@ +ceedling-pretty-tests-report +============================ + +## Overview + +The stdout_pretty_tests_report is the default output of ceedling. Instead of +showing most of the raw output of CMock, Ceedling, etc., it shows a simplified +view. It also creates a nice summary at the end of execution which groups the +results into ignored and failed tests. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_pretty_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_pretty_tests_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_subprojects.md b/CAN_App/vendor/ceedling/docs/plugin_subprojects.md new file mode 100644 index 0000000..e51a4e6 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_subprojects.md @@ -0,0 +1,63 @@ +ceedling-subprojects +==================== + +Plugin for supporting subprojects that are built as static libraries. It continues to support +dependency tracking, without getting confused between your main project files and your +subproject files. It accepts different compiler flags and linker flags, allowing you to +optimize for your situation. + +First, you're going to want to add the extension to your list of known extensions: + +``` +:extension: + :subprojects: '.a' +``` + +Define a new section called :subprojects. There, you can list as many subprojects +as you may need under the :paths key. For each, you specify a unique place to build +and a unique name. + +``` +:subprojects: + :paths: + - :name: libprojectA + :source: + - ./subprojectA/first/dir + - ./subprojectA/second/dir + :include: + - ./subprojectA/include/dir + :build_root: ./subprojectA/build/dir + :defines: + - DEFINE_JUST_FOR_THIS_FILE + - AND_ANOTHER + - :name: libprojectB + :source: + - ./subprojectB/only/dir + :include: + - ./subprojectB/first/include/dir + - ./subprojectB/second/include/dir + :build_root: ./subprojectB/build/dir + :defines: [] #none for this one +``` + +You can specify the compiler and linker, just as you would a release build: + +``` +:tools: + :subprojects_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_SUBPROJECTS + - -D$: COLLECTION_DEFINES_SUBPROJECTS + - -c "${1}" + - -o "${2}" + :subprojects_linker: + :executable: ar + :arguments: + - rcs + - ${2} + - ${1} +``` + +That's all there is to it! Happy Hacking! diff --git a/CAN_App/vendor/ceedling/docs/plugin_teamcity_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_teamcity_tests_report.md new file mode 100644 index 0000000..9fcda7d --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_teamcity_tests_report.md @@ -0,0 +1,18 @@ +ceedling-teamcity-tests-report +============================== + +## Overview + +The teamcity_tests_report replaces the normal ceedling "pretty" output with +a version that has results tagged to be consumed with the teamcity CI server. + +## Setup + +Enable the plugin in your project.yml by adding `teamcity_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - teamcity_tests_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_warnings_report.md b/CAN_App/vendor/ceedling/docs/plugin_warnings_report.md new file mode 100644 index 0000000..fd7fae5 --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_warnings_report.md @@ -0,0 +1,19 @@ +warnings-report +=============== + +## Overview + +The warnings_report captures all warnings throughout the build process +and collects them into a single report at the end of execution. It places all +of this into a warnings file in the output artifact directory. + +## Setup + +Enable the plugin in your project.yml by adding `warnings_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - warnings_report +``` diff --git a/CAN_App/vendor/ceedling/docs/plugin_xml_tests_report.md b/CAN_App/vendor/ceedling/docs/plugin_xml_tests_report.md new file mode 100644 index 0000000..6200c7d --- /dev/null +++ b/CAN_App/vendor/ceedling/docs/plugin_xml_tests_report.md @@ -0,0 +1,36 @@ +xml_tests_report +================ + +## Overview + +The xml_tests_report plugin creates an XML file of test results in xUnit +format, which is handy for Continuous Integration build servers or as input +into other reporting tools. The XML file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `xml_tests_report` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - xml_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.xml`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:xml_tests_report: + :artifact_filename: report_xunit.xml +``` diff --git a/CAN_App/vendor/ceedling/lib/ceedling.rb b/CAN_App/vendor/ceedling/lib/ceedling.rb new file mode 100644 index 0000000..7f34002 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling.rb @@ -0,0 +1,99 @@ +## +# This module defines the interface for interacting with and loading a project +# with Ceedling. +module Ceedling + ## + # Returns the location where the gem is installed. + # === Return + # _String_ - The location where the gem lives. + def self.location + File.join( File.dirname(__FILE__), '..') + end + + ## + # Return the path to the "built-in" plugins. + # === Return + # _String_ - The path where the default plugins live. + def self.load_path + File.join( self.location, 'plugins') + end + + ## + # Return the path to the Ceedling Rakefile + # === Return + # _String_ + def self.rakefile + File.join( self.location, 'lib', 'ceedling', 'rakefile.rb' ) + end + + ## + # This method selects the project file that Ceedling will use by setting the + # CEEDLING_MAIN_PROJECT_FILE environment variable before loading the ceedling + # rakefile. A path supplied as an argument to this method will override the + # current value of the environment variable. If no path is supplied as an + # argument then the existing value of the environment variable is used. If + # the environment variable has not been set and no argument has been supplied + # then a default path of './project.yml' will be used. + # + # === Arguments + # +options+ _Hash_:: + # A hash containing the options for ceedling. Currently the following + # options are supported: + # * +config+ - The path to the project YAML configuration file. + # * +root+ - The root of the project directory. + # * +prefix+ - A prefix to prepend to plugin names in order to determine the + # corresponding gem name. + # * +plugins+ - The list of ceedling plugins to load + def self.load_project(options = {}) + # Make sure our path to the yaml file is setup + if options.has_key? :config + ENV['CEEDLING_MAIN_PROJECT_FILE'] = options[:config] + elsif ENV['CEEDLING_MAIN_PROJECT_FILE'].nil? + ENV['CEEDLING_MAIN_PROJECT_FILE'] = './project.yml' + end + + # Register the plugins + if options.has_key? :plugins + options[:plugins].each do |plugin| + register_plugin( plugin, options[:prefix] ) + end + end + + # Define the root of the project if specified + Object.const_set('PROJECT_ROOT', options[:root]) if options.has_key? :root + + # Load ceedling + load "#{self.rakefile}" + end + + ## + # Register a plugin for ceedling to use when a project is loaded. This method + # *must* be called prior to calling the _load_project_ method. + # + # This method is intended to be used for loading plugins distributed via the + # RubyGems mechanism. As such, the following gem structure is assumed for + # plugins. + # + # * The gem name must be prefixed with 'ceedling-' followed by the plugin + # name (ex. 'ceedling-bullseye') + # + # * The contents of the plugin must be isntalled into a subdirectory of + # the gem with the same name as the plugin (ex. 'bullseye/') + # + # === Arguments + # +name+ _String_:: The name of the plugin to load. + # +prefix+ _String_:: + # (optional, default = nil) The prefix to use for the full gem name. + def self.register_plugin(name, prefix=nil) + # Figure out the full name of the gem and location + prefix ||= 'ceedling-' + gem_name = prefix + name + gem_dir = Gem::Specification.find_by_name(gem_name).gem_dir() + + # Register the plugin with Ceedling + require 'ceedling/defaults' + DEFAULT_CEEDLING_CONFIG[:plugins][:enabled] << name + DEFAULT_CEEDLING_CONFIG[:plugins][:load_paths] << gem_dir + end +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/build_invoker_utils.rb b/CAN_App/vendor/ceedling/lib/ceedling/build_invoker_utils.rb new file mode 100644 index 0000000..5727bca --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/build_invoker_utils.rb @@ -0,0 +1,39 @@ +require 'ceedling/constants' + +## +# Utilities for raiser and reporting errors during building. +class BuildInvokerUtils + + constructor :configurator, :streaminator + + ## + # Processes exceptions and tries to display a useful message for the user. + # + # ==== Attributes + # + # * _exception_: The exception given by a rescue statement. + # * _context_: A symbol representing where in the build the exception + # occurs. + # * _test_build_: A bool to signify if the exception occurred while building + # from test or source. + # + def process_exception(exception, context, test_build=true) + if (exception.message =~ /Don't know how to build task '(.+)'/i) + error_header = "ERROR: Rake could not find file referenced in source" + error_header += " or test" if (test_build) + error_header += ": '#{$1}'. Possible stale dependency." + + @streaminator.stderr_puts( error_header ) + + if (@configurator.project_use_deep_dependencies) + help_message = "Try fixing #include statements or adding missing file. Then run '#{REFRESH_TASK_ROOT}#{context.to_s}' task and try again." + @streaminator.stderr_puts( help_message ) + end + + raise '' + else + raise exception + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/cacheinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/cacheinator.rb new file mode 100644 index 0000000..519a4aa --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/cacheinator.rb @@ -0,0 +1,47 @@ + +class Cacheinator + + constructor :cacheinator_helper, :file_path_utils, :file_wrapper, :yaml_wrapper + + def cache_test_config(hash) + @yaml_wrapper.dump( @file_path_utils.form_test_build_cache_path( INPUT_CONFIGURATION_CACHE_FILE), hash ) + end + + def cache_release_config(hash) + @yaml_wrapper.dump( @file_path_utils.form_release_build_cache_path( INPUT_CONFIGURATION_CACHE_FILE ), hash ) + end + + + def diff_cached_test_file( filepath ) + cached_filepath = @file_path_utils.form_test_build_cache_path( filepath ) + + if (@file_wrapper.exist?( cached_filepath ) and (!@file_wrapper.compare( filepath, cached_filepath ))) + @file_wrapper.cp(filepath, cached_filepath, {:preserve => false}) + return filepath + elsif (!@file_wrapper.exist?( cached_filepath )) + @file_wrapper.cp(filepath, cached_filepath, {:preserve => false}) + return filepath + end + + return cached_filepath + end + + def diff_cached_test_config?(hash) + cached_filepath = @file_path_utils.form_test_build_cache_path(INPUT_CONFIGURATION_CACHE_FILE) + + return @cacheinator_helper.diff_cached_config?( cached_filepath, hash ) + end + + def diff_cached_test_defines?(files) + cached_filepath = @file_path_utils.form_test_build_cache_path(DEFINES_DEPENDENCY_CACHE_FILE) + + return @cacheinator_helper.diff_cached_defines?( cached_filepath, files ) + end + + def diff_cached_release_config?(hash) + cached_filepath = @file_path_utils.form_release_build_cache_path(INPUT_CONFIGURATION_CACHE_FILE) + + return @cacheinator_helper.diff_cached_config?( cached_filepath, hash ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/cacheinator_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/cacheinator_helper.rb new file mode 100644 index 0000000..14e8a6e --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/cacheinator_helper.rb @@ -0,0 +1,35 @@ + +class CacheinatorHelper + + constructor :file_wrapper, :yaml_wrapper + + def diff_cached_config?(cached_filepath, hash) + return false if ( not @file_wrapper.exist?(cached_filepath) ) + return true if (@yaml_wrapper.load(cached_filepath) != hash) + return false + end + + def diff_cached_defines?(cached_filepath, files) + changed_defines = false + current_defines = COLLECTION_DEFINES_TEST_AND_VENDOR.reject(&:empty?) + + current_dependencies = Hash[files.collect { |source| [source, current_defines.dup] }] + if not @file_wrapper.exist?(cached_filepath) + @yaml_wrapper.dump(cached_filepath, current_dependencies) + return changed_defines + end + + dependencies = @yaml_wrapper.load(cached_filepath) + common_dependencies = current_dependencies.select { |file, defines| dependencies.has_key?(file) } + + if dependencies.values_at(*common_dependencies.keys) != common_dependencies.values + changed_defines = true + end + + dependencies.merge!(current_dependencies) + @yaml_wrapper.dump(cached_filepath, dependencies) + + return changed_defines + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/cmock_builder.rb b/CAN_App/vendor/ceedling/lib/ceedling/cmock_builder.rb new file mode 100644 index 0000000..4a74aa8 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/cmock_builder.rb @@ -0,0 +1,15 @@ +require 'cmock' + +class CmockBuilder + + attr_accessor :cmock + + def setup + @cmock = nil + end + + def manufacture(cmock_config) + @cmock = CMock.new(cmock_config) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/configurator.rb b/CAN_App/vendor/ceedling/lib/ceedling/configurator.rb new file mode 100644 index 0000000..0ae4d04 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/configurator.rb @@ -0,0 +1,382 @@ +require 'ceedling/defaults' +require 'ceedling/constants' +require 'ceedling/file_path_utils' +require 'deep_merge' + + + +class Configurator + + attr_reader :project_config_hash, :script_plugins, :rake_plugins + attr_accessor :project_logging, :project_debug, :project_verbosity, :sanity_checks + + constructor(:configurator_setup, :configurator_builder, :configurator_plugins, :cmock_builder, :yaml_wrapper, :system_wrapper) do + @project_logging = false + @project_debug = false + @project_verbosity = Verbosity::NORMAL + @sanity_checks = TestResultsSanityChecks::NORMAL + end + + def setup + # special copy of cmock config to provide to cmock for construction + @cmock_config_hash = {} + + # note: project_config_hash is an instance variable so constants and accessors created + # in eval() statements in build() have something of proper scope and persistence to reference + @project_config_hash = {} + @project_config_hash_backup = {} + + @script_plugins = [] + @rake_plugins = [] + end + + + def replace_flattened_config(config) + @project_config_hash.merge!(config) + @configurator_setup.build_constants_and_accessors(@project_config_hash, binding()) + end + + + def store_config + @project_config_hash_backup = @project_config_hash.clone + end + + + def restore_config + @project_config_hash = @project_config_hash_backup + @configurator_setup.build_constants_and_accessors(@project_config_hash, binding()) + end + + + def reset_defaults(config) + [:test_compiler, + :test_linker, + :test_fixture, + :test_includes_preprocessor, + :test_file_preprocessor, + :test_file_preprocessor_directives, + :test_dependencies_generator, + :release_compiler, + :release_assembler, + :release_linker, + :release_dependencies_generator].each do |tool| + config[:tools].delete(tool) if (not (config[:tools][tool].nil?)) + end + end + + + # The default values defined in defaults.rb (eg. DEFAULT_TOOLS_TEST) are populated + # into @param config + def populate_defaults(config) + new_config = DEFAULT_CEEDLING_CONFIG.deep_clone + new_config.deep_merge!(config) + config.replace(new_config) + + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST ) + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_PREPROCESSORS ) if (config[:project][:use_test_preprocessor]) + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_DEPENDENCIES ) if (config[:project][:use_deep_dependencies]) + + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE ) if (config[:project][:release_build]) + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_ASSEMBLER ) if (config[:project][:release_build] and config[:release_build][:use_assembly]) + @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_DEPENDENCIES ) if (config[:project][:release_build] and config[:project][:use_deep_dependencies]) + end + + + def populate_unity_defaults(config) + unity = config[:unity] || {} + @runner_config = unity.merge(@runner_config || config[:test_runner] || {}) + end + + def populate_cmock_defaults(config) + # cmock has its own internal defaults handling, but we need to set these specific values + # so they're present for the build environment to access; + # note: these need to end up in the hash given to initialize cmock for this to be successful + cmock = config[:cmock] || {} + + # yes, we're duplicating the default mock_prefix in cmock, but it's because we need CMOCK_MOCK_PREFIX always available in Ceedling's environment + cmock[:mock_prefix] = 'Mock' if (cmock[:mock_prefix].nil?) + + # just because strict ordering is the way to go + cmock[:enforce_strict_ordering] = true if (cmock[:enforce_strict_ordering].nil?) + + cmock[:mock_path] = File.join(config[:project][:build_root], TESTS_BASE_PATH, 'mocks') if (cmock[:mock_path].nil?) + cmock[:verbosity] = @project_verbosity if (cmock[:verbosity].nil?) + + cmock[:plugins] = [] if (cmock[:plugins].nil?) + cmock[:plugins].map! { |plugin| plugin.to_sym } + cmock[:plugins] << (:cexception) if (!cmock[:plugins].include?(:cexception) and (config[:project][:use_exceptions])) + cmock[:plugins].uniq! + + cmock[:unity_helper] = false if (cmock[:unity_helper].nil?) + + if (cmock[:unity_helper]) + cmock[:unity_helper] = [cmock[:unity_helper]] if cmock[:unity_helper].is_a? String + cmock[:includes] += cmock[:unity_helper].map{|helper| File.basename(helper) } + cmock[:includes].uniq! + end + + @runner_config = cmock.merge(@runner_config || config[:test_runner] || {}) + + @cmock_builder.manufacture(cmock) + end + + + def get_runner_config + @runner_config + end + + + # grab tool names from yaml and insert into tool structures so available for error messages + # set up default values + def tools_setup(config) + config[:tools].each_key do |name| + tool = config[:tools][name] + + # populate name if not given + tool[:name] = name.to_s if (tool[:name].nil?) + + # handle inline ruby string substitution in executable + if (tool[:executable] =~ RUBY_STRING_REPLACEMENT_PATTERN) + tool[:executable].replace(@system_wrapper.module_eval(tool[:executable])) + end + + # populate stderr redirect option + tool[:stderr_redirect] = StdErrRedirect::NONE if (tool[:stderr_redirect].nil?) + + # populate background execution option + tool[:background_exec] = BackgroundExec::NONE if (tool[:background_exec].nil?) + + # populate optional option to control verification of executable in search paths + tool[:optional] = false if (tool[:optional].nil?) + end + end + + + def tools_supplement_arguments(config) + tools_name_prefix = 'tools_' + config[:tools].each_key do |name| + tool = @project_config_hash[(tools_name_prefix + name.to_s).to_sym] + + # smoosh in extra arguments if specified at top-level of config (useful for plugins & default gcc tools) + # arguments are squirted in at _end_ of list + top_level_tool = (tools_name_prefix + name.to_s).to_sym + if (not config[top_level_tool].nil?) + # adding and flattening is not a good idea: might over-flatten if there's array nesting in tool args + tool[:arguments].concat config[top_level_tool][:arguments] + end + end + end + + + def find_and_merge_plugins(config) + # plugins must be loaded before generic path evaluation & magic that happen later; + # perform path magic here as discrete step + config[:plugins][:load_paths].each do |path| + path.replace(@system_wrapper.module_eval(path)) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + FilePathUtils::standardize(path) + end + + config[:plugins][:load_paths] << FilePathUtils::standardize(Ceedling.load_path) + config[:plugins][:load_paths].uniq! + + paths_hash = @configurator_plugins.add_load_paths(config) + + @rake_plugins = @configurator_plugins.find_rake_plugins(config, paths_hash) + @script_plugins = @configurator_plugins.find_script_plugins(config, paths_hash) + config_plugins = @configurator_plugins.find_config_plugins(config, paths_hash) + plugin_yml_defaults = @configurator_plugins.find_plugin_yml_defaults(config, paths_hash) + plugin_hash_defaults = @configurator_plugins.find_plugin_hash_defaults(config, paths_hash) + + config_plugins.each do |plugin| + plugin_config = @yaml_wrapper.load(plugin) + config.deep_merge(plugin_config) + end + + plugin_yml_defaults.each do |defaults| + @configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) ) + end + + plugin_hash_defaults.each do |defaults| + @configurator_builder.populate_defaults( config, defaults ) + end + + # special plugin setting for results printing + config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?) + + paths_hash.each_pair { |name, path| config[:plugins][name] = path } + end + + + def merge_imports(config) + if config[:import] + if config[:import].is_a? Array + until config[:import].empty? + path = config[:import].shift + path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + config.deep_merge!(@yaml_wrapper.load(path)) + end + else + config[:import].each_value do |path| + if !path.nil? + path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + config.deep_merge!(@yaml_wrapper.load(path)) + end + end + end + end + config.delete(:import) + end + + + def eval_environment_variables(config) + config[:environment].each do |hash| + key = hash.keys[0] + value = hash[key] + items = [] + + interstitial = ((key == :path) ? File::PATH_SEPARATOR : '') + items = ((value.class == Array) ? hash[key] : [value]) + + items.each do |item| + if item.is_a? String and item =~ RUBY_STRING_REPLACEMENT_PATTERN + item.replace( @system_wrapper.module_eval( item ) ) + end + end + hash[key] = items.join( interstitial ) + + @system_wrapper.env_set( key.to_s.upcase, hash[key] ) + end + end + + + def eval_paths(config) + # [:plugins]:[load_paths] already handled + + paths = [ # individual paths that don't follow convention processed below + config[:project][:build_root], + config[:release_build][:artifacts]] + + eval_path_list( paths ) + + config[:paths].each_pair { |collection, paths| eval_path_list( paths ) } + + config[:files].each_pair { |collection, files| eval_path_list( files ) } + + # all other paths at secondary hash key level processed by convention: + # ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are evaluated + config.each_pair { |parent, child| eval_path_list( collect_path_list( child ) ) } + end + + + def standardize_paths(config) + # [:plugins]:[load_paths] already handled + + paths = [ # individual paths that don't follow convention processed below + config[:project][:build_root], + config[:release_build][:artifacts]] # cmock path in case it was explicitly set in config + + paths.flatten.each { |path| FilePathUtils::standardize( path ) } + + config[:paths].each_pair do |collection, paths| + # ensure that list is an array (i.e. handle case of list being a single string, + # or a multidimensional array) + config[:paths][collection] = [paths].flatten.map{|path| FilePathUtils::standardize( path )} + end + + config[:files].each_pair { |collection, files| files.each{ |path| FilePathUtils::standardize( path ) } } + + config[:tools].each_pair { |tool, config| FilePathUtils::standardize( config[:executable] ) if (config.include? :executable) } + + # all other paths at secondary hash key level processed by convention: + # ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are standardized + config.each_pair do |parent, child| + collect_path_list( child ).each { |path| FilePathUtils::standardize( path ) } + end + end + + + def validate(config) + # collect felonies and go straight to jail + raise if (not @configurator_setup.validate_required_sections( config )) + + # collect all misdemeanors, everybody on probation + blotter = [] + blotter << @configurator_setup.validate_required_section_values( config ) + blotter << @configurator_setup.validate_paths( config ) + blotter << @configurator_setup.validate_tools( config ) + blotter << @configurator_setup.validate_plugins( config ) + + raise if (blotter.include?( false )) + end + + + # create constants and accessors (attached to this object) from given hash + def build(config, *keys) + # create flattened & expanded configuration hash + built_config = @configurator_setup.build_project_config( config, @configurator_builder.flattenify( config ) ) + + @project_config_hash = built_config.clone + store_config() + + @configurator_setup.build_constants_and_accessors(built_config, binding()) + + # top-level keys disappear when we flatten, so create global constants & accessors to any specified keys + keys.each do |key| + hash = { key => config[key] } + @configurator_setup.build_constants_and_accessors(hash, binding()) + end + end + + + # add to constants and accessors as post build step + def build_supplement(config_base, config_more) + # merge in our post-build additions to base configuration hash + config_base.deep_merge!( config_more ) + + # flatten our addition hash + config_more_flattened = @configurator_builder.flattenify( config_more ) + + # merge our flattened hash with built hash from previous build + @project_config_hash.deep_merge!( config_more_flattened ) + store_config() + + # create more constants and accessors + @configurator_setup.build_constants_and_accessors(config_more_flattened, binding()) + + # recreate constants & update accessors with new merged, base values + config_more.keys.each do |key| + hash = { key => config_base[key] } + @configurator_setup.build_constants_and_accessors(hash, binding()) + end + end + + + def insert_rake_plugins(plugins) + plugins.each do |plugin| + @project_config_hash[:project_rakefile_component_files] << plugin + end + end + + ### private ### + + private + + def collect_path_list( container ) + paths = [] + container.each_key { |key| paths << container[key] if (key.to_s =~ /_path(s)?$/) } if (container.class == Hash) + return paths.flatten + end + + def eval_path_list( paths ) + if paths.kind_of?(Array) + paths = Array.new(paths) + end + + paths.flatten.each do |path| + path.replace( @system_wrapper.module_eval( path ) ) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + end + end + + +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/configurator_builder.rb b/CAN_App/vendor/ceedling/lib/ceedling/configurator_builder.rb new file mode 100644 index 0000000..f202d8a --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/configurator_builder.rb @@ -0,0 +1,475 @@ +require 'rubygems' +require 'rake' # for ext() method +require 'ceedling/file_path_utils' # for class methods +require 'ceedling/defaults' +require 'ceedling/constants' # for Verbosity constants class & base file paths + + + +class ConfiguratorBuilder + + constructor :file_system_utils, :file_wrapper, :system_wrapper + + + def build_global_constants(config) + config.each_pair do |key, value| + formatted_key = key.to_s.upcase + # undefine global constant if it already exists + Object.send(:remove_const, formatted_key.to_sym) if @system_wrapper.constants_include?(formatted_key) + # create global constant + Object.module_eval("#{formatted_key} = value") + end + end + + + def build_accessor_methods(config, context) + config.each_pair do |key, value| + # fill configurator object with accessor methods + eval("def #{key.to_s.downcase}() return @project_config_hash[:#{key.to_s}] end", context) + end + end + + + # create a flattened hash from the original configuration structure + def flattenify(config) + new_hash = {} + + config.each_key do | parent | + + # gracefully handle empty top-level entries + next if (config[parent].nil?) + + case config[parent] + when Array + config[parent].each do |hash| + key = "#{parent.to_s.downcase}_#{hash.keys[0].to_s.downcase}".to_sym + new_hash[key] = hash[hash.keys[0]] + end + when Hash + config[parent].each_pair do | child, value | + key = "#{parent.to_s.downcase}_#{child.to_s.downcase}".to_sym + new_hash[key] = value + end + # handle entries with no children, only values + else + new_hash["#{parent.to_s.downcase}".to_sym] = config[parent] + end + + end + + return new_hash + end + + + def populate_defaults(config, defaults) + defaults.keys.sort.each do |section| + defaults[section].keys.sort.each do |entry| + config[section] = {} if config[section].nil? + config[section][entry] = defaults[section][entry].deep_clone if (config[section][entry].nil?) + end + end + end + + + def clean(in_hash) + # ensure that include files inserted into test runners have file extensions & proper ones at that + in_hash[:test_runner_includes].map!{|include| include.ext(in_hash[:extension_header])} + end + + + def set_build_paths(in_hash) + out_hash = {} + + project_build_artifacts_root = File.join(in_hash[:project_build_root], 'artifacts') + project_build_tests_root = File.join(in_hash[:project_build_root], TESTS_BASE_PATH) + project_build_release_root = File.join(in_hash[:project_build_root], RELEASE_BASE_PATH) + + paths = [ + [:project_build_artifacts_root, project_build_artifacts_root, true ], + [:project_build_tests_root, project_build_tests_root, true ], + [:project_build_release_root, project_build_release_root, in_hash[:project_release_build] ], + + [:project_test_artifacts_path, File.join(project_build_artifacts_root, TESTS_BASE_PATH), true ], + [:project_test_runners_path, File.join(project_build_tests_root, 'runners'), true ], + [:project_test_results_path, File.join(project_build_tests_root, 'results'), true ], + [:project_test_build_output_path, File.join(project_build_tests_root, 'out'), true ], + [:project_test_build_output_asm_path, File.join(project_build_tests_root, 'out', 'asm'), true ], + [:project_test_build_output_c_path, File.join(project_build_tests_root, 'out', 'c'), true ], + [:project_test_build_cache_path, File.join(project_build_tests_root, 'cache'), true ], + [:project_test_dependencies_path, File.join(project_build_tests_root, 'dependencies'), true ], + + [:project_release_artifacts_path, File.join(project_build_artifacts_root, RELEASE_BASE_PATH), in_hash[:project_release_build] ], + [:project_release_build_cache_path, File.join(project_build_release_root, 'cache'), in_hash[:project_release_build] ], + [:project_release_build_output_path, File.join(project_build_release_root, 'out'), in_hash[:project_release_build] ], + [:project_release_build_output_asm_path, File.join(project_build_release_root, 'out', 'asm'), in_hash[:project_release_build] ], + [:project_release_build_output_c_path, File.join(project_build_release_root, 'out', 'c'), in_hash[:project_release_build] ], + [:project_release_dependencies_path, File.join(project_build_release_root, 'dependencies'), in_hash[:project_release_build] ], + + [:project_log_path, File.join(in_hash[:project_build_root], 'logs'), true ], + [:project_temp_path, File.join(in_hash[:project_build_root], 'temp'), true ], + + [:project_test_preprocess_includes_path, File.join(project_build_tests_root, 'preprocess/includes'), in_hash[:project_use_test_preprocessor] ], + [:project_test_preprocess_files_path, File.join(project_build_tests_root, 'preprocess/files'), in_hash[:project_use_test_preprocessor] ], + ] + + out_hash[:project_build_paths] = [] + + # fetch already set mock path + out_hash[:project_build_paths] << in_hash[:cmock_mock_path] if (in_hash[:project_use_mocks]) + + paths.each do |path| + build_path_name = path[0] + build_path = path[1] + build_path_add_condition = path[2] + + # insert path into build paths if associated with true condition + out_hash[:project_build_paths] << build_path if build_path_add_condition + # set path symbol name and path for each entry in paths array + out_hash[build_path_name] = build_path + end + + return out_hash + end + + + def set_force_build_filepaths(in_hash) + out_hash = {} + + out_hash[:project_test_force_rebuild_filepath] = File.join( in_hash[:project_test_dependencies_path], 'force_build' ) + out_hash[:project_release_force_rebuild_filepath] = File.join( in_hash[:project_release_dependencies_path], 'force_build' ) if (in_hash[:project_release_build]) + + return out_hash + end + + + def set_rakefile_components(in_hash) + out_hash = { + :project_rakefile_component_files => + [File.join(CEEDLING_LIB, 'ceedling', 'tasks_base.rake'), + File.join(CEEDLING_LIB, 'ceedling', 'tasks_filesystem.rake'), + File.join(CEEDLING_LIB, 'ceedling', 'tasks_tests.rake'), + File.join(CEEDLING_LIB, 'ceedling', 'tasks_vendor.rake'), + File.join(CEEDLING_LIB, 'ceedling', 'rules_tests.rake')]} + + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_cmock.rake') if (in_hash[:project_use_mocks]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_preprocess.rake') if (in_hash[:project_use_test_preprocessor]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies]) + + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_release.rake') if (in_hash[:project_release_build]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies]) + out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_release.rake') if (in_hash[:project_release_build]) + + return out_hash + end + + + def set_release_target(in_hash) + return {} if (not in_hash[:project_release_build]) + + release_target_file = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_executable])) : in_hash[:release_build_output]) + release_map_file = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_map])) : in_hash[:release_build_output].ext(in_hash[:extension_map])) + + return { + # tempted to make a helper method in file_path_utils? stop right there, pal. you'll introduce a cyclical dependency + :project_release_build_target => File.join(in_hash[:project_build_release_root], release_target_file), + :project_release_build_map => File.join(in_hash[:project_build_release_root], release_map_file) + } + end + + + def collect_project_options(in_hash) + options = [] + + in_hash[:project_options_paths].each do |path| + options << @file_wrapper.directory_listing( File.join(path, '*.yml') ) + end + + return { + :collection_project_options => options.flatten + } + end + + + def expand_all_path_globs(in_hash) + out_hash = {} + path_keys = [] + + in_hash.each_key do |key| + next if (not key.to_s[0..4] == 'paths') + path_keys << key + end + + # sorted to provide assured order of traversal in test calls on mocks + path_keys.sort.each do |key| + out_hash["collection_#{key.to_s}".to_sym] = @file_system_utils.collect_paths( in_hash[key] ) + end + + return out_hash + end + + + def collect_source_and_include_paths(in_hash) + return { + :collection_paths_source_and_include => + ( in_hash[:collection_paths_source] + + in_hash[:collection_paths_include] ).select {|x| File.directory?(x)} + } + end + + + def collect_source_include_vendor_paths(in_hash) + extra_paths = [] + extra_paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions]) + + return { + :collection_paths_source_include_vendor => + in_hash[:collection_paths_source_and_include] + + extra_paths + } + end + + + def collect_test_support_source_include_paths(in_hash) + return { + :collection_paths_test_support_source_include => + (in_hash[:collection_paths_test] + + in_hash[:collection_paths_support] + + in_hash[:collection_paths_source] + + in_hash[:collection_paths_include] ).select {|x| File.directory?(x)} + } + end + + + def collect_vendor_paths(in_hash) + return {:collection_paths_vendor => get_vendor_paths(in_hash)} + end + + + def collect_test_support_source_include_vendor_paths(in_hash) + return { + :collection_paths_test_support_source_include_vendor => + get_vendor_paths(in_hash) + + in_hash[:collection_paths_test_support_source_include] + } + end + + + def collect_tests(in_hash) + all_tests = @file_wrapper.instantiate_file_list + + in_hash[:collection_paths_test].each do |path| + all_tests.include( File.join(path, "#{in_hash[:project_test_file_prefix]}*#{in_hash[:extension_source]}") ) + end + + @file_system_utils.revise_file_list( all_tests, in_hash[:files_test] ) + + return {:collection_all_tests => all_tests} + end + + + def collect_assembly(in_hash) + all_assembly = @file_wrapper.instantiate_file_list + + return {:collection_all_assembly => all_assembly} if ((not in_hash[:release_build_use_assembly]) && (not in_hash[:test_build_use_assembly])) + + # Sprinkle in all assembly files we can find in the source folders + in_hash[:collection_paths_source].each do |path| + all_assembly.include( File.join(path, "*#{in_hash[:extension_assembly]}") ) + end + + # Also add all assembly files we can find in the support folders + in_hash[:collection_paths_support].each do |path| + all_assembly.include( File.join(path, "*#{in_hash[:extension_assembly]}") ) + end + + # Also add files that we are explicitly adding via :files:assembly: section + @file_system_utils.revise_file_list( all_assembly, in_hash[:files_assembly] ) + + return {:collection_all_assembly => all_assembly} + end + + + def collect_source(in_hash) + all_source = @file_wrapper.instantiate_file_list + in_hash[:collection_paths_source].each do |path| + if File.exists?(path) and not File.directory?(path) + all_source.include( path ) + else + all_source.include( File.join(path, "*#{in_hash[:extension_source]}") ) + end + end + @file_system_utils.revise_file_list( all_source, in_hash[:files_source] ) + + return {:collection_all_source => all_source} + end + + + def collect_headers(in_hash) + all_headers = @file_wrapper.instantiate_file_list + + paths = + in_hash[:collection_paths_test] + + in_hash[:collection_paths_support] + + in_hash[:collection_paths_source] + + in_hash[:collection_paths_include] + + paths.each do |path| + all_headers.include( File.join(path, "*#{in_hash[:extension_header]}") ) + end + + @file_system_utils.revise_file_list( all_headers, in_hash[:files_include] ) + + return {:collection_all_headers => all_headers} + end + + + def collect_release_existing_compilation_input(in_hash) + release_input = @file_wrapper.instantiate_file_list + + paths = + in_hash[:collection_paths_source] + + in_hash[:collection_paths_include] + + paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions]) + + paths.each do |path| + release_input.include( File.join(path, "*#{in_hash[:extension_header]}") ) + if File.exists?(path) and not File.directory?(path) + release_input.include( path ) + else + release_input.include( File.join(path, "*#{in_hash[:extension_source]}") ) + end + end + + @file_system_utils.revise_file_list( release_input, in_hash[:files_source] ) + @file_system_utils.revise_file_list( release_input, in_hash[:files_include] ) + # finding assembly files handled explicitly through other means + + return {:collection_release_existing_compilation_input => release_input} + end + + + def collect_all_existing_compilation_input(in_hash) + all_input = @file_wrapper.instantiate_file_list + + paths = + in_hash[:collection_paths_test] + + in_hash[:collection_paths_support] + + in_hash[:collection_paths_source] + + in_hash[:collection_paths_include] + + [File.join(in_hash[:unity_vendor_path], UNITY_LIB_PATH)] + + paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions]) + paths << File.join(in_hash[:cmock_vendor_path], CMOCK_LIB_PATH) if (in_hash[:project_use_mocks]) + + paths.each do |path| + all_input.include( File.join(path, "*#{in_hash[:extension_header]}") ) + if File.exists?(path) and not File.directory?(path) + all_input.include( path ) + else + all_input.include( File.join(path, "*#{in_hash[:extension_source]}") ) + all_input.include( File.join(path, "*#{in_hash[:extension_assembly]}") ) if (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) + end + end + + @file_system_utils.revise_file_list( all_input, in_hash[:files_test] ) + @file_system_utils.revise_file_list( all_input, in_hash[:files_support] ) + @file_system_utils.revise_file_list( all_input, in_hash[:files_source] ) + @file_system_utils.revise_file_list( all_input, in_hash[:files_include] ) + # finding assembly files handled explicitly through other means + + return {:collection_all_existing_compilation_input => all_input} + end + + + def get_vendor_defines(in_hash) + defines = in_hash[:unity_defines].clone + defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks]) + defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions]) + + return defines + end + + + def collect_vendor_defines(in_hash) + return {:collection_defines_vendor => get_vendor_defines(in_hash)} + end + + + def collect_test_and_vendor_defines(in_hash) + defines = in_hash[:defines_test].clone + vendor_defines = get_vendor_defines(in_hash) + defines.concat(vendor_defines) if vendor_defines + + return {:collection_defines_test_and_vendor => defines} + end + + + def collect_release_and_vendor_defines(in_hash) + release_defines = in_hash[:defines_release].clone + + release_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions]) + + return {:collection_defines_release_and_vendor => release_defines} + end + + + def collect_release_artifact_extra_link_objects(in_hash) + objects = [] + + # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime) + objects << CEXCEPTION_C_FILE.ext( in_hash[:extension_object] ) if (in_hash[:project_use_exceptions]) + + return {:collection_release_artifact_extra_link_objects => objects} + end + + + def collect_test_fixture_extra_link_objects(in_hash) + # Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration; + # we also handle those dependencies elsewhere in compilation dependencies + + sources = [UNITY_C_FILE] + + in_hash[:files_support].each { |file| sources << file } + + # we don't include paths here because use of plugins or mixing different compilers may require different build paths + sources << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions]) + sources << CMOCK_C_FILE if (in_hash[:project_use_mocks]) + + # if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros), + # then link in the unity_helper object file too + if ( in_hash[:project_use_mocks] and in_hash[:cmock_unity_helper] ) + in_hash[:cmock_unity_helper].each do |helper| + if @file_wrapper.exist?(helper.ext(in_hash[:extension_source])) + sources << helper + end + end + end + + # create object files from all the sources + objects = sources.map { |file| File.basename(file) } + + # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime) + objects.map! { |object| object.ext(in_hash[:extension_object]) } + + return { :collection_all_support => sources, + :collection_test_fixture_extra_link_objects => objects + } + end + + + private + + def get_vendor_paths(in_hash) + vendor_paths = [] + vendor_paths << File.join(in_hash[:unity_vendor_path], UNITY_LIB_PATH) + vendor_paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions]) + vendor_paths << File.join(in_hash[:cmock_vendor_path], CMOCK_LIB_PATH) if (in_hash[:project_use_mocks]) + vendor_paths << in_hash[:cmock_mock_path] if (in_hash[:project_use_mocks]) + + return vendor_paths + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/configurator_plugins.rb b/CAN_App/vendor/ceedling/lib/ceedling/configurator_plugins.rb new file mode 100644 index 0000000..75bcd98 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/configurator_plugins.rb @@ -0,0 +1,131 @@ +require 'ceedling/constants' + +class ConfiguratorPlugins + + constructor :stream_wrapper, :file_wrapper, :system_wrapper + attr_reader :rake_plugins, :script_plugins + + def setup + @rake_plugins = [] + @script_plugins = [] + end + + + def add_load_paths(config) + plugin_paths = {} + + config[:plugins][:enabled].each do |plugin| + config[:plugins][:load_paths].each do |root| + path = File.join(root, plugin) + + is_script_plugin = ( not @file_wrapper.directory_listing( File.join( path, 'lib', '*.rb' ) ).empty? ) + is_rake_plugin = ( not @file_wrapper.directory_listing( File.join( path, '*.rake' ) ).empty? ) + + if is_script_plugin or is_rake_plugin + plugin_paths[(plugin + '_path').to_sym] = path + + if is_script_plugin + @system_wrapper.add_load_path( File.join( path, 'lib') ) + @system_wrapper.add_load_path( File.join( path, 'config') ) + end + break + end + end + end + + return plugin_paths + end + + + # gather up and return .rake filepaths that exist on-disk + def find_rake_plugins(config, plugin_paths) + @rake_plugins = [] + plugins_with_path = [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + rake_plugin_path = File.join(path, "#{plugin}.rake") + if (@file_wrapper.exist?(rake_plugin_path)) + plugins_with_path << rake_plugin_path + @rake_plugins << plugin + end + end + end + + return plugins_with_path + end + + + # gather up and return just names of .rb classes that exist on-disk + def find_script_plugins(config, plugin_paths) + @script_plugins = [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + script_plugin_path = File.join(path, "lib", "#{plugin}.rb") + + if @file_wrapper.exist?(script_plugin_path) + @script_plugins << plugin + end + end + end + + return @script_plugins + end + + + # gather up and return configuration .yml filepaths that exist on-disk + def find_config_plugins(config, plugin_paths) + plugins_with_path = [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + config_plugin_path = File.join(path, "config", "#{plugin}.yml") + + if @file_wrapper.exist?(config_plugin_path) + plugins_with_path << config_plugin_path + end + end + end + + return plugins_with_path + end + + + # gather up and return default .yml filepaths that exist on-disk + def find_plugin_yml_defaults(config, plugin_paths) + defaults_with_path = [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + default_path = File.join(path, 'config', 'defaults.yml') + + if @file_wrapper.exist?(default_path) + defaults_with_path << default_path + end + end + end + + return defaults_with_path + end + + # gather up and return + def find_plugin_hash_defaults(config, plugin_paths) + defaults_hash= [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + default_path = File.join(path, "config", "defaults_#{plugin}.rb") + if @file_wrapper.exist?(default_path) + @system_wrapper.require_file( "defaults_#{plugin}.rb") + + object = eval("get_default_config()") + defaults_hash << object + end + end + end + + return defaults_hash + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/configurator_setup.rb b/CAN_App/vendor/ceedling/lib/ceedling/configurator_setup.rb new file mode 100644 index 0000000..c43bb5c --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/configurator_setup.rb @@ -0,0 +1,128 @@ + +# add sort-ability to symbol so we can order keys array in hash for test-ability +class Symbol + include Comparable + + def <=>(other) + self.to_s <=> other.to_s + end +end + + +class ConfiguratorSetup + + constructor :configurator_builder, :configurator_validator, :configurator_plugins, :stream_wrapper + + + def build_project_config(config, flattened_config) + ### flesh out config + @configurator_builder.clean(flattened_config) + + ### add to hash values we build up from configuration & file system contents + flattened_config.merge!(@configurator_builder.set_build_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.set_force_build_filepaths(flattened_config)) + flattened_config.merge!(@configurator_builder.set_rakefile_components(flattened_config)) + flattened_config.merge!(@configurator_builder.set_release_target(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_project_options(flattened_config)) + + ### iterate through all entries in paths section and expand any & all globs to actual paths + flattened_config.merge!(@configurator_builder.expand_all_path_globs(flattened_config)) + + flattened_config.merge!(@configurator_builder.collect_vendor_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_source_and_include_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_source_include_vendor_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_test_support_source_include_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_test_support_source_include_vendor_paths(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_tests(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_assembly(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_source(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_headers(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_release_existing_compilation_input(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_vendor_defines(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_release_artifact_extra_link_objects(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_test_fixture_extra_link_objects(flattened_config)) + + return flattened_config + end + + + def build_constants_and_accessors(config, context) + @configurator_builder.build_global_constants(config) + @configurator_builder.build_accessor_methods(config, context) + end + + + def validate_required_sections(config) + validation = [] + validation << @configurator_validator.exists?(config, :project) + validation << @configurator_validator.exists?(config, :paths) + + return false if (validation.include?(false)) + return true + end + + def validate_required_section_values(config) + validation = [] + validation << @configurator_validator.exists?(config, :project, :build_root) + validation << @configurator_validator.exists?(config, :paths, :test) + validation << @configurator_validator.exists?(config, :paths, :source) + + return false if (validation.include?(false)) + return true + end + + def validate_paths(config) + validation = [] + + if config[:cmock][:unity_helper] + config[:cmock][:unity_helper].each do |path| + validation << @configurator_validator.validate_filepath_simple( path, :cmock, :unity_helper ) + end + end + + config[:project][:options_paths].each do |path| + validation << @configurator_validator.validate_filepath_simple( path, :project, :options_paths ) + end + + config[:plugins][:load_paths].each do |path| + validation << @configurator_validator.validate_filepath_simple( path, :plugins, :load_paths ) + end + + config[:paths].keys.sort.each do |key| + validation << @configurator_validator.validate_path_list(config, :paths, key) + end + + return false if (validation.include?(false)) + return true + end + + def validate_tools(config) + validation = [] + + config[:tools].keys.sort.each do |key| + validation << @configurator_validator.exists?(config, :tools, key, :executable) + validation << @configurator_validator.validate_executable_filepath(config, :tools, key, :executable) if (not config[:tools][key][:optional]) + validation << @configurator_validator.validate_tool_stderr_redirect(config, :tools, key) + end + + return false if (validation.include?(false)) + return true + end + + def validate_plugins(config) + missing_plugins = + Set.new( config[:plugins][:enabled] ) - + Set.new( @configurator_plugins.rake_plugins ) - + Set.new( @configurator_plugins.script_plugins ) + + missing_plugins.each do |plugin| + @stream_wrapper.stderr_puts("ERROR: Ceedling plugin '#{plugin}' contains no rake or ruby class entry point. (Misspelled or missing files?)") + end + + return ( (missing_plugins.size > 0) ? false : true ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/configurator_validator.rb b/CAN_App/vendor/ceedling/lib/ceedling/configurator_validator.rb new file mode 100644 index 0000000..fc02101 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/configurator_validator.rb @@ -0,0 +1,193 @@ +require 'rubygems' +require 'rake' # for ext() +require 'ceedling/constants' +require 'ceedling/tool_executor' # for argument replacement pattern +require 'ceedling/file_path_utils' # for glob handling class methods + + +class ConfiguratorValidator + + constructor :file_wrapper, :stream_wrapper, :system_wrapper + + # walk into config hash verify existence of data at key depth + def exists?(config, *keys) + hash = retrieve_value(config, keys) + exist = !hash[:value].nil? + + if (not exist) + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Required config file entry #{format_key_sequence(keys, hash[:depth])} does not exist.") + end + + return exist + end + + + # walk into config hash. verify directory path(s) at given key depth + def validate_path_list(config, *keys) + hash = retrieve_value(config, keys) + list = hash[:value] + + # return early if we couldn't walk into hash and find a value + return false if (list.nil?) + + path_list = [] + exist = true + + case list + when String then path_list << list + when Array then path_list = list + end + + path_list.each do |path| + base_path = FilePathUtils::extract_path(path) # lop off add/subtract notation & glob specifiers + + if (not @file_wrapper.exist?(base_path)) + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Config path #{format_key_sequence(keys, hash[:depth])}['#{base_path}'] does not exist on disk.") + exist = false + end + end + + return exist + end + + + # simple path verification + def validate_filepath_simple(path, *keys) + validate_path = path + + if (not @file_wrapper.exist?(validate_path)) + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Config path '#{validate_path}' associated with #{format_key_sequence(keys, keys.size)} does not exist on disk.") + return false + end + + return true + end + + # walk into config hash. verify specified file exists. + def validate_filepath(config, *keys) + hash = retrieve_value(config, keys) + filepath = hash[:value] + + # return early if we couldn't walk into hash and find a value + return false if (filepath.nil?) + + # skip everything if we've got an argument replacement pattern + return true if (filepath =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN) + + if (not @file_wrapper.exist?(filepath)) + + # See if we can deal with it internally. + if GENERATED_DIR_PATH.include?(filepath) + # we already made this directory before let's make it again. + FileUtils.mkdir_p File.join(File.dirname(__FILE__), filepath) + @stream_wrapper.stderr_puts("WARNING: Generated filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk. Recreating") + + else + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.") + return false + end + end + + return true + end + + # walk into config hash. verify specified file exists. + def validate_executable_filepath(config, *keys) + exe_extension = config[:extension][:executable] + hash = retrieve_value(config, keys) + filepath = hash[:value] + + # return early if we couldn't walk into hash and find a value + return false if (filepath.nil?) + + # skip everything if we've got an argument replacement pattern + return true if (filepath =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN) + + # if there's no path included, verify file exists somewhere in system search paths + if (not filepath.include?('/')) + exists = false + + @system_wrapper.search_paths.each do |path| + if (@file_wrapper.exist?( File.join(path, filepath)) ) + exists = true + break + end + + if (@file_wrapper.exist?( (File.join(path, filepath)).ext( exe_extension ) )) + exists = true + break + elsif (@system_wrapper.windows? and @file_wrapper.exist?( (File.join(path, filepath)).ext( EXTENSION_WIN_EXE ) )) + exists = true + break + end + end + + if (not exists) + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist in system search paths.") + return false + end + + # if there is a path included, check that explicit filepath exists + else + if (not @file_wrapper.exist?(filepath)) + # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator + @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.") + return false + end + end + + return true + end + + def validate_tool_stderr_redirect(config, tools, tool) + redirect = config[tools][tool][:stderr_redirect] + if (redirect.class == Symbol) + # map constants and force to array of strings for runtime universality across ruby versions + if (not StdErrRedirect.constants.map{|constant| constant.to_s}.include?(redirect.to_s.upcase)) + error = "ERROR: [:#{tools}][:#{tool}][:stderr_redirect][:#{redirect}] is not a recognized option " + + "{#{StdErrRedirect.constants.map{|constant| ':' + constant.to_s.downcase}.join(', ')}}." + @stream_wrapper.stderr_puts(error) + return false + end + end + + return true + end + + private ######################################### + + + def retrieve_value(config, keys) + value = nil + hash = config + depth = 0 + + # walk into hash & extract value at requested key sequence + keys.each do |symbol| + depth += 1 + if (not hash[symbol].nil?) + hash = hash[symbol] + value = hash + else + value = nil + break + end + end + + return {:value => value, :depth => depth} + end + + + def format_key_sequence(keys, depth) + walked_keys = keys.slice(0, depth) + formatted_keys = walked_keys.map{|key| "[:#{key.to_s}]"} + + return formatted_keys.join + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/constants.rb b/CAN_App/vendor/ceedling/lib/ceedling/constants.rb new file mode 100644 index 0000000..19484f0 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/constants.rb @@ -0,0 +1,99 @@ + +class Verbosity + SILENT = 0 # as silent as possible (though there are some messages that must be spit out) + ERRORS = 1 # only errors + COMPLAIN = 2 # spit out errors and warnings/notices + NORMAL = 3 # errors, warnings/notices, standard status messages + OBNOXIOUS = 4 # all messages including extra verbose output (used for lite debugging / verification) + DEBUG = 5 # special extra verbose output for hardcore debugging +end + + +class TestResultsSanityChecks + NONE = 0 # no sanity checking of test results + NORMAL = 1 # perform non-problematic checks + THOROUGH = 2 # perform checks that require inside knowledge of system workings +end + + +class StdErrRedirect + NONE = :none + AUTO = :auto + WIN = :win + UNIX = :unix + TCSH = :tcsh +end + + +class BackgroundExec + NONE = :none + AUTO = :auto + WIN = :win + UNIX = :unix +end + +unless defined?(PROJECT_ROOT) + PROJECT_ROOT = Dir.pwd() +end + +GENERATED_DIR_PATH = [['vendor', 'ceedling'], 'src', "test", ['test', 'support'], 'build'].each{|p| File.join(*p)} + +EXTENSION_WIN_EXE = '.exe' +EXTENSION_NONWIN_EXE = '.out' + + +CEXCEPTION_ROOT_PATH = 'c_exception' +CEXCEPTION_LIB_PATH = "#{CEXCEPTION_ROOT_PATH}/lib" +CEXCEPTION_C_FILE = 'CException.c' +CEXCEPTION_H_FILE = 'CException.h' + +UNITY_ROOT_PATH = 'unity' +UNITY_LIB_PATH = "#{UNITY_ROOT_PATH}/src" +UNITY_C_FILE = 'unity.c' +UNITY_H_FILE = 'unity.h' +UNITY_INTERNALS_H_FILE = 'unity_internals.h' + +CMOCK_ROOT_PATH = 'cmock' +CMOCK_LIB_PATH = "#{CMOCK_ROOT_PATH}/src" +CMOCK_C_FILE = 'cmock.c' +CMOCK_H_FILE = 'cmock.h' + + +DEFAULT_CEEDLING_MAIN_PROJECT_FILE = 'project.yml' unless defined?(DEFAULT_CEEDLING_MAIN_PROJECT_FILE) # main project file +DEFAULT_CEEDLING_USER_PROJECT_FILE = 'user.yml' unless defined?(DEFAULT_CEEDLING_USER_PROJECT_FILE) # supplemental user config file + +INPUT_CONFIGURATION_CACHE_FILE = 'input.yml' unless defined?(INPUT_CONFIGURATION_CACHE_FILE) # input configuration file dump +DEFINES_DEPENDENCY_CACHE_FILE = 'defines_dependency.yml' unless defined?(DEFINES_DEPENDENCY_CACHE_FILE) # preprocessor definitions for files + +TEST_ROOT_NAME = 'test' unless defined?(TEST_ROOT_NAME) +TEST_TASK_ROOT = TEST_ROOT_NAME + ':' unless defined?(TEST_TASK_ROOT) +TEST_SYM = TEST_ROOT_NAME.to_sym unless defined?(TEST_SYM) + +RELEASE_ROOT_NAME = 'release' unless defined?(RELEASE_ROOT_NAME) +RELEASE_TASK_ROOT = RELEASE_ROOT_NAME + ':' unless defined?(RELEASE_TASK_ROOT) +RELEASE_SYM = RELEASE_ROOT_NAME.to_sym unless defined?(RELEASE_SYM) + +REFRESH_ROOT_NAME = 'refresh' unless defined?(REFRESH_ROOT_NAME) +REFRESH_TASK_ROOT = REFRESH_ROOT_NAME + ':' unless defined?(REFRESH_TASK_ROOT) +REFRESH_SYM = REFRESH_ROOT_NAME.to_sym unless defined?(REFRESH_SYM) + +UTILS_ROOT_NAME = 'utils' unless defined?(UTILS_ROOT_NAME) +UTILS_TASK_ROOT = UTILS_ROOT_NAME + ':' unless defined?(UTILS_TASK_ROOT) +UTILS_SYM = UTILS_ROOT_NAME.to_sym unless defined?(UTILS_SYM) + +OPERATION_COMPILE_SYM = :compile unless defined?(OPERATION_COMPILE_SYM) +OPERATION_ASSEMBLE_SYM = :assemble unless defined?(OPERATION_ASSEMBLE_SYM) +OPERATION_LINK_SYM = :link unless defined?(OPERATION_LINK_SYM) + + +RUBY_STRING_REPLACEMENT_PATTERN = /#\{.+\}/ +RUBY_EVAL_REPLACEMENT_PATTERN = /^\{(.+)\}$/ +TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN = /(\$\{(\d+)\})/ +TEST_STDOUT_STATISTICS_PATTERN = /\n-+\s*(\d+)\s+Tests\s+(\d+)\s+Failures\s+(\d+)\s+Ignored\s+(OK|FAIL)\s*/i + +NULL_FILE_PATH = '/dev/null' + +TESTS_BASE_PATH = TEST_ROOT_NAME +RELEASE_BASE_PATH = RELEASE_ROOT_NAME + +VENDORS_FILES = %w(unity UnityHelper cmock CException).freeze diff --git a/CAN_App/vendor/ceedling/lib/ceedling/defaults.rb b/CAN_App/vendor/ceedling/lib/ceedling/defaults.rb new file mode 100644 index 0000000..1300a1a --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/defaults.rb @@ -0,0 +1,471 @@ +require 'ceedling/constants' +require 'ceedling/system_wrapper' +require 'ceedling/file_path_utils' + +#this should be defined already, but not always during system specs +CEEDLING_VENDOR = File.expand_path(File.dirname(__FILE__) + '/../../vendor') unless defined? CEEDLING_VENDOR +CEEDLING_PLUGINS = [] unless defined? CEEDLING_PLUGINS + +DEFAULT_TEST_COMPILER_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_test_compiler'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + "-DGNU_COMPILER".freeze, + "-g".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + "-c \"${1}\"".freeze, + "-o \"${2}\"".freeze, + # gcc's list file output options are complex; no use of ${3} parameter in default config + "-MMD".freeze, + "-MF \"${4}\"".freeze, + ].freeze + } + +DEFAULT_TEST_LINKER_TOOL = { + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], + :name => 'default_test_linker'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, + "\"${1}\"".freeze, + "${5}".freeze, + "-o \"${2}\"".freeze, + "".freeze, + "${4}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split + ].freeze + } + +DEFAULT_TEST_FIXTURE_TOOL = { + :executable => '${1}'.freeze, + :name => 'default_test_fixture'.freeze, + :stderr_redirect => StdErrRedirect::AUTO.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [].freeze + } + +DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_test_includes_preprocessor'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + '-E'.freeze, # OSX clang + '-MM'.freeze, + '-MG'.freeze, + # avoid some possibility of deep system lib header file complications by omitting vendor paths + # if cpp is run on *nix system, escape spaces in paths; if cpp on windows just use the paths collection as is + # {"-I\"$\"" => "{SystemWrapper.windows? ? COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE : COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE.map{|path| path.gsub(\/ \/, \'\\\\ \') }}"}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, # OSX clang + # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX + "\"${1}\"".freeze + ].freeze + } + +DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_test_file_preprocessor'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + '-E'.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, + # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX + "\"${1}\"".freeze, + "-o \"${2}\"".freeze + ].freeze + } + +DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL = { + :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :name => 'default_test_file_preprocessor_directives'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + '-E'.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, + '-fdirectives-only'.freeze, + # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX + "\"${1}\"".freeze, + "-o \"${2}\"".freeze + ].freeze + } + +# Disable the -MD flag for OSX LLVM Clang, since unsupported +if RUBY_PLATFORM =~ /darwin/ && `gcc --version 2> /dev/null` =~ /Apple LLVM version .* \(clang/m # OSX w/LLVM Clang + MD_FLAG = '' # Clang doesn't support the -MD flag +else + MD_FLAG = '-MD' +end + +DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_test_dependencies_generator'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + '-E'.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, + "-MT \"${3}\"".freeze, + '-MM'.freeze, + MD_FLAG.freeze, + '-MG'.freeze, + "-MF \"${2}\"".freeze, + "-c \"${1}\"".freeze, + # '-nostdinc'.freeze, + ].freeze + } + +DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_release_dependencies_generator'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + '-E'.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_RELEASE_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, + "-MT \"${3}\"".freeze, + '-MM'.freeze, + MD_FLAG.freeze, + '-MG'.freeze, + "-MF \"${2}\"".freeze, + "-c \"${1}\"".freeze, + # '-nostdinc'.freeze, + ].freeze + } + + +DEFAULT_RELEASE_COMPILER_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_release_compiler'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze, + "-DGNU_COMPILER".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + "-c \"${1}\"".freeze, + "-o \"${2}\"".freeze, + # gcc's list file output options are complex; no use of ${3} parameter in default config + "-MMD".freeze, + "-MF \"${4}\"".freeze, + ].freeze + } + +DEFAULT_RELEASE_ASSEMBLER_TOOL = { + :executable => ENV['AS'].nil? ? FilePathUtils.os_executable_ext('as').freeze : ENV['AS'].split[0], + :name => 'default_release_assembler'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['AS'].nil? ? "" : ENV['AS'].split[1..-1], + ENV['ASFLAGS'].nil? ? "" : ENV['ASFLAGS'].split, + {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_AND_INCLUDE'}.freeze, + "\"${1}\"".freeze, + "-o \"${2}\"".freeze, + ].freeze + } + +DEFAULT_RELEASE_LINKER_TOOL = { + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], + :name => 'default_release_linker'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, + "\"${1}\"".freeze, + "${5}".freeze, + "-o \"${2}\"".freeze, + "".freeze, + "${4}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split + ].freeze + } + + +DEFAULT_TOOLS_TEST = { + :tools => { + :test_compiler => DEFAULT_TEST_COMPILER_TOOL, + :test_linker => DEFAULT_TEST_LINKER_TOOL, + :test_fixture => DEFAULT_TEST_FIXTURE_TOOL, + } + } + +DEFAULT_TOOLS_TEST_PREPROCESSORS = { + :tools => { + :test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL, + :test_file_preprocessor => DEFAULT_TEST_FILE_PREPROCESSOR_TOOL, + :test_file_preprocessor_directives => DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL, + } + } + +DEFAULT_TOOLS_TEST_DEPENDENCIES = { + :tools => { + :test_dependencies_generator => DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL, + } + } + + +DEFAULT_TOOLS_RELEASE = { + :tools => { + :release_compiler => DEFAULT_RELEASE_COMPILER_TOOL, + :release_linker => DEFAULT_RELEASE_LINKER_TOOL, + } + } + +DEFAULT_TOOLS_RELEASE_ASSEMBLER = { + :tools => { + :release_assembler => DEFAULT_RELEASE_ASSEMBLER_TOOL, + } + } + +DEFAULT_TOOLS_RELEASE_DEPENDENCIES = { + :tools => { + :release_dependencies_generator => DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL, + } + } + + +DEFAULT_RELEASE_TARGET_NAME = 'project' + +DEFAULT_CEEDLING_CONFIG = { + :project => { + # :build_root must be set by user + :use_exceptions => true, + :use_mocks => true, + :compile_threads => 1, + :test_threads => 1, + :use_test_preprocessor => false, + :use_preprocessor_directives => false, + :use_deep_dependencies => false, + :generate_deep_dependencies => true, # only applicable if use_deep_dependencies is true + :auto_link_deep_dependencies => false, + :test_file_prefix => 'test_', + :options_paths => [], + :release_build => false, + }, + + :release_build => { + # :output is set while building configuration -- allows smart default system-dependent file extension handling + :use_assembly => false, + :artifacts => [], + }, + + :paths => { + :test => [], # must be populated by user + :source => [], # must be populated by user + :support => [], + :include => [], + :libraries => [], + :test_toolchain_include => [], + :release_toolchain_include => [], + }, + + :files => { + :test => [], + :source => [], + :assembly => [], + :support => [], + :include => [], + }, + + # unlike other top-level entries, environment's value is an array to preserve order + :environment => [ + # when evaluated, this provides wider text field for rake task comments + {:rake_columns => '120'}, + ], + + :defines => { + :test => [], + :test_preprocess => [], + :release => [], + :release_preprocess => [], + :use_test_definition => false, + }, + + :libraries => { + :flag => '-l${1}', + :path_flag => '-L ${1}', + :test => [], + :test_preprocess => [], + :release => [], + :release_preprocess => [], + }, + + :flags => {}, + + :extension => { + :header => '.h', + :source => '.c', + :assembly => '.s', + :object => '.o', + :libraries => ['.a','.so'], + :executable => ( SystemWrapper.windows? ? EXTENSION_WIN_EXE : EXTENSION_NONWIN_EXE ), + :map => '.map', + :list => '.lst', + :testpass => '.pass', + :testfail => '.fail', + :dependencies => '.d', + }, + + :unity => { + :vendor_path => CEEDLING_VENDOR, + :defines => [] + }, + + :cmock => { + :vendor_path => CEEDLING_VENDOR, + :defines => [], + :includes => [] + }, + + :cexception => { + :vendor_path => CEEDLING_VENDOR, + :defines => [] + }, + + :test_runner => { + :includes => [], + :file_suffix => '_runner', + }, + + # all tools populated while building up config structure + :tools => {}, + + # empty argument lists for default tools + # (these can be overridden in project file to add arguments to tools without totally redefining tools) + :test_compiler => { :arguments => [] }, + :test_linker => { :arguments => [] }, + :test_fixture => { + :arguments => [], + :link_objects => [], # compiled object files to always be linked in (e.g. cmock.o if using mocks) + }, + :test_includes_preprocessor => { :arguments => [] }, + :test_file_preprocessor => { :arguments => [] }, + :test_file_preprocessor_directives => { :arguments => [] }, + :test_dependencies_generator => { :arguments => [] }, + :release_compiler => { :arguments => [] }, + :release_linker => { :arguments => [] }, + :release_assembler => { :arguments => [] }, + :release_dependencies_generator => { :arguments => [] }, + + :plugins => { + :load_paths => CEEDLING_PLUGINS, + :enabled => [], + } + }.freeze + + +DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{ +% ignored = hash[:results][:counts][:ignored] +% failed = hash[:results][:counts][:failed] +% stdout_count = hash[:results][:counts][:stdout] +% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '') +% banner_width = 25 + header_prepend.length # widest message + +% if (stdout_count > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'TEST OUTPUT')%> +% hash[:results][:stdout].each do |string| +% string[:collection].each do |item| +<%=string[:source][:path]%><%=File::SEPARATOR%><%=string[:source][:file]%>: "<%=item%>" +% end +% end + +% end +% if (ignored > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'IGNORED TEST SUMMARY')%> +% hash[:results][:ignores].each do |ignore| +% ignore[:collection].each do |item| +<%=ignore[:source][:path]%><%=File::SEPARATOR%><%=ignore[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%> +% if (item[:message].length > 0) +: "<%=item[:message]%>" +% else +<%="\n"%> +% end +% end +% end + +% end +% if (failed > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'FAILED TEST SUMMARY')%> +% hash[:results][:failures].each do |failure| +% failure[:collection].each do |item| +<%=failure[:source][:path]%><%=File::SEPARATOR%><%=failure[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%> +% if (item[:message].length > 0) +: "<%=item[:message]%>" +% else +<%="\n"%> +% end +% end +% end + +% end +% total_string = hash[:results][:counts][:total].to_s +% format_string = "%#{total_string.length}i" +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'OVERALL TEST SUMMARY')%> +% if (hash[:results][:counts][:total] > 0) +TESTED: <%=hash[:results][:counts][:total].to_s%> +PASSED: <%=sprintf(format_string, hash[:results][:counts][:passed])%> +FAILED: <%=sprintf(format_string, failed)%> +IGNORED: <%=sprintf(format_string, ignored)%> +% else + +No tests executed. +% end + +} diff --git a/CAN_App/vendor/ceedling/lib/ceedling/dependinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/dependinator.rb new file mode 100644 index 0000000..accfe80 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/dependinator.rb @@ -0,0 +1,97 @@ + +class Dependinator + + constructor :configurator, :project_config_manager, :test_includes_extractor, :file_path_utils, :rake_wrapper, :file_wrapper + + def touch_force_rebuild_files + @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath ) + @file_wrapper.touch( @configurator.project_release_force_rebuild_filepath ) if (@configurator.project_release_build) + end + + + + def load_release_object_deep_dependencies(dependencies_list) + dependencies_list.each do |dependencies_file| + if File.exists?(dependencies_file) + @rake_wrapper.load_dependencies( dependencies_file ) + end + end + end + + + def enhance_release_file_dependencies(files) + files.each do |filepath| + @rake_wrapper[filepath].enhance( [@configurator.project_release_force_rebuild_filepath] ) if (@project_config_manager.release_config_changed) + end + end + + + + def load_test_object_deep_dependencies(files_list) + dependencies_list = @file_path_utils.form_test_dependencies_filelist(files_list) + dependencies_list.each do |dependencies_file| + if File.exists?(dependencies_file) + @rake_wrapper.load_dependencies(dependencies_file) + end + end + end + + + def enhance_runner_dependencies(runner_filepath) + @rake_wrapper[runner_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + end + + + def enhance_shallow_include_lists_dependencies(include_lists) + include_lists.each do |include_list_filepath| + @rake_wrapper[include_list_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + end + end + + + def enhance_preprocesed_file_dependencies(files) + files.each do |filepath| + @rake_wrapper[filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + end + end + + + def enhance_mock_dependencies(mocks_list) + # if input configuration or ceedling changes, make sure these guys get rebuilt + mocks_list.each do |mock_filepath| + @rake_wrapper[mock_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + @rake_wrapper[mock_filepath].enhance( @configurator.cmock_unity_helper ) if (@configurator.cmock_unity_helper) + end + end + + + def enhance_dependencies_dependencies(dependencies) + dependencies.each do |dependencies_filepath| + @rake_wrapper[dependencies_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + end + end + + + def enhance_test_build_object_dependencies(objects) + objects.each do |object_filepath| + @rake_wrapper[object_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || + @project_config_manager.test_defines_changed) + end + end + + + def enhance_results_dependencies(result_filepath) + @rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if @project_config_manager.test_config_changed + end + + + def enhance_test_executable_dependencies(test, objects) + @rake_wrapper[ @file_path_utils.form_test_executable_filepath(test) ].enhance( objects ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/erb_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/erb_wrapper.rb new file mode 100644 index 0000000..8d70b6d --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/erb_wrapper.rb @@ -0,0 +1,9 @@ +require 'erb' + +class ErbWrapper + def generate_file(template, data, output_file) + File.open(output_file, "w") do |f| + f << ERB.new(template, 0, "<>").result(binding) + end + end +end \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_finder.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_finder.rb new file mode 100644 index 0000000..53775b7 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_finder.rb @@ -0,0 +1,149 @@ +require 'rubygems' +require 'rake' # for adding ext() method to string +require 'thread' + + +class FileFinder + SEMAPHORE = Mutex.new + + constructor :configurator, :file_finder_helper, :cacheinator, :file_path_utils, :file_wrapper, :yaml_wrapper + + def prepare_search_sources + @all_test_source_and_header_file_collection = + @configurator.collection_all_tests + + @configurator.collection_all_source + + @configurator.collection_all_headers + end + + + def find_header_file(mock_file) + header = File.basename(mock_file).sub(/#{@configurator.cmock_mock_prefix}/, '').ext(@configurator.extension_header) + + found_path = @file_finder_helper.find_file_in_collection(header, @configurator.collection_all_headers, :error) + + return found_path + end + + + def find_header_input_for_mock_file(mock_file) + found_path = find_header_file(mock_file) + mock_input = found_path + + if (@configurator.project_use_test_preprocessor) + mock_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) ) + end + + return mock_input + end + + + def find_source_from_test(test, complain) + test_prefix = @configurator.project_test_file_prefix + source_paths = @configurator.collection_all_source + + source = File.basename(test).sub(/#{test_prefix}/, '') + + # we don't blow up if a test file has no corresponding source file + return @file_finder_helper.find_file_in_collection(source, source_paths, complain) + end + + + def find_test_from_runner_path(runner_path) + extension_source = @configurator.extension_source + + test_file = File.basename(runner_path).sub(/#{@configurator.test_runner_file_suffix}#{'\\'+extension_source}/, extension_source) + + found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error) + + return found_path + end + + + def find_test_input_for_runner_file(runner_path) + found_path = find_test_from_runner_path(runner_path) + runner_input = found_path + + if (@configurator.project_use_test_preprocessor) + runner_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) ) + end + + return runner_input + end + + + def find_test_from_file_path(file_path) + test_file = File.basename(file_path).ext(@configurator.extension_source) + + found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error) + + return found_path + end + + + def find_test_or_source_or_header_file(file_path) + file = File.basename(file_path) + return @file_finder_helper.find_file_in_collection(file, @all_test_source_and_header_file_collection, :error) + end + + + def find_compilation_input_file(file_path, complain=:error, release=false) + found_file = nil + + source_file = File.basename(file_path).ext(@configurator.extension_source) + + # We only collect files that already exist when we start up. + # FileLists can produce undesired results for dynamically generated files depending on when they're accessed. + # So collect mocks and runners separately and right now. + + SEMAPHORE.synchronize { + + if (source_file =~ /#{@configurator.test_runner_file_suffix}/) + found_file = + @file_finder_helper.find_file_in_collection( + source_file, + @file_wrapper.directory_listing( File.join(@configurator.project_test_runners_path, '*') ), + complain) + + elsif (@configurator.project_use_mocks and (source_file =~ /#{@configurator.cmock_mock_prefix}/)) + found_file = + @file_finder_helper.find_file_in_collection( + source_file, + @file_wrapper.directory_listing( File.join(@configurator.cmock_mock_path, '*') ), + complain) + + elsif release + found_file = + @file_finder_helper.find_file_in_collection( + source_file, + @configurator.collection_release_existing_compilation_input, + complain) + else + temp_complain = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) ? :ignore : complain + found_file = + @file_finder_helper.find_file_in_collection( + source_file, + @configurator.collection_all_existing_compilation_input, + temp_complain) + found_file ||= find_assembly_file(file_path, false) if (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) + end + } + return found_file + end + + + def find_source_file(file_path, complain) + source_file = File.basename(file_path).ext(@configurator.extension_source) + return @file_finder_helper.find_file_in_collection(source_file, @configurator.collection_all_source, complain) + end + + + def find_assembly_file(file_path, complain = :error) + assembly_file = File.basename(file_path).ext(@configurator.extension_assembly) + return @file_finder_helper.find_file_in_collection(assembly_file, @configurator.collection_all_assembly, complain) + end + + def find_file_from_list(file_path, file_list, complain) + return @file_finder_helper.find_file_in_collection(file_path, file_list, complain) + end +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_finder_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_finder_helper.rb new file mode 100644 index 0000000..a168e5c --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_finder_helper.rb @@ -0,0 +1,56 @@ +require 'fileutils' +require 'ceedling/constants' # for Verbosity enumeration + +class FileFinderHelper + + constructor :streaminator + + + def find_file_in_collection(file_name, file_list, complain, extra_message="") + file_to_find = nil + + file_list.each do |item| + base_file = File.basename(item) + + # case insensitive comparison + if (base_file.casecmp(file_name) == 0) + # case sensitive check + if (base_file == file_name) + file_to_find = item + break + else + blow_up(file_name, "However, a filename having different capitalization was found: '#{item}'.") + end + end + + end + + if file_to_find.nil? + case (complain) + when :error then blow_up(file_name, extra_message) + when :warn then gripe(file_name, extra_message) + #when :ignore then + end + end + + return file_to_find + end + + private + + def blow_up(file_name, extra_message="") + error = "ERROR: Found no file '#{file_name}' in search paths." + error += ' ' if (extra_message.length > 0) + @streaminator.stderr_puts(error + extra_message, Verbosity::ERRORS) + raise + end + + def gripe(file_name, extra_message="") + warning = "WARNING: Found no file '#{file_name}' in search paths." + warning += ' ' if (extra_message.length > 0) + @streaminator.stderr_puts(warning + extra_message, Verbosity::COMPLAIN) + end + +end + + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_path_utils.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_path_utils.rb new file mode 100644 index 0000000..89a28ba --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_path_utils.rb @@ -0,0 +1,202 @@ +require 'rubygems' +require 'rake' # for ext() +require 'fileutils' +require 'ceedling/system_wrapper' + +# global utility methods (for plugins, project files, etc.) +def ceedling_form_filepath(destination_path, original_filepath, new_extension=nil) + filename = File.basename(original_filepath) + filename.replace(filename.ext(new_extension)) if (!new_extension.nil?) + return File.join( destination_path.gsub(/\\/, '/'), filename ) +end + +class FilePathUtils + + GLOB_MATCHER = /[\*\?\{\}\[\]]/ + + constructor :configurator, :file_wrapper + + + ######### class methods ########## + + # standardize path to use '/' path separator & have no trailing path separator + def self.standardize(path) + if path.is_a? String + path.strip! + path.gsub!(/\\/, '/') + path.chomp!('/') + end + return path + end + + def self.os_executable_ext(executable) + return executable.ext('.exe') if SystemWrapper.windows? + return executable + end + + # extract directory path from between optional add/subtract aggregation modifiers and up to glob specifiers + # note: slightly different than File.dirname in that /files/foo remains /files/foo and does not become /files + def self.extract_path(path) + path = path.sub(/^(\+|-):/, '') + + # find first occurrence of path separator followed by directory glob specifier: *, ?, {, }, [, ] + find_index = (path =~ GLOB_MATCHER) + + # no changes needed (lop off final path separator) + return path.chomp('/') if (find_index.nil?) + + # extract up to first glob specifier + path = path[0..(find_index-1)] + + # lop off everything up to and including final path separator + find_index = path.rindex('/') + return path[0..(find_index-1)] if (not find_index.nil?) + + # return string up to first glob specifier if no path separator found + return path + end + + # return whether the given path is to be aggregated (no aggregation modifier defaults to same as +:) + def self.add_path?(path) + return (path =~ /^-:/).nil? + end + + # get path (and glob) lopping off optional +: / -: prefixed aggregation modifiers + def self.extract_path_no_aggregation_operators(path) + return path.sub(/^(\+|-):/, '') + end + + # all the globs that may be in a path string work fine with one exception; + # to recurse through all subdirectories, the glob is dir/**/** but our paths use + # convention of only dir/** + def self.reform_glob(path) + return path if (path =~ /\/\*\*$/).nil? + return path + '/**' + end + + ######### instance methods ########## + + def form_temp_path(filepath, prefix='') + return File.join( @configurator.project_temp_path, prefix + File.basename(filepath) ) + end + + ### release ### + def form_release_build_cache_path(filepath) + return File.join( @configurator.project_release_build_cache_path, File.basename(filepath) ) + end + + def form_release_dependencies_filepath(filepath) + return File.join( @configurator.project_release_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) ) + end + + def form_release_build_c_object_filepath(filepath) + return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_object) ) + end + + def form_release_build_asm_object_filepath(filepath) + return File.join( @configurator.project_release_build_output_asm_path, File.basename(filepath).ext(@configurator.extension_object) ) + end + + def form_release_build_c_objects_filelist(files) + return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_build_output_c_path}/%n#{@configurator.extension_object}") + end + + def form_release_build_asm_objects_filelist(files) + return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_build_output_asm_path}/%n#{@configurator.extension_object}") + end + + def form_release_build_c_list_filepath(filepath) + return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_list) ) + end + + def form_release_dependencies_filelist(files) + return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_dependencies_path}/%n#{@configurator.extension_dependencies}") + end + + ### tests ### + def form_test_build_cache_path(filepath) + return File.join( @configurator.project_test_build_cache_path, File.basename(filepath) ) + end + + def form_test_dependencies_filepath(filepath) + return File.join( @configurator.project_test_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) ) + end + + def form_pass_results_filepath(filepath) + return File.join( @configurator.project_test_results_path, File.basename(filepath).ext(@configurator.extension_testpass) ) + end + + def form_fail_results_filepath(filepath) + return File.join( @configurator.project_test_results_path, File.basename(filepath).ext(@configurator.extension_testfail) ) + end + + def form_runner_filepath_from_test(filepath) + return File.join( @configurator.project_test_runners_path, File.basename(filepath, @configurator.extension_source)) + @configurator.test_runner_file_suffix + @configurator.extension_source + end + + def form_test_filepath_from_runner(filepath) + return filepath.sub(/#{TEST_RUNNER_FILE_SUFFIX}/, '') + end + + def form_runner_object_filepath_from_test(filepath) + return (form_test_build_c_object_filepath(filepath)).sub(/(#{@configurator.extension_object})$/, "#{@configurator.test_runner_file_suffix}\\1") + end + + def form_test_build_c_object_filepath(filepath) + return File.join( @configurator.project_test_build_output_c_path, File.basename(filepath).ext(@configurator.extension_object) ) + end + + def form_test_build_asm_object_filepath(filepath) + return File.join( @configurator.project_test_build_output_asm_path, File.basename(filepath).ext(@configurator.extension_object) ) + end + + def form_test_executable_filepath(filepath) + return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_executable) ) + end + + def form_test_build_map_filepath(filepath) + return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_map) ) + end + + def form_test_build_list_filepath(filepath) + return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_list) ) + end + + def form_preprocessed_file_filepath(filepath) + return File.join( @configurator.project_test_preprocess_files_path, File.basename(filepath) ) + end + + def form_preprocessed_includes_list_filepath(filepath) + return File.join( @configurator.project_test_preprocess_includes_path, File.basename(filepath) ) + end + + def form_test_build_objects_filelist(sources) + return (@file_wrapper.instantiate_file_list(sources)).pathmap("#{@configurator.project_test_build_output_c_path}/%n#{@configurator.extension_object}") + end + + def form_preprocessed_mockable_headers_filelist(mocks) + list = @file_wrapper.instantiate_file_list(mocks) + headers = list.map do |file| + module_name = File.basename(file).sub(/^#{@configurator.cmock_mock_prefix}/, '').sub(/\.[a-zA-Z]+$/,'') + "#{@configurator.project_test_preprocess_files_path}/#{module_name}#{@configurator.extension_header}" + end + return headers + end + + def form_mocks_source_filelist(mocks) + list = (@file_wrapper.instantiate_file_list(mocks)) + sources = list.map{|file| "#{@configurator.cmock_mock_path}/#{file}#{@configurator.extension_source}"} + return sources + end + + def form_test_dependencies_filelist(files) + list = @file_wrapper.instantiate_file_list(files) + return list.pathmap("#{@configurator.project_test_dependencies_path}/%n#{@configurator.extension_dependencies}") + end + + def form_pass_results_filelist(path, files) + list = @file_wrapper.instantiate_file_list(files) + return list.pathmap("#{path}/%n#{@configurator.extension_testpass}") + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_system_utils.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_system_utils.rb new file mode 100644 index 0000000..97e5856 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_system_utils.rb @@ -0,0 +1,69 @@ +require 'rubygems' +require 'rake' +require 'set' +require 'fileutils' +require 'ceedling/file_path_utils' + + +class FileSystemUtils + + constructor :file_wrapper + + # build up path list from input of one or more strings or arrays of (+/-) paths & globs + def collect_paths(*paths) + raw = [] # all paths and globs + plus = Set.new # all paths to expand and add + minus = Set.new # all paths to remove from plus set + + # assemble all globs and simple paths, reforming our glob notation to ruby globs + paths.each do |paths_container| + case (paths_container) + when String then raw << (FilePathUtils::reform_glob(paths_container)) + when Array then paths_container.each {|path| raw << (FilePathUtils::reform_glob(path))} + else raise "Don't know how to handle #{paths_container.class}" + end + end + + # iterate through each path and glob + raw.each do |path| + + dirs = [] # container for only (expanded) paths + + # if a glob, expand it and slurp up all non-file paths + if path.include?('*') + # grab base directory only if globs are snug up to final path separator + if (path =~ /\/\*+$/) + dirs << FilePathUtils.extract_path(path) + end + + # grab expanded sub-directory globs + expanded = @file_wrapper.directory_listing( FilePathUtils.extract_path_no_aggregation_operators(path) ) + expanded.each do |entry| + dirs << entry if @file_wrapper.directory?(entry) + end + + # else just grab simple path + # note: we could just run this through glob expansion but such an + # approach doesn't handle a path not yet on disk) + else + dirs << FilePathUtils.extract_path_no_aggregation_operators(path) + end + + # add dirs to the appropriate set based on path aggregation modifier if present + FilePathUtils.add_path?(path) ? plus.merge(dirs) : minus.merge(dirs) + end + + return (plus - minus).to_a.uniq + end + + + # given a file list, add to it or remove from it + def revise_file_list(list, revisions) + revisions.each do |revision| + # include or exclude file or glob to file list + file = FilePathUtils.extract_path_no_aggregation_operators( revision ) + FilePathUtils.add_path?(revision) ? list.include(file) : list.exclude(file) + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_system_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_system_wrapper.rb new file mode 100644 index 0000000..807cbd2 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_system_wrapper.rb @@ -0,0 +1,10 @@ + +class FileSystemWrapper + + def cd(path) + FileUtils.cd path do + yield + end + end + +end \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/lib/ceedling/file_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/file_wrapper.rb new file mode 100644 index 0000000..9e5a909 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/file_wrapper.rb @@ -0,0 +1,83 @@ +require 'rubygems' +require 'rake' # for FileList +require 'fileutils' +require 'ceedling/constants' + + +class FileWrapper + + def get_expanded_path(path) + return File.expand_path(path) + end + + def basename(path, extension=nil) + return File.basename(path, extension) if extension + return File.basename(path) + end + + def exist?(filepath) + return true if (filepath == NULL_FILE_PATH) + return File.exist?(filepath) + end + + def directory?(path) + return File.directory?(path) + end + + def dirname(path) + return File.dirname(path) + end + + def directory_listing(glob) + return Dir.glob(glob, File::FNM_PATHNAME) + end + + def rm_f(filepath, options={}) + FileUtils.rm_f(filepath, **options) + end + + def rm_r(filepath, options={}) + FileUtils.rm_r(filepath, **options={}) + end + + def cp(source, destination, options={}) + FileUtils.cp(source, destination, **options) + end + + def compare(from, to) + return FileUtils.compare_file(from, to) + end + + def open(filepath, flags) + File.open(filepath, flags) do |file| + yield(file) + end + end + + def read(filepath) + return File.read(filepath) + end + + def touch(filepath, options={}) + FileUtils.touch(filepath, **options) + end + + def write(filepath, contents, flags='w') + File.open(filepath, flags) do |file| + file.write(contents) + end + end + + def readlines(filepath) + return File.readlines(filepath) + end + + def instantiate_file_list(files=[]) + return FileList.new(files) + end + + def mkdir(folder) + return FileUtils.mkdir_p(folder) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/flaginator.rb b/CAN_App/vendor/ceedling/lib/ceedling/flaginator.rb new file mode 100644 index 0000000..31d62c4 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/flaginator.rb @@ -0,0 +1,74 @@ +require 'rubygems' +require 'rake' # for ext() +require 'fileutils' +require 'ceedling/constants' + + +# :flags: +# :release: +# :compile: +# :'test_.+' +# - -pedantic # add '-pedantic' to every test file +# :*: # add '-foo' to compilation of all files not main.c +# - -foo +# :main: # add '-Wall' to compilation of main.c +# - -Wall +# :test: +# :link: +# :test_main: # add '--bar --baz' to linking of test_main.exe +# - --bar +# - --baz + +def partition(hash, &predicate) + hash.partition(&predicate).map(&:to_h) +end + +class Flaginator + + constructor :configurator + + def get_flag(hash, file_name) + file_key = file_name.to_sym + + # 1. try literals + literals, magic = partition(hash) { |k, v| k.to_s =~ /^\w+$/ } + return literals[file_key] if literals.include?(file_key) + + any, regex = partition(magic) { |k, v| (k == :'*') || (k == :'.*') } # glob or regex wild card + + # 2. try regexes + find_res = regex.find { |k, v| file_name =~ /^#{k.to_s}$/ } + return find_res[1] if find_res + + # 3. try anything + find_res = any.find { |k, v| file_name =~ /.*/ } + return find_res[1] if find_res + + # 4. well, we've tried + return [] + end + + def flag_down( operation, context, file ) + # create configurator accessor method + accessor = ('flags_' + context.to_s).to_sym + + # create simple filename key from whatever filename provided + file_name = File.basename( file ).ext('') + file_key = File.basename( file ).ext('').to_sym + + # if no entry in configuration for flags for this context, bail out + return [] if not @configurator.respond_to?( accessor ) + + # get flags sub hash associated with this context + flags = @configurator.send( accessor ) + + # if operation not represented in flags hash, bail out + return [] if not flags.include?( operation ) + + # redefine flags to sub hash associated with the operation + flags = flags[operation] + + return get_flag(flags, file_name) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/generator.rb b/CAN_App/vendor/ceedling/lib/ceedling/generator.rb new file mode 100644 index 0000000..0b89024 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/generator.rb @@ -0,0 +1,186 @@ +require 'ceedling/constants' + +class Generator + + constructor :configurator, + :generator_helper, + :preprocessinator, + :cmock_builder, + :generator_test_runner, + :generator_test_results, + :flaginator, + :test_includes_extractor, + :tool_executor, + :file_finder, + :file_path_utils, + :streaminator, + :plugin_manager, + :file_wrapper + + + def generate_shallow_includes_list(context, file) + @streaminator.stdout_puts("Generating include list for #{File.basename(file)}...", Verbosity::NORMAL) + @preprocessinator.preprocess_shallow_includes(file) + end + + def generate_preprocessed_file(context, file) + @streaminator.stdout_puts("Preprocessing #{File.basename(file)}...", Verbosity::NORMAL) + @preprocessinator.preprocess_file(file) + end + + def generate_dependencies_file(tool, context, source, object, dependencies) + @streaminator.stdout_puts("Generating dependencies for #{File.basename(source)}...", Verbosity::NORMAL) + + command = + @tool_executor.build_command_line( + tool, + [], # extra per-file command line parameters + source, + dependencies, + object) + + @tool_executor.exec( command[:line], command[:options] ) + end + + def generate_mock(context, header_filepath) + arg_hash = {:header_file => header_filepath, :context => context} + @plugin_manager.pre_mock_generate( arg_hash ) + + begin + @cmock_builder.cmock.setup_mocks( arg_hash[:header_file] ) + rescue + raise + ensure + @plugin_manager.post_mock_generate( arg_hash ) + end + end + + # test_filepath may be either preprocessed test file or original test file + def generate_test_runner(context, test_filepath, runner_filepath) + arg_hash = {:context => context, :test_file => test_filepath, :runner_file => runner_filepath} + @plugin_manager.pre_runner_generate(arg_hash) + + # collect info we need + module_name = File.basename(arg_hash[:test_file]) + test_cases = @generator_test_runner.find_test_cases( @file_finder.find_test_from_runner_path(runner_filepath) ) + mock_list = @test_includes_extractor.lookup_raw_mock_list(arg_hash[:test_file]) + + @streaminator.stdout_puts("Generating runner for #{module_name}...", Verbosity::NORMAL) + + test_file_includes = [] # Empty list for now, since apparently unused + + # build runner file + begin + @generator_test_runner.generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes) + rescue + raise + ensure + @plugin_manager.post_runner_generate(arg_hash) + end + end + + def generate_object_file(tool, operation, context, source, object, list='', dependencies='') + shell_result = {} + arg_hash = {:tool => tool, :operation => operation, :context => context, :source => source, :object => object, :list => list, :dependencies => dependencies} + @plugin_manager.pre_compile_execute(arg_hash) + + @streaminator.stdout_puts("Compiling #{File.basename(arg_hash[:source])}...", Verbosity::NORMAL) + command = + @tool_executor.build_command_line( arg_hash[:tool], + @flaginator.flag_down( operation, context, source ), + arg_hash[:source], + arg_hash[:object], + arg_hash[:list], + arg_hash[:dependencies]) + + @streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG) + + begin + shell_result = @tool_executor.exec( command[:line], command[:options] ) + rescue ShellExecutionException => ex + shell_result = ex.shell_result + raise ex + ensure + arg_hash[:shell_command] = command[:line] + arg_hash[:shell_result] = shell_result + @plugin_manager.post_compile_execute(arg_hash) + end + end + + def generate_executable_file(tool, context, objects, executable, map='', libraries=[], libpaths=[]) + shell_result = {} + arg_hash = { :tool => tool, + :context => context, + :objects => objects, + :executable => executable, + :map => map, + :libraries => libraries, + :libpaths => libpaths + } + + @plugin_manager.pre_link_execute(arg_hash) + + @streaminator.stdout_puts("Linking #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL) + command = + @tool_executor.build_command_line( arg_hash[:tool], + @flaginator.flag_down( OPERATION_LINK_SYM, context, executable ), + arg_hash[:objects], + arg_hash[:executable], + arg_hash[:map], + arg_hash[:libraries], + arg_hash[:libpaths] + ) + @streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG) + + begin + shell_result = @tool_executor.exec( command[:line], command[:options] ) + rescue ShellExecutionException => ex + notice = "\n" + + "NOTICE: If the linker reports missing symbols, the following may be to blame:\n" + + " 1. Test lacks #include statements corresponding to needed source files.\n" + + " 2. Project search paths do not contain source files corresponding to #include statements in the test.\n" + + if (@configurator.project_use_mocks) + notice += " 3. Test does not #include needed mocks.\n\n" + else + notice += "\n" + end + + @streaminator.stderr_puts(notice, Verbosity::COMPLAIN) + shell_result = ex.shell_result + raise '' + ensure + arg_hash[:shell_result] = shell_result + @plugin_manager.post_link_execute(arg_hash) + end + end + + def generate_test_results(tool, context, executable, result) + arg_hash = {:tool => tool, :context => context, :executable => executable, :result_file => result} + @plugin_manager.pre_test_fixture_execute(arg_hash) + + @streaminator.stdout_puts("Running #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL) + + # Unity's exit code is equivalent to the number of failed tests, so we tell @tool_executor not to fail out if there are failures + # so that we can run all tests and collect all results + command = @tool_executor.build_command_line(arg_hash[:tool], [], arg_hash[:executable]) + @streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG) + command[:options][:boom] = false + shell_result = @tool_executor.exec( command[:line], command[:options] ) + + #Don't Let The Failure Count Make Us Believe Things Aren't Working + shell_result[:exit_code] = 0 + @generator_helper.test_results_error_handler(executable, shell_result) + + processed = @generator_test_results.process_and_write_results( shell_result, + arg_hash[:result_file], + @file_finder.find_test_from_file_path(arg_hash[:executable]) ) + + arg_hash[:result_file] = processed[:result_file] + arg_hash[:results] = processed[:results] + arg_hash[:shell_result] = shell_result # for raw output display if no plugins for formatted display + + @plugin_manager.post_test_fixture_execute(arg_hash) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/generator_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/generator_helper.rb new file mode 100644 index 0000000..3431560 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/generator_helper.rb @@ -0,0 +1,40 @@ +require 'ceedling/constants' + + +class GeneratorHelper + + constructor :streaminator + + + def test_results_error_handler(executable, shell_result) + notice = '' + error = false + + if (shell_result[:output].nil? or shell_result[:output].strip.empty?) + error = true + # mirror style of generic tool_executor failure output + notice = "\n" + + "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" + + "> Produced no output to $stdout.\n" + elsif ((shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN).nil?) + error = true + # mirror style of generic tool_executor failure output + notice = "\n" + + "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" + + "> Produced no final test result counts in $stdout:\n" + + "#{shell_result[:output].strip}\n" + end + + if (error) + # since we told the tool executor to ignore the exit code, handle it explicitly here + notice += "> And exited with status: [#{shell_result[:exit_code]}] (count of failed tests).\n" if (shell_result[:exit_code] != nil) + notice += "> And then likely crashed.\n" if (shell_result[:exit_code] == nil) + + notice += "> This is often a symptom of a bad memory access in source or test code.\n\n" + + @streaminator.stderr_puts(notice, Verbosity::COMPLAIN) + raise + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results.rb b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results.rb new file mode 100644 index 0000000..3af2d72 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results.rb @@ -0,0 +1,100 @@ +require 'rubygems' +require 'rake' # for .ext() +require 'ceedling/constants' + +class GeneratorTestResults + + constructor :configurator, :generator_test_results_sanity_checker, :yaml_wrapper + + def process_and_write_results(unity_shell_result, results_file, test_file) + output_file = results_file + + results = get_results_structure + + results[:source][:path] = File.dirname(test_file) + results[:source][:file] = File.basename(test_file) + results[:time] = unity_shell_result[:time] unless unity_shell_result[:time].nil? + + # process test statistics + if (unity_shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN) + results[:counts][:total] = $1.to_i + results[:counts][:failed] = $2.to_i + results[:counts][:ignored] = $3.to_i + results[:counts][:passed] = (results[:counts][:total] - results[:counts][:failed] - results[:counts][:ignored]) + end + + # remove test statistics lines + output_string = unity_shell_result[:output].sub(TEST_STDOUT_STATISTICS_PATTERN, '') + + output_string.lines do |line| + # process unity output + case line + when /(:IGNORE)/ + elements = extract_line_elements(line, results[:source][:file]) + results[:ignores] << elements[0] + results[:stdout] << elements[1] if (!elements[1].nil?) + when /(:PASS$)/ + elements = extract_line_elements(line, results[:source][:file]) + results[:successes] << elements[0] + results[:stdout] << elements[1] if (!elements[1].nil?) + when /(:PASS \(.* ms\)$)/ + elements = extract_line_elements(line, results[:source][:file]) + results[:successes] << elements[0] + results[:stdout] << elements[1] if (!elements[1].nil?) + when /(:FAIL)/ + elements = extract_line_elements(line, results[:source][:file]) + results[:failures] << elements[0] + results[:stdout] << elements[1] if (!elements[1].nil?) + else # collect up all other + results[:stdout] << line.chomp + end + end + + @generator_test_results_sanity_checker.verify(results, unity_shell_result[:exit_code]) + + output_file = results_file.ext(@configurator.extension_testfail) if (results[:counts][:failed] > 0) + + @yaml_wrapper.dump(output_file, results) + + return { :result_file => output_file, :result => results } + end + + private + + def get_results_structure + return { + :source => {:path => '', :file => ''}, + :successes => [], + :failures => [], + :ignores => [], + :counts => {:total => 0, :passed => 0, :failed => 0, :ignored => 0}, + :stdout => [], + :time => 0.0 + } + end + + def extract_line_elements(line, filename) + # handle anything preceding filename in line as extra output to be collected + stdout = nil + stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i + unity_test_time = 0 + + if (line =~ stdout_regex) + stdout = $1.clone + line.sub!(/#{Regexp.escape(stdout)}/, '') + end + + # collect up test results minus and extra output + elements = (line.strip.split(':'))[1..-1] + + # find timestamp if available + if (elements[-1] =~ / \((\d*(?:\.\d*)?) ms\)/) + unity_test_time = $1.to_f / 1000 + elements[-1].sub!(/ \((\d*(?:\.\d*)?) ms\)/, '') + end + + return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip, :unity_test_time => unity_test_time}, stdout if elements.size >= 3 + return {:test => '???', :line => -1, :message => nil, :unity_test_time => unity_test_time} #fallback safe option. TODO better handling + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb new file mode 100644 index 0000000..0b51832 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb @@ -0,0 +1,65 @@ +require 'rubygems' +require 'rake' # for ext() method +require 'ceedling/constants' + + +class GeneratorTestResultsSanityChecker + + constructor :configurator, :streaminator + + def verify(results, unity_exit_code) + + # do no sanity checking if it's disabled + return if (@configurator.sanity_checks == TestResultsSanityChecks::NONE) + raise "results nil or empty" if results.nil? || results.empty? + + ceedling_ignores_count = results[:ignores].size + ceedling_failures_count = results[:failures].size + ceedling_tests_summation = (ceedling_ignores_count + ceedling_failures_count + results[:successes].size) + + # Exit code handling is not a sanity check that can always be performed because + # command line simulators may or may not pass through Unity's exit code + if (@configurator.sanity_checks >= TestResultsSanityChecks::THOROUGH) + # many platforms limit exit codes to a maximum of 255 + if ((ceedling_failures_count != unity_exit_code) and (unity_exit_code < 255)) + sanity_check_warning(results[:source][:file], "Unity's exit code (#{unity_exit_code}) does not match Ceedling's summation of failed test cases (#{ceedling_failures_count}).") + end + + if ((ceedling_failures_count < 255) and (unity_exit_code == 255)) + sanity_check_warning(results[:source][:file], "Ceedling's summation of failed test cases (#{ceedling_failures_count}) is less than Unity's exit code (255 or more).") + end + end + + if (ceedling_ignores_count != results[:counts][:ignored]) + sanity_check_warning(results[:source][:file], "Unity's final ignore count (#{results[:counts][:ignored]}) does not match Ceedling's summation of ignored test cases (#{ceedling_ignores_count}).") + end + + if (ceedling_failures_count != results[:counts][:failed]) + sanity_check_warning(results[:source][:file], "Unity's final fail count (#{results[:counts][:failed]}) does not match Ceedling's summation of failed test cases (#{ceedling_failures_count}).") + end + + if (ceedling_tests_summation != results[:counts][:total]) + sanity_check_warning(results[:source][:file], "Unity's final test count (#{results[:counts][:total]}) does not match Ceedling's summation of all test cases (#{ceedling_tests_summation}).") + end + + end + + private + + def sanity_check_warning(file, message) + unless defined?(CEEDLING_IGNORE_SANITY_CHECK) + notice = "\n" + + "ERROR: Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" + + " Possible causes:\n" + + " 1. Your test + source dereferenced a null pointer.\n" + + " 2. Your test + source indexed past the end of a buffer.\n" + + " 3. Your test + source committed a memory access violation.\n" + + " 4. Your test fixture produced an exit code of 0 despite execution ending prematurely.\n" + + " Sanity check failures of test results are usually a symptom of interrupted test execution.\n\n" + + @streaminator.stderr_puts( notice ) + raise + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/generator_test_runner.rb b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_runner.rb new file mode 100644 index 0000000..79ed714 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/generator_test_runner.rb @@ -0,0 +1,58 @@ + +class GeneratorTestRunner + + constructor :configurator, :file_path_utils, :file_wrapper + + def find_test_cases(test_file) + + #Pull in Unity's Test Runner Generator + require 'generate_test_runner.rb' + @test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config ) + + if (@configurator.project_use_test_preprocessor) + + #redirect to use the preprocessor file if we're doing that sort of thing + pre_test_file = @file_path_utils.form_preprocessed_file_filepath(test_file) + + #actually look for the tests using Unity's test runner generator + contents = @file_wrapper.read(pre_test_file) + tests_and_line_numbers = @test_runner_generator.find_tests(contents) + @test_runner_generator.find_setup_and_teardown(contents) + + #look up the line numbers in the original file + source_lines = @file_wrapper.read(test_file).split("\n") + source_index = 0; + tests_and_line_numbers.size.times do |i| + source_lines[source_index..-1].each_with_index do |line, index| + if (line =~ /#{tests_and_line_numbers[i][:test]}/) + source_index += index + tests_and_line_numbers[i][:line_number] = source_index + 1 + break + end + end + end + else + #Just look for the tests using Unity's test runner generator + contents = @file_wrapper.read(test_file) + tests_and_line_numbers = @test_runner_generator.find_tests(contents) + @test_runner_generator.find_setup_and_teardown(contents) + end + + return tests_and_line_numbers + end + + def generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes=[]) + require 'generate_test_runner.rb' + + header_extension = @configurator.extension_header + + #actually build the test runner using Unity's test runner generator + #(there is no need to use preprocessor here because we've already looked up test cases and are passing them in here) + @test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config ) + @test_runner_generator.generate( module_name, + runner_filepath, + test_cases, + mock_list.map{|f| File.basename(f,'.*')+header_extension}, + test_file_includes.map{|f| File.basename(f,'.*')+header_extension}) + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/loginator.rb b/CAN_App/vendor/ceedling/lib/ceedling/loginator.rb new file mode 100644 index 0000000..92276e1 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/loginator.rb @@ -0,0 +1,31 @@ + +class Loginator + + constructor :configurator, :project_file_loader, :project_config_manager, :file_wrapper, :system_wrapper + + + def setup_log_filepath + config_files = [] + config_files << @project_file_loader.main_file + config_files << @project_file_loader.user_file + config_files.concat( @project_config_manager.options_files ) + config_files.compact! + config_files.map! { |file| file.ext('') } + + log_name = config_files.join( '_' ) + + @project_log_filepath = File.join( @configurator.project_log_path, log_name.ext('.log') ) + end + + + def log(string, heading=nil) + return if (not @configurator.project_logging) + + output = "\n[#{@system_wrapper.time_now}]" + output += " :: #{heading}" if (not heading.nil?) + output += "\n#{string.strip}\n" + + @file_wrapper.write(@project_log_filepath, output, 'a') + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/makefile.rb b/CAN_App/vendor/ceedling/lib/ceedling/makefile.rb new file mode 100644 index 0000000..c3d7496 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/makefile.rb @@ -0,0 +1,46 @@ + +# modified version of Rake's provided make-style dependency loader +# customizations: +# (1) handles windows drives in paths -- colons don't confuse task demarcation +# (2) handles spaces in directory paths + +module Rake + + # Makefile loader to be used with the import file loader. + class MakefileLoader + + # Load the makefile dependencies in +fn+. + def load(fn) + open(fn) do |mf| + lines = mf.read + lines.gsub!(/#[^\n]*\n/m, "") # remove comments + lines.gsub!(/\\\n/, ' ') # string together line continuations into single line + lines.split("\n").each do |line| + process_line(line) + end + end + end + + private + + # Process one logical line of makefile data. + def process_line(line) + # split on presence of task demaractor followed by space (i.e don't get confused by a colon in a win path) + file_tasks, args = line.split(/:\s/) + + return if args.nil? + + # split at non-escaped space boundary between files (i.e. escaped spaces in paths are left alone) + dependents = args.split(/\b\s+/) + # replace escaped spaces and clean up any extra whitespace + dependents.map! { |path| path.gsub(/\\ /, ' ').strip } + + file_tasks.strip.split.each do |file_task| + file file_task => dependents + end + end + end + + # Install the handler + Rake.application.add_loader('mf', MakefileLoader.new) +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/objects.yml b/CAN_App/vendor/ceedling/lib/ceedling/objects.yml new file mode 100644 index 0000000..43bbc06 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/objects.yml @@ -0,0 +1,313 @@ + +file_wrapper: + +file_system_wrapper: + +stream_wrapper: + +rake_wrapper: + +yaml_wrapper: + +system_wrapper: + +cmock_builder: + +reportinator: + +rake_utils: + compose: + - rake_wrapper + +system_utils: + compose: + - system_wrapper + +file_path_utils: + compose: + - configurator + - file_wrapper + +file_system_utils: + compose: file_wrapper + +project_file_loader: + compose: + - yaml_wrapper + - stream_wrapper + - system_wrapper + - file_wrapper + +project_config_manager: + compose: + - cacheinator + - configurator + - yaml_wrapper + - file_wrapper + +cacheinator: + compose: + - cacheinator_helper + - file_path_utils + - file_wrapper + - yaml_wrapper + +cacheinator_helper: + compose: + - file_wrapper + - yaml_wrapper + +tool_executor: + compose: + - configurator + - tool_executor_helper + - streaminator + - system_wrapper + +tool_executor_helper: + compose: + - streaminator + - system_utils + - system_wrapper + +configurator: + compose: + - configurator_setup + - configurator_plugins + - configurator_builder + - cmock_builder + - yaml_wrapper + - system_wrapper + +configurator_setup: + compose: + - configurator_builder + - configurator_validator + - configurator_plugins + - stream_wrapper + +configurator_plugins: + compose: + - stream_wrapper + - file_wrapper + - system_wrapper + +configurator_validator: + compose: + - file_wrapper + - stream_wrapper + - system_wrapper + +configurator_builder: + compose: + - file_system_utils + - file_wrapper + - system_wrapper + +loginator: + compose: + - configurator + - project_file_loader + - project_config_manager + - file_wrapper + - system_wrapper + +streaminator: + compose: + - streaminator_helper + - verbosinator + - loginator + - stream_wrapper + +streaminator_helper: + +setupinator: + +plugin_builder: + +plugin_manager: + compose: + - configurator + - plugin_manager_helper + - streaminator + - reportinator + - system_wrapper + +plugin_manager_helper: + +plugin_reportinator: + compose: + - plugin_reportinator_helper + - plugin_manager + - reportinator + +plugin_reportinator_helper: + compose: + - configurator + - streaminator + - yaml_wrapper + - file_wrapper + +verbosinator: + compose: configurator + +file_finder: + compose: + - configurator + - file_finder_helper + - cacheinator + - file_path_utils + - file_wrapper + - yaml_wrapper + +file_finder_helper: + compose: streaminator + +test_includes_extractor: + compose: + - configurator + - yaml_wrapper + - file_wrapper + +task_invoker: + compose: + - dependinator + - rake_utils + - rake_wrapper + - project_config_manager + +flaginator: + compose: + - configurator + +generator: + compose: + - configurator + - generator_helper + - preprocessinator + - cmock_builder + - generator_test_runner + - generator_test_results + - flaginator + - test_includes_extractor + - tool_executor + - file_finder + - file_path_utils + - streaminator + - plugin_manager + - file_wrapper + +generator_helper: + compose: + - streaminator + +generator_test_results: + compose: + - configurator + - generator_test_results_sanity_checker + - yaml_wrapper + +generator_test_results_sanity_checker: + compose: + - configurator + - streaminator + +generator_test_runner: + compose: + - configurator + - file_path_utils + - file_wrapper + +dependinator: + compose: + - configurator + - project_config_manager + - test_includes_extractor + - file_path_utils + - rake_wrapper + - file_wrapper + +preprocessinator: + compose: + - preprocessinator_helper + - preprocessinator_includes_handler + - preprocessinator_file_handler + - task_invoker + - file_path_utils + - yaml_wrapper + - project_config_manager + - configurator + +preprocessinator_helper: + compose: + - configurator + - test_includes_extractor + - task_invoker + - file_finder + - file_path_utils + +preprocessinator_includes_handler: + compose: + - configurator + - tool_executor + - task_invoker + - file_path_utils + - yaml_wrapper + - file_wrapper + - file_finder + +preprocessinator_file_handler: + compose: + - preprocessinator_extractor + - configurator + - tool_executor + - file_path_utils + - file_wrapper + +preprocessinator_extractor: + +test_invoker: + compose: + - configurator + - test_invoker_helper + - plugin_manager + - streaminator + - preprocessinator + - task_invoker + - dependinator + - project_config_manager + - build_invoker_utils + - file_path_utils + - file_wrapper + +test_invoker_helper: + compose: + - configurator + - task_invoker + - test_includes_extractor + - file_finder + - file_path_utils + - file_wrapper + +release_invoker: + compose: + - configurator + - release_invoker_helper + - build_invoker_utils + - dependinator + - task_invoker + - file_path_utils + - file_wrapper + +release_invoker_helper: + compose: + - configurator + - dependinator + - task_invoker + +build_invoker_utils: + compose: + - configurator + - streaminator + +erb_wrapper: diff --git a/CAN_App/vendor/ceedling/lib/ceedling/par_map.rb b/CAN_App/vendor/ceedling/lib/ceedling/par_map.rb new file mode 100644 index 0000000..98198a2 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/par_map.rb @@ -0,0 +1,19 @@ + + +def par_map(n, things, &block) + queue = Queue.new + things.each { |thing| queue << thing } + threads = (1..n).collect do + Thread.new do + begin + while true + yield queue.pop(true) + end + rescue ThreadError + + end + end + end + threads.each { |t| t.join } +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin.rb new file mode 100644 index 0000000..f20b3a3 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin.rb @@ -0,0 +1,80 @@ + +class String + # reformat a multiline string to have given number of whitespace columns; + # helpful for formatting heredocs + def left_margin(margin=0) + non_whitespace_column = 0 + new_lines = [] + + # find first line with non-whitespace and count left columns of whitespace + self.each_line do |line| + if (line =~ /^\s*\S/) + non_whitespace_column = $&.length - 1 + break + end + end + + # iterate through each line, chopping off leftmost whitespace columns and add back the desired whitespace margin + self.each_line do |line| + columns = [] + margin.times{columns << ' '} + # handle special case of line being narrower than width to be lopped off + if (non_whitespace_column < line.length) + new_lines << "#{columns.join}#{line[non_whitespace_column..-1]}" + else + new_lines << "\n" + end + end + + return new_lines.join + end +end + +class Plugin + attr_reader :name, :environment + attr_accessor :plugin_objects + + def initialize(system_objects, name) + @environment = [] + @ceedling = system_objects + @name = name + self.setup + end + + def setup; end + + # mock generation + def pre_mock_generate(arg_hash); end + def post_mock_generate(arg_hash); end + + # test runner generation + def pre_runner_generate(arg_hash); end + def post_runner_generate(arg_hash); end + + # compilation (test or source) + def pre_compile_execute(arg_hash); end + def post_compile_execute(arg_hash); end + + # linking (test or source) + def pre_link_execute(arg_hash); end + def post_link_execute(arg_hash); end + + # test fixture execution + def pre_test_fixture_execute(arg_hash); end + def post_test_fixture_execute(arg_hash); end + + # test task + def pre_test(test); end + def post_test(test); end + + # release task + def pre_release; end + def post_release; end + + # whole shebang (any use of Ceedling) + def pre_build; end + def post_build; end + + def summary; end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin_builder.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin_builder.rb new file mode 100644 index 0000000..1269141 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin_builder.rb @@ -0,0 +1,53 @@ +require 'ceedling/plugin' + +class PluginBuilder + + attr_accessor :plugin_objects + + def construct_plugin(plugin_name, object_map_yaml, system_objects) + # @streaminator.stdout_puts("Constructing plugin #{plugin_name}...", Verbosity::OBNOXIOUS) + object_map = {} + @plugin_objects = {} + @system_objects = system_objects + + if object_map_yaml + @object_map = YAML.load(object_map_yaml) + @object_map.each_key do |obj| + construct_object(obj) + end + else + raise "Invalid object map for plugin #{plugin_name}!" + end + + return @plugin_objects + end + + private + + def camelize(underscored_name) + return underscored_name.gsub(/(_|^)([a-z0-9])/) {$2.upcase} + end + + def construct_object(obj) + if @plugin_objects[obj].nil? + if @object_map[obj] && @object_map[obj]['compose'] + @object_map[obj]['compose'].each do |dep| + construct_object(dep) + end + end + build_object(obj) + end + end + + def build_object(new_object) + if @plugin_objects[new_object.to_sym].nil? + # @streaminator.stdout_puts("Building plugin object #{new_object}", Verbosity::OBNOXIOUS) + require new_object + class_name = camelize(new_object) + new_instance = eval("#{class_name}.new(@system_objects, class_name.to_s)") + new_instance.plugin_objects = @plugin_objects + @plugin_objects[new_object.to_sym] = new_instance + end + end + +end \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager.rb new file mode 100644 index 0000000..0468f2f --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager.rb @@ -0,0 +1,107 @@ +require 'ceedling/constants' + +class PluginManager + + constructor :configurator, :plugin_manager_helper, :streaminator, :reportinator, :system_wrapper + + def setup + @build_fail_registry = [] + @plugin_objects = [] # so we can preserve order + end + + def load_plugin_scripts(script_plugins, system_objects) + environment = [] + + script_plugins.each do |plugin| + # protect against instantiating object multiple times due to processing config multiple times (option files, etc) + next if (@plugin_manager_helper.include?(@plugin_objects, plugin)) + begin + @system_wrapper.require_file( "#{plugin}.rb" ) + object = @plugin_manager_helper.instantiate_plugin_script( camelize(plugin), system_objects, plugin ) + @plugin_objects << object + environment += object.environment + + # add plugins to hash of all system objects + system_objects[plugin.downcase.to_sym] = object + rescue + puts "Exception raised while trying to load plugin: #{plugin}" + raise + end + end + + yield( { :environment => environment } ) if (environment.size > 0) + end + + def plugins_failed? + return (@build_fail_registry.size > 0) + end + + def print_plugin_failures + if (@build_fail_registry.size > 0) + report = @reportinator.generate_banner('BUILD FAILURE SUMMARY') + + @build_fail_registry.each do |failure| + report += "#{' - ' if (@build_fail_registry.size > 1)}#{failure}\n" + end + + report += "\n" + + @streaminator.stderr_puts(report, Verbosity::ERRORS) + end + end + + def register_build_failure(message) + @build_fail_registry << message if (message and not message.empty?) + end + + #### execute all plugin methods #### + + def pre_mock_generate(arg_hash); execute_plugins(:pre_mock_generate, arg_hash); end + def post_mock_generate(arg_hash); execute_plugins(:post_mock_generate, arg_hash); end + + def pre_runner_generate(arg_hash); execute_plugins(:pre_runner_generate, arg_hash); end + def post_runner_generate(arg_hash); execute_plugins(:post_runner_generate, arg_hash); end + + def pre_compile_execute(arg_hash); execute_plugins(:pre_compile_execute, arg_hash); end + def post_compile_execute(arg_hash); execute_plugins(:post_compile_execute, arg_hash); end + + def pre_link_execute(arg_hash); execute_plugins(:pre_link_execute, arg_hash); end + def post_link_execute(arg_hash); execute_plugins(:post_link_execute, arg_hash); end + + def pre_test_fixture_execute(arg_hash); execute_plugins(:pre_test_fixture_execute, arg_hash); end + def post_test_fixture_execute(arg_hash) + # special arbitration: raw test results are printed or taken over by plugins handling the job + @streaminator.stdout_puts(arg_hash[:shell_result][:output]) if (@configurator.plugins_display_raw_test_results) + execute_plugins(:post_test_fixture_execute, arg_hash) + end + + def pre_test(test); execute_plugins(:pre_test, test); end + def post_test(test); execute_plugins(:post_test, test); end + + def pre_release; execute_plugins(:pre_release); end + def post_release; execute_plugins(:post_release); end + + def pre_build; execute_plugins(:pre_build); end + def post_build; execute_plugins(:post_build); end + def post_error; execute_plugins(:post_error); end + + def summary; execute_plugins(:summary); end + + private #################################### + + def camelize(underscored_name) + return underscored_name.gsub(/(_|^)([a-z0-9])/) {$2.upcase} + end + + def execute_plugins(method, *args) + @plugin_objects.each do |plugin| + begin + plugin.send(method, *args) if plugin.respond_to?(method) + rescue + puts "Exception raised in plugin: #{plugin.name}, in method #{method}" + raise + end + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb new file mode 100644 index 0000000..b18248a --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb @@ -0,0 +1,19 @@ + +class PluginManagerHelper + + def include?(plugins, name) + include = false + plugins.each do |plugin| + if (plugin.name == name) + include = true + break + end + end + return include + end + + def instantiate_plugin_script(plugin, system_objects, name) + return eval("#{plugin}.new(system_objects, name)") + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator.rb new file mode 100644 index 0000000..8d83727 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator.rb @@ -0,0 +1,76 @@ +require 'ceedling/constants' +require 'ceedling/defaults' + +class PluginReportinator + + constructor :plugin_reportinator_helper, :plugin_manager, :reportinator + + def setup + @test_results_template = nil + end + + + def set_system_objects(system_objects) + @plugin_reportinator_helper.ceedling = system_objects + end + + + def fetch_results(results_path, test, options={:boom => false}) + return @plugin_reportinator_helper.fetch_results( File.join(results_path, test), options ) + end + + + def generate_banner(message) + return @reportinator.generate_banner(message) + end + + + def assemble_test_results(results_list, options={:boom => false}) + aggregated_results = get_results_structure + + results_list.each do |result_path| + results = @plugin_reportinator_helper.fetch_results( result_path, options ) + @plugin_reportinator_helper.process_results(aggregated_results, results) + end + + return aggregated_results + end + + + def register_test_results_template(template) + @test_results_template = template if (@test_results_template.nil?) + end + + + def run_test_results_report(hash, verbosity=Verbosity::NORMAL, &block) + run_report( $stdout, + ((@test_results_template.nil?) ? DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE : @test_results_template), + hash, + verbosity, + &block ) + end + + + def run_report(stream, template, hash=nil, verbosity=Verbosity::NORMAL) + failure = nil + failure = yield() if block_given? + + @plugin_manager.register_build_failure( failure ) + + @plugin_reportinator_helper.run_report( stream, template, hash, verbosity ) + end + + private ############################### + + def get_results_structure + return { + :successes => [], + :failures => [], + :ignores => [], + :stdout => [], + :counts => {:total => 0, :passed => 0, :failed => 0, :ignored => 0, :stdout => 0}, + :time => 0.0 + } + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb new file mode 100644 index 0000000..322a530 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb @@ -0,0 +1,51 @@ +require 'erb' +require 'rubygems' +require 'rake' # for ext() +require 'ceedling/constants' + +class PluginReportinatorHelper + + attr_writer :ceedling + + constructor :configurator, :streaminator, :yaml_wrapper, :file_wrapper + + def fetch_results(results_path, options) + pass_path = File.join(results_path.ext( @configurator.extension_testpass )) + fail_path = File.join(results_path.ext( @configurator.extension_testfail )) + + if (@file_wrapper.exist?(fail_path)) + return @yaml_wrapper.load(fail_path) + elsif (@file_wrapper.exist?(pass_path)) + return @yaml_wrapper.load(pass_path) + else + if (options[:boom]) + @streaminator.stderr_puts("Could find no test results for '#{File.basename(results_path).ext(@configurator.extension_source)}'", Verbosity::ERRORS) + raise + end + end + + return {} + end + + + def process_results(aggregate_results, results) + return if (results.empty?) + aggregate_results[:successes] << { :source => results[:source].clone, :collection => results[:successes].clone } if (results[:successes].size > 0) + aggregate_results[:failures] << { :source => results[:source].clone, :collection => results[:failures].clone } if (results[:failures].size > 0) + aggregate_results[:ignores] << { :source => results[:source].clone, :collection => results[:ignores].clone } if (results[:ignores].size > 0) + aggregate_results[:stdout] << { :source => results[:source].clone, :collection => results[:stdout].clone } if (results[:stdout].size > 0) + aggregate_results[:counts][:total] += results[:counts][:total] + aggregate_results[:counts][:passed] += results[:counts][:passed] + aggregate_results[:counts][:failed] += results[:counts][:failed] + aggregate_results[:counts][:ignored] += results[:counts][:ignored] + aggregate_results[:counts][:stdout] += results[:stdout].size + aggregate_results[:time] += results[:time] + end + + + def run_report(stream, template, hash, verbosity) + output = ERB.new(template, 0, "%<>") + @streaminator.stream_puts(stream, output.result(binding()), verbosity) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator.rb new file mode 100644 index 0000000..52d82ca --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator.rb @@ -0,0 +1,56 @@ + +class Preprocessinator + + constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper, :project_config_manager, :configurator + + + def setup + # fashion ourselves callbacks @preprocessinator_helper can use + @preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) } + @preprocess_mock_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } + @preprocess_test_file_directives_proc = Proc.new { |filepath| self.preprocess_file_directives(filepath) } + @preprocess_test_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } + end + + def preprocess_shallow_source_includes(test) + @preprocessinator_helper.preprocess_source_includes(test) + end + + def preprocess_test_and_invoke_test_mocks(test) + @preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc) + + mocks_list = @preprocessinator_helper.assemble_mocks_list(test) + + @project_config_manager.process_test_defines_change(mocks_list) + + @preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_mock_file_proc) + + @task_invoker.invoke_test_mocks(mocks_list) + + if (@configurator.project_use_preprocessor_directives) + @preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_directives_proc) + else + @preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_proc) + end + + return mocks_list + end + + def preprocess_shallow_includes(filepath) + includes = @preprocessinator_includes_handler.extract_includes(filepath) + + @preprocessinator_includes_handler.write_shallow_includes_list( + @file_path_utils.form_preprocessed_includes_list_filepath(filepath), includes) + end + + def preprocess_file(filepath) + @preprocessinator_includes_handler.invoke_shallow_includes_list(filepath) + @preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) ) + end + + def preprocess_file_directives(filepath) + @preprocessinator_includes_handler.invoke_shallow_includes_list( filepath ) + @preprocessinator_file_handler.preprocess_file_directives( filepath, + @yaml_wrapper.load( @file_path_utils.form_preprocessed_includes_list_filepath( filepath ) ) ) + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb new file mode 100644 index 0000000..62026e1 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb @@ -0,0 +1,55 @@ +class PreprocessinatorExtractor + def extract_base_file_from_preprocessed_expansion(filepath) + # preprocessing by way of toolchain preprocessor expands macros, eliminates + # comments, strips out #ifdef code, etc. however, it also expands in place + # each #include'd file. so, we must extract only the lines of the file + # that belong to the file originally preprocessed + + # iterate through all lines and alternate between extract and ignore modes + # all lines between a '#'line containing file name of our filepath and the + # next '#'line should be extracted + + base_name = File.basename(filepath) + not_pragma = /^#(?!pragma\b)/ # preprocessor directive that's not a #pragma + pattern = /^#.*(\s|\/|\\|\")#{Regexp.escape(base_name)}/ + found_file = false # have we found the file we care about? + + lines = [] + File.readlines(filepath).each do |line| + line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') + if found_file and not line =~ not_pragma + lines << line + else + found_file = false + end + + found_file = true if line =~ pattern + end + + return lines + end + + def extract_base_file_from_preprocessed_directives(filepath) + # preprocessing by way of toolchain preprocessor eliminates directives only + # like #ifdef's and leave other code + + # iterate through all lines and only get last chunk of file after a last + # '#'line containing file name of our filepath + + base_name = File.basename(filepath) + pattern = /^#.*(\s|\/|\\|\")#{Regexp.escape(base_name)}/ + found_file = false # have we found the file we care about? + + lines = [] + File.readlines(filepath).each do |line| + line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') + lines << line + + if line =~ pattern + lines = [] + end + end + + return lines + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb new file mode 100644 index 0000000..978fa0d --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb @@ -0,0 +1,34 @@ + + +class PreprocessinatorFileHandler + + constructor :preprocessinator_extractor, :configurator, :tool_executor, :file_path_utils, :file_wrapper + + + def preprocess_file(filepath, includes) + preprocessed_filepath = @file_path_utils.form_preprocessed_file_filepath(filepath) + + command = @tool_executor.build_command_line(@configurator.tools_test_file_preprocessor, [], filepath, preprocessed_filepath) + @tool_executor.exec(command[:line], command[:options]) + + contents = @preprocessinator_extractor.extract_base_file_from_preprocessed_expansion(preprocessed_filepath) + + includes.each{|include| contents.unshift("#include \"#{include}\"")} + + @file_wrapper.write(preprocessed_filepath, contents.join("\n")) + end + + def preprocess_file_directives(filepath, includes) + preprocessed_filepath = @file_path_utils.form_preprocessed_file_filepath(filepath) + + command = @tool_executor.build_command_line(@configurator.tools_test_file_preprocessor_directives, [], filepath, preprocessed_filepath) + @tool_executor.exec(command[:line], command[:options]) + + contents = @preprocessinator_extractor.extract_base_file_from_preprocessed_directives(preprocessed_filepath) + + includes.each{|include| contents.unshift("#include \"#{include}\"")} + + @file_wrapper.write(preprocessed_filepath, contents.join("\n")) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb new file mode 100644 index 0000000..4bbda67 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb @@ -0,0 +1,50 @@ + + +class PreprocessinatorHelper + + constructor :configurator, :test_includes_extractor, :task_invoker, :file_finder, :file_path_utils + + + def preprocess_includes(test, preprocess_includes_proc) + if (@configurator.project_use_test_preprocessor) + preprocessed_includes_list = @file_path_utils.form_preprocessed_includes_list_filepath(test) + preprocess_includes_proc.call( @file_finder.find_test_from_file_path(preprocessed_includes_list) ) + @test_includes_extractor.parse_includes_list(preprocessed_includes_list) + else + @test_includes_extractor.parse_test_file(test) + end + end + + def preprocess_source_includes(test) + @test_includes_extractor.parse_test_file_source_include(test) + end + + def assemble_mocks_list(test) + return @file_path_utils.form_mocks_source_filelist( @test_includes_extractor.lookup_raw_mock_list(test) ) + end + + def preprocess_mockable_headers(mock_list, preprocess_file_proc) + if (@configurator.project_use_test_preprocessor) + preprocess_files_smartly( + @file_path_utils.form_preprocessed_mockable_headers_filelist(mock_list), + preprocess_file_proc ) { |file| @file_finder.find_header_file(file) } + end + end + + def preprocess_test_file(test, preprocess_file_proc) + return if (!@configurator.project_use_test_preprocessor) + + preprocess_file_proc.call(test) + end + + private ############################ + + def preprocess_files_smartly(file_list, preprocess_file_proc) + if (@configurator.project_use_deep_dependencies) + @task_invoker.invoke_test_preprocessed_files(file_list) + else + file_list.each { |file| preprocess_file_proc.call( yield(file) ) } + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb new file mode 100644 index 0000000..8b89c0b --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb @@ -0,0 +1,189 @@ + + +class PreprocessinatorIncludesHandler + + constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper, :file_finder + @@makefile_cache = {} + + # shallow includes: only those headers a source file explicitly includes + + def invoke_shallow_includes_list(filepath) + @task_invoker.invoke_test_shallow_include_lists( [@file_path_utils.form_preprocessed_includes_list_filepath(filepath)] ) + end + + ## + # Ask the preprocessor for a make-style dependency rule of only the headers + # the source file immediately includes. + # + # === Arguments + # +filepath+ _String_:: Path to the test file to process. + # + # === Return + # _String_:: The text of the dependency rule generated by the preprocessor. + def form_shallow_dependencies_rule(filepath) + if @@makefile_cache.has_key?(filepath) + return @@makefile_cache[filepath] + end + # change filename (prefix of '_') to prevent preprocessor from finding + # include files in temp directory containing file it's scanning + temp_filepath = @file_path_utils.form_temp_path(filepath, '_') + + # read the file and replace all include statements with a decorated version + # (decorating the names creates file names that don't exist, thus preventing + # the preprocessor from snaking out and discovering the entire include path + # that winds through the code). The decorated filenames indicate files that + # are included directly by the test file. + contents = @file_wrapper.read(filepath) + + if !contents.valid_encoding? + contents = contents.encode("UTF-16be", :invalid=>:replace, :replace=>"?").encode('UTF-8') + end + + contents.gsub!( /^\s*#include\s+[\"<]\s*(\S+)\s*[\">]/, "#include \"\\1\"\n#include \"@@@@\\1\"" ) + contents.gsub!( /^\s*TEST_FILE\(\s*\"\s*(\S+)\s*\"\s*\)/, "#include \"\\1\"\n#include \"@@@@\\1\"") + @file_wrapper.write( temp_filepath, contents ) + + # extract the make-style dependency rule telling the preprocessor to + # ignore the fact that it can't find the included files + command = @tool_executor.build_command_line(@configurator.tools_test_includes_preprocessor, [], temp_filepath) + shell_result = @tool_executor.exec(command[:line], command[:options]) + + @@makefile_cache[filepath] = shell_result[:output] + return shell_result[:output] + end + + ## + # Extract the headers that are directly included by a source file using the + # provided, annotated Make dependency rule. + # + # === Arguments + # +filepath+ _String_:: C source or header file to extract includes for. + # + # === Return + # _Array_ of _String_:: Array of the direct dependencies for the source file. + def extract_includes(filepath) + to_process = [filepath] + ignore_list = [] + list = [] + all_mocks = [] + + include_paths = @configurator.project_config_hash[:collection_paths_include] + include_paths = [] if include_paths.nil? + include_paths.map! {|path| File.expand_path(path)} + + while to_process.length > 0 + target = to_process.shift() + ignore_list << target + new_deps, new_to_process, all_mocks = extract_includes_helper(target, include_paths, ignore_list, all_mocks) + list += new_deps + to_process += new_to_process + if !@configurator.project_config_hash[:project_auto_link_deep_dependencies] + break + else + list = list.uniq() + to_process = to_process.uniq() + end + end + + return list + end + + def extract_includes_helper(filepath, include_paths, ignore_list, mocks) + # Extract the dependencies from the make rule + make_rule = self.form_shallow_dependencies_rule(filepath) + target_file = make_rule.split[0].gsub(':', '').gsub('\\','/') + base = File.basename(target_file, File.extname(target_file)) + make_rule_dependencies = make_rule.gsub(/.*\b#{Regexp.escape(base)}\S*/, '').gsub(/\\$/, '') + + # Extract the headers dependencies from the make rule + hdr_ext = @configurator.extension_header + headers_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(hdr_ext) }.uniq + headers_dependencies.map! {|hdr| hdr.gsub('\\','/') } + full_path_headers_dependencies = extract_full_path_dependencies(headers_dependencies) + + # Extract the sources dependencies from the make rule + src_ext = @configurator.extension_source + sources_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(src_ext) }.uniq + sources_dependencies.map! {|src| src.gsub('\\','/') } + full_path_sources_dependencies = extract_full_path_dependencies(sources_dependencies) + + list = full_path_headers_dependencies + full_path_sources_dependencies + + mock_prefix = @configurator.project_config_hash[:cmock_mock_prefix] + # Creating list of mocks + mocks += full_path_headers_dependencies.find_all do |header| + File.basename(header) =~ /^#{mock_prefix}.*$/ + end.compact + + # ignore real file when both mock and real file exist + mocks.each do |mock| + list.each do |filename| + if File.basename(filename) == File.basename(mock).sub(mock_prefix, '') + ignore_list << filename + end + end + end.compact + + # Filtering list of final includes to only include mocks and anything that is NOT in the ignore_list + list = list.select do |item| + mocks.include? item or !(ignore_list.any? { |ignore_item| !item.match(/^(.*\/)?#{Regexp.escape(ignore_item)}$/).nil? }) + end + + to_process = [] + + if @configurator.project_config_hash[:project_auto_link_deep_dependencies] + # Creating list of headers that should be recursively pre-processed + # Skipping mocks and vendor headers + headers_to_deep_link = full_path_headers_dependencies.select do |hdr| + !(mocks.include? hdr) and (hdr.match(/^(.*\/)(#{VENDORS_FILES.join('|')}) + #{Regexp.escape(hdr_ext)}$/).nil?) + end + headers_to_deep_link.map! {|hdr| File.expand_path(hdr) } + headers_to_deep_link.compact! + + headers_to_deep_link.each do |hdr| + if (ignore_list.none? {|ignore_header| hdr.match(/^(.*\/)?#{Regexp.escape(ignore_header)}$/)} and + include_paths.none? {|include_path| hdr =~ /^#{include_path}\.*/}) + if File.exist?(hdr) + to_process << hdr + src = @file_finder.find_compilation_input_file(hdr, :ignore) + to_process << src if src + end + end + end + end + + return list, to_process, mocks + + end + + def write_shallow_includes_list(filepath, list) + @yaml_wrapper.dump(filepath, list) + end + + private + + def extract_full_path_dependencies(dependencies) + # Separate the real files form the annotated ones and remove the '@@@@' + annotated_files, real_files = dependencies.partition {|file| file =~ /^@@@@/} + annotated_files.map! {|file| file.gsub('@@@@','') } + # Matching annotated_files values against real_files to ensure that + # annotated_files contain full path entries (as returned by make rule) + annotated_files.map! {|file| real_files.find {|real| !real.match(/^(.*\/)?#{Regexp.escape(file)}$/).nil?}} + annotated_files = annotated_files.compact + + # Find which of our annotated files are "real" dependencies. This is + # intended to weed out dependencies that have been removed due to build + # options defined in the project yaml and/or in the files themselves. + return annotated_files.find_all do |annotated_file| + # find the index of the "real" file that matches the annotated one. + idx = real_files.find_index do |real_file| + real_file =~ /^(.*\/)?#{Regexp.escape(annotated_file)}$/ + end + # If we found a real file, delete it from the array and return it, + # otherwise return nil. Since nil is falsy this has the effect of making + # find_all return only the annotated filess for which a real file was + # found/deleted + idx ? real_files.delete_at(idx) : nil + end.compact + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/project_config_manager.rb b/CAN_App/vendor/ceedling/lib/ceedling/project_config_manager.rb new file mode 100644 index 0000000..ed7a73b --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/project_config_manager.rb @@ -0,0 +1,52 @@ +require 'ceedling/constants' + + +class ProjectConfigManager + + attr_reader :options_files, :release_config_changed, :test_config_changed, :test_defines_changed + attr_accessor :config_hash + + constructor :cacheinator, :configurator, :yaml_wrapper, :file_wrapper + + + def setup + @options_files = [] + @release_config_changed = false + @test_config_changed = false + @test_defines_changed = false + end + + + def merge_options(config_hash, option_filepath) + @options_files << File.basename( option_filepath ) + config_hash.deep_merge!( @yaml_wrapper.load( option_filepath ) ) + end + + + def filter_internal_sources(sources) + filtered_sources = sources.clone + filtered_sources.delete_if { |item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{Regexp.escape(EXTENSION_SOURCE)}$/ } + filtered_sources.delete_if { |item| item =~ /#{VENDORS_FILES.map{|source| '\b' + Regexp.escape(source.ext(EXTENSION_SOURCE)) + '\b'}.join('|')}$/ } + return filtered_sources + end + + def process_release_config_change + # has project configuration changed since last release build + @release_config_changed = @cacheinator.diff_cached_release_config?( @config_hash ) + end + + + def process_test_config_change + # has project configuration changed since last test build + @test_config_changed = @cacheinator.diff_cached_test_config?( @config_hash ) + end + + def process_test_defines_change(files) + # has definitions changed since last test build + @test_defines_changed = @cacheinator.diff_cached_test_defines?( files ) + if @test_defines_changed + # update timestamp for rake task prerequisites + @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath, :mtime => Time.now + 10 ) + end + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/project_file_loader.rb b/CAN_App/vendor/ceedling/lib/ceedling/project_file_loader.rb new file mode 100644 index 0000000..bf5dcd4 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/project_file_loader.rb @@ -0,0 +1,99 @@ +require 'ceedling/constants' + + +class ProjectFileLoader + + attr_reader :main_file, :user_file + + constructor :yaml_wrapper, :stream_wrapper, :system_wrapper, :file_wrapper + + def setup + @main_file = nil + @mixin_files = [] + @user_file = nil + + @main_project_filepath = '' + @mixin_project_filepaths = [] + @user_project_filepath = '' + end + + + def find_project_files + # first go hunting for optional user project file by looking for environment variable and then default location on disk + user_filepath = @system_wrapper.env_get('CEEDLING_USER_PROJECT_FILE') + + if ( not user_filepath.nil? and @file_wrapper.exist?(user_filepath) ) + @user_project_filepath = user_filepath + elsif (@file_wrapper.exist?(DEFAULT_CEEDLING_USER_PROJECT_FILE)) + @user_project_filepath = DEFAULT_CEEDLING_USER_PROJECT_FILE + end + + # next check for mixin project files by looking for environment variable + mixin_filepaths = @system_wrapper.env_get('CEEDLING_MIXIN_PROJECT_FILES') + if ( not mixin_filepaths.nil? ) + mixin_filepaths.split(File::PATH_SEPARATOR).each do |filepath| + if ( @file_wrapper.exist?(filepath) ) + @mixin_project_filepaths.push(filepath) + end + end + end + + # next check for main project file by looking for environment variable and then default location on disk; + # blow up if we don't find this guy -- like, he's so totally important + main_filepath = @system_wrapper.env_get('CEEDLING_MAIN_PROJECT_FILE') + + if ( not main_filepath.nil? and @file_wrapper.exist?(main_filepath) ) + @main_project_filepath = main_filepath + elsif (@file_wrapper.exist?(DEFAULT_CEEDLING_MAIN_PROJECT_FILE)) + @main_project_filepath = DEFAULT_CEEDLING_MAIN_PROJECT_FILE + else + # no verbosity checking since this is lowest level reporting anyhow & + # verbosity checking depends on configurator which in turns needs this class (circular dependency) + @stream_wrapper.stderr_puts('Found no Ceedling project file (*.yml)') + raise + end + + @main_file = File.basename( @main_project_filepath ) + @mixin_project_filepaths.each do |filepath| + @mixin_files.push(File.basename( filepath )) + end + @user_file = File.basename( @user_project_filepath ) if ( not @user_project_filepath.empty? ) + end + + def yaml_merger(y1, y2) + o1 = y1 + y2.each_pair do |k,v| + if o1[k].nil? + o1[k] = v + else + if (o1[k].instance_of? Hash) + o1[k] = yaml_merger(o1[k], v) + elsif (o1[k].instance_of? Array) + o1[k] += v + else + o1[k] = v + end + end + end + return o1 + end + + def load_project_config + config_hash = @yaml_wrapper.load(@main_project_filepath) + + # if there are mixin project files, then use them + @mixin_project_filepaths.each do |filepath| + mixin = @yaml_wrapper.load(filepath) + config_hash = yaml_merger( config_hash, mixin ) + end + + # if there's a user project file, then use it + if ( not @user_project_filepath.empty? ) + user_hash = @yaml_wrapper.load(@user_project_filepath) + config_hash = yaml_merger( config_hash, user_hash ) + end + + return config_hash + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rake_utils.rb b/CAN_App/vendor/ceedling/lib/ceedling/rake_utils.rb new file mode 100644 index 0000000..3f667c8 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rake_utils.rb @@ -0,0 +1,17 @@ + +class RakeUtils + + constructor :rake_wrapper + + def task_invoked?(task_regex) + task_invoked = false + @rake_wrapper.task_list.each do |task| + if ((task.already_invoked) and (task.to_s =~ task_regex)) + task_invoked = true + break + end + end + return task_invoked + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rake_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/rake_wrapper.rb new file mode 100644 index 0000000..15e4796 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rake_wrapper.rb @@ -0,0 +1,33 @@ +require 'rubygems' +require 'rake' +require 'ceedling/makefile' # our replacement for rake's make-style dependency loader + +include Rake::DSL if defined?(Rake::DSL) + +class Rake::Task + attr_reader :already_invoked +end + +class RakeWrapper + + def initialize + @makefile_loader = Rake::MakefileLoader.new # use our custom replacement noted above + end + + def [](task) + return Rake::Task[task] + end + + def task_list + return Rake::Task.tasks + end + + def create_file_task(file_task, dependencies) + file(file_task => dependencies) + end + + def load_dependencies(dependencies_path) + @makefile_loader.load(dependencies_path) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rakefile.rb b/CAN_App/vendor/ceedling/lib/ceedling/rakefile.rb new file mode 100644 index 0000000..1bcb824 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rakefile.rb @@ -0,0 +1,85 @@ +require 'fileutils' + +# get directory containing this here file, back up one directory, and expand to full path +CEEDLING_ROOT = File.expand_path(File.dirname(__FILE__) + '/../..') +CEEDLING_LIB = File.join(CEEDLING_ROOT, 'lib') +CEEDLING_VENDOR = File.join(CEEDLING_ROOT, 'vendor') +CEEDLING_RELEASE = File.join(CEEDLING_ROOT, 'release') + +$LOAD_PATH.unshift( CEEDLING_LIB ) +$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'unity/auto') ) +$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'diy/lib') ) +$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'cmock/lib') ) + +require 'rake' + +#Let's make sure we remember the task descriptions in case we need them +Rake::TaskManager.record_task_metadata = true + +require 'diy' +require 'constructor' + +require 'ceedling/constants' +require 'ceedling/target_loader' + + +# construct all our objects +# ensure load path contains all libraries needed first +lib_ceedling_load_path_temp = File.join(CEEDLING_LIB, 'ceedling') +$LOAD_PATH.unshift( lib_ceedling_load_path_temp ) +@ceedling = DIY::Context.from_yaml( File.read( File.join(lib_ceedling_load_path_temp, 'objects.yml') ) ) +@ceedling.build_everything +# now that all objects are built, delete 'lib/ceedling' from load path +$LOAD_PATH.delete(lib_ceedling_load_path_temp) +# one-stop shopping for all our setup and such after construction +@ceedling[:setupinator].ceedling = @ceedling + +project_config = + begin + cfg = @ceedling[:setupinator].load_project_files + TargetLoader.inspect(cfg, ENV['TARGET']) + rescue TargetLoader::NoTargets + cfg + rescue TargetLoader::RequestReload + @ceedling[:setupinator].load_project_files + end + +@ceedling[:setupinator].do_setup( project_config ) + + +# tell all our plugins we're about to do something +@ceedling[:plugin_manager].pre_build + +# load rakefile component files (*.rake) +PROJECT_RAKEFILE_COMPONENT_FILES.each { |component| load(component) } + +# tell rake to shut up by default (overridden in verbosity / debug tasks as appropriate) +verbose(false) + + +# end block always executed following rake run +END { + $stdout.flush unless $stdout.nil? + $stderr.flush unless $stderr.nil? + + # cache our input configurations to use in comparison upon next execution + @ceedling[:cacheinator].cache_test_config( @ceedling[:setupinator].config_hash ) if (@ceedling[:task_invoker].test_invoked?) + @ceedling[:cacheinator].cache_release_config( @ceedling[:setupinator].config_hash ) if (@ceedling[:task_invoker].release_invoked?) + + # delete all temp files unless we're in debug mode + if (not @ceedling[:configurator].project_debug) + @ceedling[:file_wrapper].rm_f( @ceedling[:file_wrapper].directory_listing( File.join(@ceedling[:configurator].project_temp_path, '*') )) + end + + # only perform these final steps if we got here without runtime exceptions or errors + if (@ceedling[:system_wrapper].ruby_success) + + # tell all our plugins the build is done and process results + @ceedling[:plugin_manager].post_build + @ceedling[:plugin_manager].print_plugin_failures + exit(1) if (@ceedling[:plugin_manager].plugins_failed? && !@ceedling[:setupinator].config_hash[:graceful_fail]) + else + puts "ERROR: Ceedling Failed" + @ceedling[:plugin_manager].post_error + end +} diff --git a/CAN_App/vendor/ceedling/lib/ceedling/release_invoker.rb b/CAN_App/vendor/ceedling/lib/ceedling/release_invoker.rb new file mode 100644 index 0000000..19bbca7 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/release_invoker.rb @@ -0,0 +1,98 @@ +require 'ceedling/constants' + + +class ReleaseInvoker + + constructor :configurator, :release_invoker_helper, :build_invoker_utils, :dependinator, :task_invoker, :file_path_utils, :file_wrapper + + + def setup_and_invoke_c_objects( c_files ) + objects = @file_path_utils.form_release_build_c_objects_filelist( c_files ) + + begin + @release_invoker_helper.process_deep_dependencies( @file_path_utils.form_release_dependencies_filelist( c_files ) ) + + @dependinator.enhance_release_file_dependencies( objects ) + @task_invoker.invoke_release_objects( objects ) + rescue => e + @build_invoker_utils.process_exception( e, RELEASE_SYM, false ) + end + + return objects + end + + + def setup_and_invoke_asm_objects( asm_files ) + objects = @file_path_utils.form_release_build_asm_objects_filelist( asm_files ) + + begin + @dependinator.enhance_release_file_dependencies( objects ) + @task_invoker.invoke_release_objects( objects ) + rescue => e + @build_invoker_utils.process_exception( e, RELEASE_SYM, false ) + end + + return objects + end + + + def refresh_c_deep_dependencies + return if (not @configurator.project_use_deep_dependencies) + + @file_wrapper.rm_f( + @file_wrapper.directory_listing( + File.join( @configurator.project_release_dependencies_path, '*' + @configurator.extension_dependencies ) ) ) + + @release_invoker_helper.process_deep_dependencies( + @file_path_utils.form_release_dependencies_filelist( + @configurator.collection_all_source ) ) + end + + + def artifactinate( *files ) + files.flatten.each do |file| + @file_wrapper.cp( file, @configurator.project_release_artifacts_path ) if @file_wrapper.exist?( file ) + end + end + + def convert_libraries_to_arguments(libraries) + args = ((libraries || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten + if (defined? LIBRARIES_FLAG) + args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) } + end + return args + end + + def get_library_paths_to_arguments() + paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : [] + if (defined? LIBRARIES_PATH_FLAG) + paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) } + end + return paths + end + + def sort_objects_and_libraries(both) + extension = if ((defined? EXTENSION_SUBPROJECTS) && (defined? EXTENSION_LIBRARIES)) + extension_libraries = if (EXTENSION_LIBRARIES.class == Array) + EXTENSION_LIBRARIES.join(")|(?:\\") + else + EXTENSION_LIBRARIES + end + "(?:\\#{EXTENSION_SUBPROJECTS})|(?:\\#{extension_libraries})" + elsif (defined? EXTENSION_SUBPROJECTS) + "\\#{EXTENSION_SUBPROJECTS}" + elsif (defined? EXTENSION_LIBRARIES) + if (EXTENSION_LIBRARIES.class == Array) + "(?:\\#{EXTENSION_LIBRARIES.join(")|(?:\\")})" + else + "\\#{EXTENSION_LIBRARIES}" + end + else + "\\.LIBRARY" + end + sorted_objects = both.group_by {|v| v.match(/.+#{extension}$/) ? :libraries : :objects } + libraries = sorted_objects[:libraries] || [] + objects = sorted_objects[:objects] || [] + return objects, libraries + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/release_invoker_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/release_invoker_helper.rb new file mode 100644 index 0000000..f83a2a5 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/release_invoker_helper.rb @@ -0,0 +1,19 @@ + + +class ReleaseInvokerHelper + + constructor :configurator, :dependinator, :task_invoker + + + def process_deep_dependencies(dependencies_list) + return if (not @configurator.project_use_deep_dependencies) + + if @configurator.project_generate_deep_dependencies + @dependinator.enhance_release_file_dependencies( dependencies_list ) + @task_invoker.invoke_release_dependencies_files( dependencies_list ) + end + + @dependinator.load_release_object_deep_dependencies( dependencies_list ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/reportinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/reportinator.rb new file mode 100644 index 0000000..0f583d0 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/reportinator.rb @@ -0,0 +1,26 @@ +## +# Pretifies reports +class Reportinator + + ## + # Generates a banner for a message based on the length of the message or a + # given width. + # ==== Attributes + # + # * _message_: The message to put. + # * _width_: The width of the message. If nil the size of the banner is + # determined by the length of the message. + # + # ==== Examples + # + # rp = Reportinator.new + # rp.generate_banner("Hello world!") => "------------\nHello world!\n------------\n" + # rp.generate_banner("Hello world!", 3) => "---\nHello world!\n---\n" + # + # + def generate_banner(message, width=nil) + dash_count = ((width.nil?) ? message.strip.length : width) + return "#{'-' * dash_count}\n#{message}\n#{'-' * dash_count}\n" + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_cmock.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_cmock.rake new file mode 100644 index 0000000..70ddcbc --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_cmock.rake @@ -0,0 +1,9 @@ + + +rule(/#{CMOCK_MOCK_PREFIX}[^\/\\]+#{'\\'+EXTENSION_SOURCE}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_header_input_for_mock_file(task_name) + end + ]) do |mock| + @ceedling[:generator].generate_mock(TEST_SYM, mock.source) +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_preprocess.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_preprocess.rake new file mode 100644 index 0000000..c291112 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_preprocess.rake @@ -0,0 +1,26 @@ + + +# invocations against this rule should only happen when enhanced dependencies are enabled; +# otherwise, dependency tracking will be too shallow and preprocessed files could intermittently +# fail to be updated when they actually need to be. +rule(/#{PROJECT_TEST_PREPROCESS_FILES_PATH}\/.+/ => [ + proc do |task_name| + @ceedling[:file_finder].find_test_or_source_or_header_file(task_name) + end + ]) do |file| + if (not @ceedling[:configurator].project_use_deep_dependencies) + raise 'ERROR: Ceedling preprocessing rule invoked though neccessary auxiliary dependency support not enabled.' + end + @ceedling[:generator].generate_preprocessed_file(TEST_SYM, file.source) +end + + +# invocations against this rule can always happen as there are no deeper dependencies to consider +rule(/#{PROJECT_TEST_PREPROCESS_INCLUDES_PATH}\/.+/ => [ + proc do |task_name| + @ceedling[:file_finder].find_test_or_source_or_header_file(task_name) + end + ]) do |file| + @ceedling[:generator].generate_shallow_includes_list(TEST_SYM, file.source) +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_release.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_release.rake new file mode 100644 index 0000000..4a583bd --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_release.rake @@ -0,0 +1,99 @@ + +RELEASE_COMPILE_TASK_ROOT = RELEASE_TASK_ROOT + 'compile:' unless defined?(RELEASE_COMPILE_TASK_ROOT) +RELEASE_ASSEMBLE_TASK_ROOT = RELEASE_TASK_ROOT + 'assemble:' unless defined?(RELEASE_ASSEMBLE_TASK_ROOT) + +# If GCC and Releasing a Library, Update Tools to Automatically Have Necessary Tags +if (TOOLS_RELEASE_COMPILER[:executable] == DEFAULT_RELEASE_COMPILER_TOOL[:executable]) + if (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.so') + TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC") + TOOLS_RELEASE_LINKER[:arguments] << "-shared" unless TOOLS_RELEASE_LINKER[:arguments].include?("-shared") + elsif (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.a') + TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC") + TOOLS_RELEASE_LINKER[:executable] = 'ar' + TOOLS_RELEASE_LINKER[:arguments] = ['rcs', '${2}', '${1}'].compact + end +end + +if (RELEASE_BUILD_USE_ASSEMBLY) +rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_ASM_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_assembly_file(task_name) + end + ]) do |object| + @ceedling[:generator].generate_object_file( + TOOLS_RELEASE_ASSEMBLER, + OPERATION_ASSEMBLE_SYM, + RELEASE_SYM, + object.source, + object.name ) +end +end + + +rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_C_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name, :error, true) + end + ]) do |object| + @ceedling[:generator].generate_object_file( + TOOLS_RELEASE_COMPILER, + OPERATION_COMPILE_SYM, + RELEASE_SYM, + object.source, + object.name, + @ceedling[:file_path_utils].form_release_build_c_list_filepath( object.name ), + @ceedling[:file_path_utils].form_release_dependencies_filepath( object.name ) ) +end + + +rule(/#{PROJECT_RELEASE_BUILD_TARGET}/) do |bin_file| + objects, libraries = @ceedling[:release_invoker].sort_objects_and_libraries(bin_file.prerequisites) + tool = TOOLS_RELEASE_LINKER.clone + lib_args = @ceedling[:release_invoker].convert_libraries_to_arguments(libraries) + lib_paths = @ceedling[:release_invoker].get_library_paths_to_arguments() + map_file = @ceedling[:configurator].project_release_build_map + @ceedling[:generator].generate_executable_file( + tool, + RELEASE_SYM, + objects, + bin_file.name, + map_file, + lib_args, + lib_paths ) + @ceedling[:release_invoker].artifactinate( bin_file.name, map_file, @ceedling[:configurator].release_build_artifacts ) +end + + +namespace RELEASE_SYM do + # use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks) + + namespace :compile do + rule(/^#{RELEASE_COMPILE_TASK_ROOT}\S+#{'\\'+EXTENSION_SOURCE}$/ => [ # compile task names by regex + proc do |task_name| + source = task_name.sub(/#{RELEASE_COMPILE_TASK_ROOT}/, '') + @ceedling[:file_finder].find_source_file(source, :error) + end + ]) do |compile| + @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:project_config_manager].process_release_config_change + @ceedling[:release_invoker].setup_and_invoke_c_objects( [compile.source] ) + end + end + + if (RELEASE_BUILD_USE_ASSEMBLY) + namespace :assemble do + rule(/^#{RELEASE_ASSEMBLE_TASK_ROOT}\S+#{'\\'+EXTENSION_ASSEMBLY}$/ => [ # assemble task names by regex + proc do |task_name| + source = task_name.sub(/#{RELEASE_ASSEMBLE_TASK_ROOT}/, '') + @ceedling[:file_finder].find_assembly_file(source) + end + ]) do |assemble| + @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:project_config_manager].process_release_config_change + @ceedling[:release_invoker].setup_and_invoke_asm_objects( [assemble.source] ) + end + end + end + +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake new file mode 100644 index 0000000..9550783 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake @@ -0,0 +1,15 @@ + + +rule(/#{PROJECT_RELEASE_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name, :error, true) + end + ]) do |dep| + @ceedling[:generator].generate_dependencies_file( + TOOLS_RELEASE_DEPENDENCIES_GENERATOR, + RELEASE_SYM, + dep.source, + @ceedling[:file_path_utils].form_release_build_c_object_filepath(dep.source), + dep.name) +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_tests.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_tests.rake new file mode 100644 index 0000000..61e15e2 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_tests.rake @@ -0,0 +1,73 @@ + + +rule(/#{PROJECT_TEST_FILE_PREFIX}#{'.+'+TEST_RUNNER_FILE_SUFFIX}#{'\\'+EXTENSION_SOURCE}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_test_input_for_runner_file(task_name) + end + ]) do |runner| + @ceedling[:generator].generate_test_runner(TEST_SYM, runner.source, runner.name) +end + +rule(/#{PROJECT_TEST_BUILD_OUTPUT_C_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |object| + if (File.basename(object.source) =~ /#{EXTENSION_SOURCE}$/) + @ceedling[:generator].generate_object_file( + TOOLS_TEST_COMPILER, + OPERATION_COMPILE_SYM, + TEST_SYM, + object.source, + object.name, + @ceedling[:file_path_utils].form_test_build_list_filepath( object.name ), + @ceedling[:file_path_utils].form_test_dependencies_filepath( object.name )) + elsif (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) + @ceedling[:generator].generate_object_file( + TOOLS_TEST_ASSEMBLER, + OPERATION_ASSEMBLE_SYM, + TEST_SYM, + object.source, + object.name ) + end +end + + +rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| + lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() + @ceedling[:generator].generate_executable_file( + TOOLS_TEST_LINKER, + TEST_SYM, + bin_file.prerequisites, + bin_file.name, + @ceedling[:file_path_utils].form_test_build_map_filepath( bin_file.name ), + lib_args, + lib_paths ) +end + + +rule(/#{PROJECT_TEST_RESULTS_PATH}\/#{'.+\\'+EXTENSION_TESTPASS}$/ => [ + proc do |task_name| + @ceedling[:file_path_utils].form_test_executable_filepath(task_name) + end + ]) do |test_result| + @ceedling[:generator].generate_test_results(TOOLS_TEST_FIXTURE, TEST_SYM, test_result.source, test_result.name) +end + + +namespace TEST_SYM do + # use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks) + + rule(/^#{TEST_TASK_ROOT}\S+$/ => [ # test task names by regex + proc do |task_name| + test = task_name.sub(/#{TEST_TASK_ROOT}/, '') + test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" if not (test.start_with?(PROJECT_TEST_FILE_PREFIX)) + @ceedling[:file_finder].find_test_from_file_path(test) + end + ]) do |test| + @ceedling[:rake_wrapper][:test_deps].invoke + @ceedling[:test_invoker].setup_and_invoke([test.source]) + end +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake b/CAN_App/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake new file mode 100644 index 0000000..7175ee3 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake @@ -0,0 +1,15 @@ + + +rule(/#{PROJECT_TEST_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |dep| + @ceedling[:generator].generate_dependencies_file( + TOOLS_TEST_DEPENDENCIES_GENERATOR, + TEST_SYM, + dep.source, + @ceedling[:file_path_utils].form_test_build_c_object_filepath(dep.source), + dep.name) +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/setupinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/setupinator.rb new file mode 100644 index 0000000..ea78fd9 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/setupinator.rb @@ -0,0 +1,53 @@ + +class Setupinator + + attr_reader :config_hash + attr_writer :ceedling + + def setup + @ceedling = {} + @config_hash = {} + end + + def load_project_files + @ceedling[:project_file_loader].find_project_files + return @ceedling[:project_file_loader].load_project_config + end + + def do_setup(config_hash) + @config_hash = config_hash + + # load up all the constants and accessors our rake files, objects, & external scripts will need; + # note: configurator modifies the cmock section of the hash with a couple defaults to tie + # project together - the modified hash is used to build cmock object + @ceedling[:configurator].populate_defaults( config_hash ) + @ceedling[:configurator].populate_unity_defaults( config_hash ) + @ceedling[:configurator].populate_cmock_defaults( config_hash ) + @ceedling[:configurator].find_and_merge_plugins( config_hash ) + @ceedling[:configurator].merge_imports( config_hash ) + @ceedling[:configurator].eval_environment_variables( config_hash ) + @ceedling[:configurator].tools_setup( config_hash ) + @ceedling[:configurator].eval_paths( config_hash ) + @ceedling[:configurator].standardize_paths( config_hash ) + @ceedling[:configurator].validate( config_hash ) + @ceedling[:configurator].build( config_hash, :environment ) + + @ceedling[:configurator].insert_rake_plugins( @ceedling[:configurator].rake_plugins ) + @ceedling[:configurator].tools_supplement_arguments( config_hash ) + + # merge in any environment variables plugins specify, after the main build + @ceedling[:plugin_manager].load_plugin_scripts( @ceedling[:configurator].script_plugins, @ceedling ) do |env| + @ceedling[:configurator].eval_environment_variables( env ) + @ceedling[:configurator].build_supplement( config_hash, env ) + end + + @ceedling[:plugin_reportinator].set_system_objects( @ceedling ) + @ceedling[:file_finder].prepare_search_sources + @ceedling[:loginator].setup_log_filepath + @ceedling[:project_config_manager].config_hash = config_hash + end + + def reset_defaults(config_hash) + @ceedling[:configurator].reset_defaults( config_hash ) + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/stream_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/stream_wrapper.rb new file mode 100644 index 0000000..7e16052 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/stream_wrapper.rb @@ -0,0 +1,28 @@ + +class StreamWrapper + + def stdout_override(&fnc) + @stdout_overide_fnc = fnc + end + + def stdout_puts(string) + if @stdout_overide_fnc + @stdout_overide_fnc.call(string) + else + $stdout.puts(string) + end + end + + def stdout_flush + $stdout.flush + end + + def stderr_puts(string) + $stderr.puts(string) + end + + def stderr_flush + $stderr.flush + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/streaminator.rb b/CAN_App/vendor/ceedling/lib/ceedling/streaminator.rb new file mode 100644 index 0000000..b8dcd07 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/streaminator.rb @@ -0,0 +1,40 @@ +require 'ceedling/constants' + +class Streaminator + + constructor :streaminator_helper, :verbosinator, :loginator, :stream_wrapper + + # for those objects for whom the configurator has already been instantiated, + # Streaminator is a convenience object for handling verbosity and writing to the std streams + + def stdout_puts(string, verbosity=Verbosity::NORMAL) + if (@verbosinator.should_output?(verbosity)) + @stream_wrapper.stdout_puts(string) + @stream_wrapper.stdout_flush + end + + # write to log as though Verbosity::OBNOXIOUS + @loginator.log( string, @streaminator_helper.extract_name($stdout) ) + end + + def stderr_puts(string, verbosity=Verbosity::NORMAL) + if (@verbosinator.should_output?(verbosity)) + @stream_wrapper.stderr_puts(string) + @stream_wrapper.stderr_flush + end + + # write to log as though Verbosity::OBNOXIOUS + @loginator.log( string, @streaminator_helper.extract_name($stderr) ) + end + + def stream_puts(stream, string, verbosity=Verbosity::NORMAL) + if (@verbosinator.should_output?(verbosity)) + stream.puts(string) + stream.flush + end + + # write to log as though Verbosity::OBNOXIOUS + @loginator.log( string, @streaminator_helper.extract_name(stream) ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/streaminator_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/streaminator_helper.rb new file mode 100644 index 0000000..9fb5cc0 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/streaminator_helper.rb @@ -0,0 +1,15 @@ + +class StreaminatorHelper + + def extract_name(stream) + name = case (stream.fileno) + when 0 then '#<IO:$stdin>' + when 1 then '#<IO:$stdout>' + when 2 then '#<IO:$stderr>' + else stream.inspect + end + + return name + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/system_utils.rb b/CAN_App/vendor/ceedling/lib/ceedling/system_utils.rb new file mode 100644 index 0000000..477aba4 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/system_utils.rb @@ -0,0 +1,37 @@ + +class Object + def deep_clone + Marshal::load(Marshal.dump(self)) + end +end + + +## +# Class containing system utility funcions. +class SystemUtils + + constructor :system_wrapper + + ## + # Sets up the class. + def setup + @tcsh_shell = nil + end + + ## + # Checks the system shell to see if it a tcsh shell. + def tcsh_shell? + # once run a single time, return state determined at that execution + return @tcsh_shell if not @tcsh_shell.nil? + + result = @system_wrapper.shell_backticks('echo $version') + + if ((result[:exit_code] == 0) and (result[:output].strip =~ /^tcsh/)) + @tcsh_shell = true + else + @tcsh_shell = false + end + + return @tcsh_shell + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/system_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/system_wrapper.rb new file mode 100644 index 0000000..2b0f1ed --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/system_wrapper.rb @@ -0,0 +1,80 @@ +require 'rbconfig' + +class SystemWrapper + + # static method for use in defaults + def self.windows? + return ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) if defined?(RbConfig) + return ((Config::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) + end + + # class method so as to be mockable for tests + def windows? + return SystemWrapper.windows? + end + + def module_eval(string) + return Object.module_eval("\"" + string + "\"") + end + + def eval(string) + return eval(string) + end + + def search_paths + return ENV['PATH'].split(File::PATH_SEPARATOR) + end + + def cmdline_args + return ARGV + end + + def env_set(name, value) + ENV[name] = value + end + + def env_get(name) + return ENV[name] + end + + def time_now + return Time.now.asctime + end + + def shell_backticks(command, boom = true) + retval = `#{command}`.freeze + $exit_code = ($?.exitstatus).freeze if boom + return { + :output => retval.freeze, + :exit_code => ($?.exitstatus).freeze + } + end + + def shell_system(command, boom = true) + system( command ) + $exit_code = ($?.exitstatus).freeze if boom + return { + :output => "".freeze, + :exit_code => ($?.exitstatus).freeze + } + end + + def add_load_path(path) + $LOAD_PATH.unshift(path) + end + + def require_file(path) + require(path) + end + + def ruby_success + # We are successful if we've never had an exit code that went boom (either because it's empty or it was 0) + return ($exit_code.nil? || ($exit_code == 0)) && ($!.nil? || $!.is_a?(SystemExit) && $!.success?) + end + + def constants_include?(item) + # forcing to strings provides consistency across Ruby versions + return Object.constants.map{|constant| constant.to_s}.include?(item.to_s) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/target_loader.rb b/CAN_App/vendor/ceedling/lib/ceedling/target_loader.rb new file mode 100644 index 0000000..7fbc095 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/target_loader.rb @@ -0,0 +1,38 @@ +module TargetLoader + class NoTargets < Exception; end + class NoDirectory < Exception; end + class NoDefault < Exception; end + class NoSuchTarget < Exception; end + + class RequestReload < Exception; end + + def self.inspect(config, target_name=nil) + unless config[:targets] + raise NoTargets + end + + targets = config[:targets] + unless targets[:targets_directory] + raise NoDirectory.new("No targets directory specified.") + end + unless targets[:default_target] + raise NoDefault.new("No default target specified.") + end + + target_path = lambda {|name| File.join(targets[:targets_directory], name + ".yml")} + + target = if target_name + target_path.call(target_name) + else + target_path.call(targets[:default_target]) + end + + unless File.exists? target + raise NoSuchTarget.new("No such target: #{target}") + end + + ENV['CEEDLING_MAIN_PROJECT_FILE'] = target + + raise RequestReload + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/task_invoker.rb b/CAN_App/vendor/ceedling/lib/ceedling/task_invoker.rb new file mode 100644 index 0000000..7bfabbb --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/task_invoker.rb @@ -0,0 +1,122 @@ +require 'ceedling/par_map' + +class TaskInvoker + + attr_accessor :first_run + + constructor :dependinator, :rake_utils, :rake_wrapper, :project_config_manager + + def setup + @test_regexs = [/^#{TEST_ROOT_NAME}:/] + @release_regexs = [/^#{RELEASE_ROOT_NAME}(:|$)/] + @first_run = true + end + + def add_test_task_regex(regex) + @test_regexs << regex + end + + def add_release_task_regex(regex) + @release_regexs << regex + end + + def test_invoked? + invoked = false + + @test_regexs.each do |regex| + invoked = true if (@rake_utils.task_invoked?(regex)) + break if invoked + end + + return invoked + end + + def release_invoked? + invoked = false + + @release_regexs.each do |regex| + invoked = true if (@rake_utils.task_invoked?(regex)) + break if invoked + end + + return invoked + end + + def invoked?(regex) + return @rake_utils.task_invoked?(regex) + end + + def reset_rake_task_for_changed_defines(file) + if !(file =~ /#{VENDORS_FILES.map{|ignore| '\b' + ignore.ext(File.extname(file)) + '\b'}.join('|')}$/) + @rake_wrapper[file].clear_actions if @first_run == false && @project_config_manager.test_defines_changed + @rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed + end + end + + def invoke_test_mocks(mocks) + @dependinator.enhance_mock_dependencies( mocks ) + mocks.each { |mock| + reset_rake_task_for_changed_defines( mock ) + @rake_wrapper[mock].invoke + } + end + + def invoke_test_runner(runner) + @dependinator.enhance_runner_dependencies( runner ) + reset_rake_task_for_changed_defines( runner ) + @rake_wrapper[runner].invoke + end + + def invoke_test_shallow_include_lists(files) + @dependinator.enhance_shallow_include_lists_dependencies( files ) + par_map(PROJECT_COMPILE_THREADS, files) do |file| + reset_rake_task_for_changed_defines( file ) + @rake_wrapper[file].invoke + end + end + + def invoke_test_preprocessed_files(files) + @dependinator.enhance_preprocesed_file_dependencies( files ) + par_map(PROJECT_COMPILE_THREADS, files) do |file| + reset_rake_task_for_changed_defines( file ) + @rake_wrapper[file].invoke + end + end + + def invoke_test_dependencies_files(files) + @dependinator.enhance_dependencies_dependencies( files ) + par_map(PROJECT_COMPILE_THREADS, files) do |file| + reset_rake_task_for_changed_defines( file ) + @rake_wrapper[file].invoke + end + end + + def invoke_test_objects(objects) + par_map(PROJECT_COMPILE_THREADS, objects) do |object| + reset_rake_task_for_changed_defines( object ) + @rake_wrapper[object].invoke + end + end + + def invoke_test_executable(file) + @rake_wrapper[file].invoke + end + + def invoke_test_results(result) + @dependinator.enhance_results_dependencies( result ) + @rake_wrapper[result].invoke + end + + def invoke_release_dependencies_files(files) + par_map(PROJECT_COMPILE_THREADS, files) do |file| + @rake_wrapper[file].invoke + end + end + + def invoke_release_objects(objects) + par_map(PROJECT_COMPILE_THREADS, objects) do |object| + @rake_wrapper[object].invoke + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_base.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_base.rake new file mode 100644 index 0000000..a35cde7 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_base.rake @@ -0,0 +1,116 @@ +require 'ceedling/constants' +require 'ceedling/file_path_utils' +require 'ceedling/version' + +desc "Display build environment version info." +task :version do + puts " Ceedling:: #{Ceedling::Version::CEEDLING}" + puts " Unity:: #{Ceedling::Version::UNITY}" + puts " CMock:: #{Ceedling::Version::CMOCK}" + puts " CException:: #{Ceedling::Version::CEXCEPTION}" +end + +desc "Set verbose output (silent:[#{Verbosity::SILENT}] - obnoxious:[#{Verbosity::OBNOXIOUS}])." +task :verbosity, :level do |t, args| + verbosity_level = args.level.to_i + + if (PROJECT_USE_MOCKS) + # don't store verbosity level in setupinator's config hash, use a copy; + # otherwise, the input configuration will change and trigger entire project rebuilds + hash = @ceedling[:setupinator].config_hash[:cmock].clone + hash[:verbosity] = verbosity_level + + @ceedling[:cmock_builder].manufacture( hash ) + end + + @ceedling[:configurator].project_verbosity = verbosity_level + + # control rake's verbosity with new setting + verbose( ((verbosity_level >= Verbosity::OBNOXIOUS) ? true : false) ) +end + +desc "Enable logging" +task :logging do + @ceedling[:configurator].project_logging = true +end + +# non advertised debug task +task :debug do + Rake::Task[:verbosity].invoke(Verbosity::DEBUG) + Rake.application.options.trace = true + @ceedling[:configurator].project_debug = true +end + +# non advertised sanity checking task +task :sanity_checks, :level do |t, args| + check_level = args.level.to_i + @ceedling[:configurator].sanity_checks = check_level +end + +# non advertised catch for calling upgrade in the wrong place +task :upgrade do + puts "WARNING: You're currently IN your project directory. Take a step out and try" + puts "again if you'd like to perform an upgrade." +end + +# list expanded environment variables +if (not ENVIRONMENT.empty?) +desc "List all configured environment variables." +task :environment do + env_list = [] + ENVIRONMENT.each do |env| + env.each_key do |key| + name = key.to_s.upcase + env_list.push(" - #{name}: \"#{env[key]}\"") + end + end + env_list.sort.each do |env_line| + puts env_line + end +end +end + +namespace :options do + + COLLECTION_PROJECT_OPTIONS.each do |option_path| + option = File.basename(option_path, '.yml') + + desc "Merge #{option} project options." + task option.to_sym do + hash = @ceedling[:project_config_manager].merge_options( @ceedling[:setupinator].config_hash, option_path ) + @ceedling[:setupinator].do_setup( hash ) + if @ceedling[:configurator].project_release_build + load(File.join(CEEDLING_LIB, 'ceedling', 'rules_release.rake')) + end + end + end + + # This is to give nice errors when typing options + rule /^options:.*/ do |t, args| + filename = t.to_s.split(':')[-1] + '.yml' + filelist = COLLECTION_PROJECT_OPTIONS.map{|s| File.basename(s) } + @ceedling[:file_finder].find_file_from_list(filename, filelist, :error) + end + + # This will output the fully-merged tools options to their own project.yml file + desc "Export tools options to a new project file" + task :export, :filename do |t, args| + outfile = args.filename || 'tools.yml' + toolcfg = {} + @ceedling[:configurator].project_config_hash.each_pair do |k,v| + toolcfg[k] = v if (k.to_s[0..5] == 'tools_') + end + File.open(outfile,'w') {|f| f << toolcfg.to_yaml({:indentation => 2})} + end +end + + +# do not present task if there's no plugins +if (not PLUGINS_ENABLED.empty?) +desc "Execute plugin result summaries (no build triggering)." +task :summary do + @ceedling[:plugin_manager].summary + puts "\nNOTE: Summaries may be out of date with project sources.\n\n" +end +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_filesystem.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_filesystem.rake new file mode 100644 index 0000000..7b950ca --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_filesystem.rake @@ -0,0 +1,113 @@ + +# rather than require 'rake/clean' & try to override, we replicate for finer control +CLEAN = Rake::FileList["**/*~", "**/*.bak"] +CLOBBER = Rake::FileList.new + +CLEAN.clear_exclude.exclude { |fn| fn.pathmap("%f") == 'core' && File.directory?(fn) } + +CLEAN.include(File.join(PROJECT_TEST_BUILD_OUTPUT_PATH, '*')) +CLEAN.include(File.join(PROJECT_TEST_RESULTS_PATH, '*')) +CLEAN.include(File.join(PROJECT_TEST_DEPENDENCIES_PATH, '*')) +CLEAN.include(File.join(PROJECT_BUILD_RELEASE_ROOT, '*.*')) +CLEAN.include(File.join(PROJECT_RELEASE_BUILD_OUTPUT_PATH, '*')) +CLEAN.include(File.join(PROJECT_RELEASE_DEPENDENCIES_PATH, '*')) + +CLOBBER.include(File.join(PROJECT_BUILD_ARTIFACTS_ROOT, '**/*')) +CLOBBER.include(File.join(PROJECT_BUILD_TESTS_ROOT, '**/*')) +CLOBBER.include(File.join(PROJECT_BUILD_RELEASE_ROOT, '**/*')) +CLOBBER.include(File.join(PROJECT_LOG_PATH, '**/*')) +CLOBBER.include(File.join(PROJECT_TEMP_PATH, '**/*')) + +# just in case they're using git, let's make sure we allow them to preserved the build directory if desired. +CLOBBER.exclude(File.join(TESTS_BASE_PATH), '**/.gitkeep') + +# because of cmock config, mock path can optionally exist apart from standard test build paths +CLOBBER.include(File.join(CMOCK_MOCK_PATH, '*')) + +REMOVE_FILE_PROC = Proc.new { |fn| rm_r fn rescue nil } + +# redefine clean so we can override how it advertises itself +desc "Delete all build artifacts and temporary products." +task(:clean) do + # because :clean is a prerequisite for :clobber, intelligently display the progress message + if (not @ceedling[:task_invoker].invoked?(/^clobber$/)) + @ceedling[:streaminator].stdout_puts("\nCleaning build artifacts...\n(For large projects, this task may take a long time to complete)\n\n") + end + begin + CLEAN.each { |fn| REMOVE_FILE_PROC.call(fn) } + rescue + end +end + +# redefine clobber so we can override how it advertises itself +desc "Delete all generated files (and build artifacts)." +task(:clobber => [:clean]) do + @ceedling[:streaminator].stdout_puts("\nClobbering all generated files...\n(For large projects, this task may take a long time to complete)\n\n") + begin + CLOBBER.each { |fn| REMOVE_FILE_PROC.call(fn) } + @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:dependinator].touch_force_rebuild_files + rescue + end +end + +# create a directory task for each of the paths, so we know how to build them +PROJECT_BUILD_PATHS.each { |path| directory(path) } + +# create a single directory task which verifies all the others get built +task :directories => PROJECT_BUILD_PATHS + +# when the force file doesn't exist, it probably means we clobbered or are on a fresh +# install. In either case, stuff was deleted, so assume we want to rebuild it all +file @ceedling[:configurator].project_test_force_rebuild_filepath do + unless File.exists?(@ceedling[:configurator].project_test_force_rebuild_filepath) + @ceedling[:dependinator].touch_force_rebuild_files + end +end + +# list paths discovered at load time +namespace :paths do + standard_paths = ['test','source','include'] + paths = @ceedling[:setupinator].config_hash[:paths].keys.map{|n| n.to_s.downcase} + paths = (paths + standard_paths).uniq + paths.each do |name| + path_list = Object.const_get("COLLECTION_PATHS_#{name.upcase}") + + if (path_list.size != 0) || (standard_paths.include?(name)) + desc "List all collected #{name} paths." + task(name.to_sym) { puts "#{name} paths:"; path_list.sort.each {|path| puts " - #{path}" } } + end + end + +end + + +# list files & file counts discovered at load time +namespace :files do + + categories = [ + ['test', COLLECTION_ALL_TESTS], + ['source', COLLECTION_ALL_SOURCE], + ['include', COLLECTION_ALL_HEADERS], + ['support', COLLECTION_ALL_SUPPORT] + ] + + using_assembly = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) || + (defined?(RELEASE_BUILD_USE_ASSEMBLY) && RELEASE_BUILD_USE_ASSEMBLY) + categories << ['assembly', COLLECTION_ALL_ASSEMBLY] if using_assembly + + categories.each do |category| + name = category[0] + collection = category[1] + + desc "List all collected #{name} files." + task(name.to_sym) do + puts "#{name} files:" + collection.sort.each { |filepath| puts " - #{filepath}" } + puts "file count: #{collection.size}" + end + end + +end + + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_release.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_release.rake new file mode 100644 index 0000000..b313b2f --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_release.rake @@ -0,0 +1,30 @@ +require 'ceedling/constants' +require 'ceedling/file_path_utils' + + +desc "Build release target." +task RELEASE_SYM => [:directories] do + header = "Release build '#{File.basename(PROJECT_RELEASE_BUILD_TARGET)}'" + @ceedling[:streaminator].stdout_puts("\n\n#{header}\n#{'-' * header.length}") + + begin + @ceedling[:plugin_manager].pre_release + + core_objects = [] + extra_objects = @ceedling[:file_path_utils].form_release_build_c_objects_filelist( COLLECTION_RELEASE_ARTIFACT_EXTRA_LINK_OBJECTS ) + + @ceedling[:project_config_manager].process_release_config_change + core_objects.concat( @ceedling[:release_invoker].setup_and_invoke_c_objects( COLLECTION_ALL_SOURCE ) ) + + # if assembler use isn't enabled, COLLECTION_ALL_ASSEMBLY is empty array & nothing happens + core_objects.concat( @ceedling[:release_invoker].setup_and_invoke_asm_objects( COLLECTION_ALL_ASSEMBLY ) ) + + # if we're using libraries, we need to add those to our collection as well + library_objects = (defined? LIBRARIES_RELEASE && !LIBRARIES_RELEASE.empty?) ? LIBRARIES_RELEASE.flatten.compact : [] + file( PROJECT_RELEASE_BUILD_TARGET => (core_objects + extra_objects + library_objects) ) + Rake::Task[PROJECT_RELEASE_BUILD_TARGET].invoke + ensure + @ceedling[:plugin_manager].post_release + end +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake new file mode 100644 index 0000000..db2be5f --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake @@ -0,0 +1,9 @@ +require 'ceedling/constants' + +namespace REFRESH_SYM do + + task RELEASE_SYM do + @ceedling[:release_invoker].refresh_c_deep_dependencies + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests.rake new file mode 100644 index 0000000..6c51ebc --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests.rake @@ -0,0 +1,62 @@ +require 'ceedling/constants' + +task :test_deps => [:directories] + +task :test => [:test_deps] do + Rake.application['test:all'].invoke +end + +namespace TEST_SYM do + + desc "Run all unit tests (also just 'test' works)." + task :all => [:test_deps] do + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS) + end + + desc "Run single test ([*] real test or source file name, no path)." + task :* do + message = "\nOops! '#{TEST_ROOT_NAME}:*' isn't a real task. " + + "Use a real test or source file name (no path) in place of the wildcard.\n" + + "Example: rake #{TEST_ROOT_NAME}:foo.c\n\n" + + @ceedling[:streaminator].stdout_puts( message ) + end + + desc "Run tests for changed files." + task :delta => [:test_deps] do + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:force_run => false}) + end + + desc "Just build tests without running." + task :build_only => [:test_deps] do + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:build_only => true}) + end + + desc "Run tests by matching regular expression pattern." + task :pattern, [:regex] => [:test_deps] do |t, args| + matches = [] + + COLLECTION_ALL_TESTS.each { |test| matches << test if (test =~ /#{args.regex}/) } + + if (matches.size > 0) + @ceedling[:test_invoker].setup_and_invoke(matches, TEST_SYM, {:force_run => false}) + else + @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.") + end + end + + desc "Run tests whose test path contains [dir] or [dir] substring." + task :path, [:dir] => [:test_deps] do |t, args| + matches = [] + + COLLECTION_ALL_TESTS.each { |test| matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/')) } + + if (matches.size > 0) + @ceedling[:test_invoker].setup_and_invoke(matches, TEST_SYM, {:force_run => false}) + else + @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.") + end + end + +end + diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake new file mode 100644 index 0000000..f899407 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake @@ -0,0 +1,9 @@ +require 'ceedling/constants' + +namespace REFRESH_SYM do + + task TEST_SYM do + @ceedling[:test_invoker].refresh_deep_dependencies + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tasks_vendor.rake b/CAN_App/vendor/ceedling/lib/ceedling/tasks_vendor.rake new file mode 100644 index 0000000..63c2ca5 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tasks_vendor.rake @@ -0,0 +1,35 @@ +require 'ceedling/constants' +require 'ceedling/file_path_utils' + +# create file dependencies to ensure C-based components of vendor tools are recompiled when they are updated with new versions +# forming these explicitly rather than depend on auxiliary dependencies so all scenarios are explicitly covered + +file( @ceedling[:file_path_utils].form_test_build_c_object_filepath( UNITY_C_FILE ) => [ + File.join( UNITY_VENDOR_PATH, UNITY_LIB_PATH, UNITY_C_FILE ), + File.join( UNITY_VENDOR_PATH, UNITY_LIB_PATH, UNITY_H_FILE ), + File.join( UNITY_VENDOR_PATH, UNITY_LIB_PATH, UNITY_INTERNALS_H_FILE ) ] + ) + + +if (PROJECT_USE_MOCKS) +file( @ceedling[:file_path_utils].form_test_build_c_object_filepath( CMOCK_C_FILE ) => [ + File.join( CMOCK_VENDOR_PATH, CMOCK_LIB_PATH, CMOCK_C_FILE ), + File.join( CMOCK_VENDOR_PATH, CMOCK_LIB_PATH, CMOCK_H_FILE ) ] + ) +end + + +if (PROJECT_USE_EXCEPTIONS) +file( @ceedling[:file_path_utils].form_test_build_c_object_filepath( CEXCEPTION_C_FILE ) => [ + File.join( CEXCEPTION_VENDOR_PATH, CEXCEPTION_LIB_PATH, CEXCEPTION_C_FILE ), + File.join( CEXCEPTION_VENDOR_PATH, CEXCEPTION_LIB_PATH, CEXCEPTION_H_FILE ) ] + ) +end + + +if (PROJECT_USE_EXCEPTIONS and PROJECT_RELEASE_BUILD) +file( @ceedling[:file_path_utils].form_release_build_c_object_filepath( CEXCEPTION_C_FILE ) => [ + File.join( CEXCEPTION_VENDOR_PATH, CEXCEPTION_LIB_PATH, CEXCEPTION_C_FILE ), + File.join( CEXCEPTION_VENDOR_PATH, CEXCEPTION_LIB_PATH, CEXCEPTION_H_FILE ) ] + ) +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/test_includes_extractor.rb b/CAN_App/vendor/ceedling/lib/ceedling/test_includes_extractor.rb new file mode 100644 index 0000000..393b0be --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/test_includes_extractor.rb @@ -0,0 +1,111 @@ + +class TestIncludesExtractor + + constructor :configurator, :yaml_wrapper, :file_wrapper + + def setup + @includes = {} + @mocks = {} + end + + + # for includes_list file, slurp up array from yaml file and sort & store includes + def parse_includes_list(includes_list) + gather_and_store_includes( includes_list, @yaml_wrapper.load(includes_list) ) + end + + # open, scan for, and sort & store includes of test file + def parse_test_file(test) + gather_and_store_includes( test, extract_from_file(test) ) + end + + # open, scan for, and sort & store includes of test file + def parse_test_file_source_include(test) + return extract_source_include_from_file(test) + end + + # mocks with no file extension + def lookup_raw_mock_list(test) + file_key = form_file_key(test) + return [] if @mocks[file_key].nil? + return @mocks[file_key] + end + + # includes with file extension + def lookup_includes_list(file) + file_key = form_file_key(file) + return [] if (@includes[file_key]).nil? + return @includes[file_key] + end + + private ################################# + + def form_file_key(filepath) + return File.basename(filepath).to_sym + end + + def extract_from_file(file) + includes = [] + header_extension = @configurator.extension_header + + contents = @file_wrapper.read(file) + + # remove line comments + contents = contents.gsub(/\/\/.*$/, '') + # remove block comments + contents = contents.gsub(/\/\*.*?\*\//m, '') + + contents.split("\n").each do |line| + # look for include statement + scan_results = line.scan(/#include\s+\"\s*(.+#{'\\'+header_extension})\s*\"/) + + includes << scan_results[0][0] if (scan_results.size > 0) + + # look for TEST_FILE statement + scan_results = line.scan(/TEST_FILE\(\s*\"\s*(.+\.\w+)\s*\"\s*\)/) + + includes << scan_results[0][0] if (scan_results.size > 0) + end + + return includes.uniq + end + + def extract_source_include_from_file(file) + source_includes = [] + source_extension = @configurator.extension_source + + contents = @file_wrapper.read(file) + + # remove line comments + contents = contents.gsub(/\/\/.*$/, '') + # remove block comments + contents = contents.gsub(/\/\*.*?\*\//m, '') + + contents.split("\n").each do |line| + # look for include statement + scan_results = line.scan(/#include\s+\"\s*(.+#{'\\'+source_extension})\s*\"/) + + source_includes << scan_results[0][0] if (scan_results.size > 0) + end + + return source_includes.uniq + end + + def gather_and_store_includes(file, includes) + mock_prefix = @configurator.cmock_mock_prefix + header_extension = @configurator.extension_header + file_key = form_file_key(file) + @mocks[file_key] = [] + + # add includes to lookup hash + @includes[file_key] = includes + + includes.each do |include_file| + # check if include is a mock + scan_results = include_file.scan(/(#{mock_prefix}.+)#{'\\'+header_extension}/) + # add mock to lookup hash + @mocks[file_key] << scan_results[0][0] if (scan_results.size > 0) + end + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/test_invoker.rb b/CAN_App/vendor/ceedling/lib/ceedling/test_invoker.rb new file mode 100644 index 0000000..ae686a1 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/test_invoker.rb @@ -0,0 +1,165 @@ +require 'ceedling/constants' + + +class TestInvoker + + attr_reader :sources, :tests, :mocks + + constructor :configurator, + :test_invoker_helper, + :plugin_manager, + :streaminator, + :preprocessinator, + :task_invoker, + :dependinator, + :project_config_manager, + :build_invoker_utils, + :file_path_utils, + :file_wrapper + + def setup + @sources = [] + @tests = [] + @mocks = [] + end + + + # Convert libraries configuration form YAML configuration + # into a string that can be given to the compiler. + def convert_libraries_to_arguments() + args = ((@configurator.project_config_hash[:libraries_test] || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten + if (defined? LIBRARIES_FLAG) + args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) } + end + return args + end + + def get_library_paths_to_arguments() + paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : [] + if (defined? LIBRARIES_PATH_FLAG) + paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) } + end + return paths + end + + def setup_and_invoke(tests, context=TEST_SYM, options={:force_run => true, :build_only => false}) + + @tests = tests + + @project_config_manager.process_test_config_change + + @tests.each do |test| + # announce beginning of test run + header = "Test '#{File.basename(test)}'" + @streaminator.stdout_puts("\n\n#{header}\n#{'-' * header.length}") + + begin + @plugin_manager.pre_test( test ) + test_name ="#{File.basename(test)}".chomp('.c') + def_test_key="defines_#{test_name.downcase}" + + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition + defs_bkp = Array.new(COLLECTION_DEFINES_TEST_AND_VENDOR) + tst_defs_cfg = Array.new(defs_bkp) + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + tst_defs_cfg.replace(@configurator.project_config_hash[def_test_key.to_sym]) + tst_defs_cfg .concat(COLLECTION_DEFINES_VENDOR) if COLLECTION_DEFINES_VENDOR + end + if @configurator.defines_use_test_definition + tst_defs_cfg << File.basename(test, ".*").strip.upcase.sub(/@.*$/, "") + end + COLLECTION_DEFINES_TEST_AND_VENDOR.replace(tst_defs_cfg) + end + + # redefine the project out path and preprocessor defines + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + @streaminator.stdout_puts("Updating test definitions for #{test_name}", Verbosity::NORMAL) + orig_path = @configurator.project_test_build_output_path + @configurator.project_config_hash[:project_test_build_output_path] = File.join(@configurator.project_test_build_output_path, test_name) + @file_wrapper.mkdir(@configurator.project_test_build_output_path) + end + + # collect up test fixture pieces & parts + runner = @file_path_utils.form_runner_filepath_from_test( test ) + mock_list = @preprocessinator.preprocess_test_and_invoke_test_mocks( test ) + sources = @test_invoker_helper.extract_sources( test ) + extras = @configurator.collection_test_fixture_extra_link_objects + core = [test] + mock_list + sources + objects = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras ).uniq + results_pass = @file_path_utils.form_pass_results_filepath( test ) + results_fail = @file_path_utils.form_fail_results_filepath( test ) + + # identify all the objects shall not be linked and then remove them from objects list. + no_link_objects = @file_path_utils.form_test_build_objects_filelist(@preprocessinator.preprocess_shallow_source_includes( test )) + objects = objects.uniq - no_link_objects + + @project_config_manager.process_test_defines_change(@project_config_manager.filter_internal_sources(sources)) + + # clean results files so we have a missing file with which to kick off rake's dependency rules + @test_invoker_helper.clean_results( {:pass => results_pass, :fail => results_fail}, options ) + + # load up auxiliary dependencies so deep changes cause rebuilding appropriately + @test_invoker_helper.process_deep_dependencies( core ) do |dependencies_list| + @dependinator.load_test_object_deep_dependencies( dependencies_list ) + end + + # tell rake to create test runner if needed + @task_invoker.invoke_test_runner( runner ) + + # enhance object file dependencies to capture externalities influencing regeneration + @dependinator.enhance_test_build_object_dependencies( objects ) + + # associate object files with executable + @dependinator.enhance_test_executable_dependencies( test, objects ) + + # build test objects + @task_invoker.invoke_test_objects( objects ) + + # if the option build_only has been specified, build only the executable + # but don't run the test + if (options[:build_only]) + executable = @file_path_utils.form_test_executable_filepath( test ) + @task_invoker.invoke_test_executable( executable ) + else + # 3, 2, 1... launch + @task_invoker.invoke_test_results( results_pass ) + end + rescue => e + @build_invoker_utils.process_exception( e, context ) + ensure + @plugin_manager.post_test( test ) + # restore the project test defines + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition + COLLECTION_DEFINES_TEST_AND_VENDOR.replace(defs_bkp) + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + @configurator.project_config_hash[:project_test_build_output_path] = orig_path + @streaminator.stdout_puts("Restored defines and build path to standard", Verbosity::NORMAL) + end + end + end + + # store away what's been processed + @mocks.concat( mock_list ) + @sources.concat( sources ) + + @task_invoker.first_run = false + end + + # post-process collected mock list + @mocks.uniq! + + # post-process collected sources list + @sources.uniq! + end + + + def refresh_deep_dependencies + @file_wrapper.rm_f( + @file_wrapper.directory_listing( + File.join( @configurator.project_test_dependencies_path, '*' + @configurator.extension_dependencies ) ) ) + + @test_invoker_helper.process_deep_dependencies( + @configurator.collection_all_tests + @configurator.collection_all_source ) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/test_invoker_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/test_invoker_helper.rb new file mode 100644 index 0000000..403d93e --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/test_invoker_helper.rb @@ -0,0 +1,32 @@ + +class TestInvokerHelper + + constructor :configurator, :task_invoker, :test_includes_extractor, :file_finder, :file_path_utils, :file_wrapper + + def clean_results(results, options) + @file_wrapper.rm_f( results[:fail] ) + @file_wrapper.rm_f( results[:pass] ) if (options[:force_run]) + end + + def process_deep_dependencies(files) + return if (not @configurator.project_use_deep_dependencies) + + dependencies_list = @file_path_utils.form_test_dependencies_filelist( files ).uniq + + if @configurator.project_generate_deep_dependencies + @task_invoker.invoke_test_dependencies_files( dependencies_list ) + end + + yield( dependencies_list ) if block_given? + end + + def extract_sources(test) + sources = [] + includes = @test_includes_extractor.lookup_includes_list(test) + + includes.each { |include| sources << @file_finder.find_compilation_input_file(include, :ignore) } + + return sources.compact + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tool_executor.rb b/CAN_App/vendor/ceedling/lib/ceedling/tool_executor.rb new file mode 100644 index 0000000..0ab5ddc --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tool_executor.rb @@ -0,0 +1,229 @@ +require 'ceedling/constants' +require 'benchmark' + +class ShellExecutionException < RuntimeError + attr_reader :shell_result + def initialize(shell_result) + @shell_result = shell_result + end +end + +class ToolExecutor + + constructor :configurator, :tool_executor_helper, :streaminator, :system_wrapper + + def setup + @tool_name = '' + @executable = '' + end + + # build up a command line from yaml provided config + + # @param extra_params is an array of parameters to append to executable + def build_command_line(tool_config, extra_params, *args) + @tool_name = tool_config[:name] + @executable = tool_config[:executable] + + command = {} + + # basic premise is to iterate top to bottom through arguments using '$' as + # a string replacement indicator to expand globals or inline yaml arrays + # into command line arguments via substitution strings + # executable must be quoted if it includes spaces (common on windows) + executable = @tool_executor_helper.osify_path_separators( expandify_element(@executable, *args) ) + executable = "\"#{executable}\"" if executable.include?(' ') + command[:line] = [ + executable, + extra_params.join(' ').strip, + build_arguments(tool_config[:arguments], *args), + ].reject{|s| s.nil? || s.empty?}.join(' ').strip + + command[:options] = { + :stderr_redirect => @tool_executor_helper.stderr_redirection(tool_config, @configurator.project_logging), + :background_exec => tool_config[:background_exec] + } + + return command + end + + + # shell out, execute command, and return response + def exec(command, options={}, args=[]) + options[:boom] = true if (options[:boom].nil?) + options[:stderr_redirect] = StdErrRedirect::NONE if (options[:stderr_redirect].nil?) + options[:background_exec] = BackgroundExec::NONE if (options[:background_exec].nil?) + # build command line + command_line = [ + @tool_executor_helper.background_exec_cmdline_prepend( options ), + command.strip, + args, + @tool_executor_helper.stderr_redirect_cmdline_append( options ), + @tool_executor_helper.background_exec_cmdline_append( options ), + ].flatten.compact.join(' ') + + @streaminator.stderr_puts("Verbose: #{__method__.to_s}(): #{command_line}", Verbosity::DEBUG) + + shell_result = {} + + # depending on background exec option, we shell out differently + time = Benchmark.realtime do + if (options[:background_exec] != BackgroundExec::NONE) + shell_result = @system_wrapper.shell_system( command_line, options[:boom] ) + else + shell_result = @system_wrapper.shell_backticks( command_line, options[:boom] ) + end + end + shell_result[:time] = time + + #scrub the string for illegal output + unless shell_result[:output].nil? + shell_result[:output] = shell_result[:output].scrub if "".respond_to?(:scrub) + shell_result[:output].gsub!(/\033\[\d\dm/,'') + end + + @tool_executor_helper.print_happy_results( command_line, shell_result, options[:boom] ) + @tool_executor_helper.print_error_results( command_line, shell_result, options[:boom] ) + + # go boom if exit code isn't 0 (but in some cases we don't want a non-0 exit code to raise) + raise ShellExecutionException.new(shell_result) if ((shell_result[:exit_code] != 0) and options[:boom]) + + return shell_result + end + + + private ############################# + + + def build_arguments(config, *args) + build_string = '' + + return nil if (config.nil?) + + # iterate through each argument + + # the yaml blob array needs to be flattened so that yaml substitution + # is handled correctly, since it creates a nested array when an anchor is + # dereferenced + config.flatten.each do |element| + argument = '' + + case(element) + # if we find a simple string then look for string replacement operators + # and expand with the parameters in this method's argument list + when String then argument = expandify_element(element, *args) + # if we find a hash, then we grab the key as a substitution string and expand the + # hash's value(s) within that substitution string + when Hash then argument = dehashify_argument_elements(element) + end + + build_string.concat("#{argument} ") if (argument.length > 0) + end + + build_string.strip! + return build_string if (build_string.length > 0) + return nil + end + + + # handle simple text string argument & argument array string replacement operators + def expandify_element(element, *args) + match = // + to_process = nil + args_index = 0 + + # handle ${#} input replacement + if (element =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN) + args_index = ($2.to_i - 1) + + if (args.nil? or args[args_index].nil?) + @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' expected valid argument data to accompany replacement operator #{$1}.", Verbosity::ERRORS) + raise + end + + match = /#{Regexp.escape($1)}/ + to_process = args[args_index] + end + + # simple string argument: replace escaped '\$' and strip + element.sub!(/\\\$/, '$') + element.strip! + + # handle inline ruby execution + if (element =~ RUBY_EVAL_REPLACEMENT_PATTERN) + element.replace(eval($1)) + end + + build_string = '' + + # handle array or anything else passed into method to be expanded in place of replacement operators + case (to_process) + when Array then to_process.each {|value| build_string.concat( "#{element.sub(match, value.to_s)} " ) } if (to_process.size > 0) + else build_string.concat( element.sub(match, to_process.to_s) ) + end + + # handle inline ruby string substitution + if (build_string =~ RUBY_STRING_REPLACEMENT_PATTERN) + build_string.replace(@system_wrapper.module_eval(build_string)) + end + + return build_string.strip + end + + + # handle argument hash: keys are substitution strings, values are data to be expanded within substitution strings + def dehashify_argument_elements(hash) + build_string = '' + elements = [] + + # grab the substitution string (hash key) + substitution = hash.keys[0].to_s + # grab the string(s) to squirt into the substitution string (hash value) + expand = hash[hash.keys[0]] + + if (expand.nil?) + @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' could not expand nil elements for substitution string '#{substitution}'.", Verbosity::ERRORS) + raise + end + + # array-ify expansion input if only a single string + expansion = ((expand.class == String) ? [expand] : expand) + + expansion.each do |item| + # code eval substitution + if (item =~ RUBY_EVAL_REPLACEMENT_PATTERN) + elements << eval($1) + # string eval substitution + elsif (item =~ RUBY_STRING_REPLACEMENT_PATTERN) + elements << @system_wrapper.module_eval(item) + # global constants + elsif (@system_wrapper.constants_include?(item)) + const = Object.const_get(item) + if (const.nil?) + @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' found constant '#{item}' to be nil.", Verbosity::ERRORS) + raise + else + elements << const + end + elsif (item.class == Array) + elements << item + elsif (item.class == String) + @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' cannot expand nonexistent value '#{item}' for substitution string '#{substitution}'.", Verbosity::ERRORS) + raise + else + @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' cannot expand value having type '#{item.class}' for substitution string '#{substitution}'.", Verbosity::ERRORS) + raise + end + end + + # expand elements (whether string or array) into substitution string & replace escaped '\$' + elements.flatten! + elements.each do |element| + build_string.concat( substitution.sub(/([^\\]*)\$/, "\\1#{element}") ) # don't replace escaped '\$' but allow us to replace just a lonesome '$' + build_string.gsub!(/\\\$/, '$') + build_string.concat(' ') + end + + return build_string.strip + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/tool_executor_helper.rb b/CAN_App/vendor/ceedling/lib/ceedling/tool_executor_helper.rb new file mode 100644 index 0000000..de4cafe --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/tool_executor_helper.rb @@ -0,0 +1,164 @@ +require 'ceedling/constants' # for Verbosity enumeration & $stderr redirect enumeration + +## +# Helper functions for the tool executor +class ToolExecutorHelper + + constructor :streaminator, :system_utils, :system_wrapper + + ## + # Returns the stderr redirection based on the config and logging. + # ==== Attributes + # + # * _tool_config_: A hash containing config information. + # * _logging_: A boolean representing if logging is enabled or not. + # + def stderr_redirection(tool_config, logging) + # if there's no logging enabled, return :stderr_redirect unmodified + return tool_config[:stderr_redirect] if (not logging) + + # if there is logging enabled but the redirect is a custom value (not enum), return the custom string + return tool_config[:stderr_redirect] if (tool_config[:stderr_redirect].class == String) + + # if logging is enabled but there's no custom string, return the AUTO enumeration so $stderr goes into the log + return StdErrRedirect::AUTO + end + + + ## + # Returns the background execution prepend based on the config. + # ==== Attributes + # + # * _tool_config_: A hash containing config information. + # + def background_exec_cmdline_prepend(tool_config) + return nil if (tool_config.nil? || tool_config[:background_exec].nil?) + + config_exec = tool_config[:background_exec] + + if ((config_exec == BackgroundExec::AUTO) and (@system_wrapper.windows?)) + return 'start' + end + + if (config_exec == BackgroundExec::WIN) + return 'start' + end + + return nil + end + + + ## + # Modifies an executables path based on platform. + # ==== Attributes + # + # * _executable_: The executable's path. + # + def osify_path_separators(executable) + return executable.gsub(/\//, '\\') if (@system_wrapper.windows?) + return executable + end + + ## + # Returns the stderr redirect append based on the config. + # ==== Attributes + # + # * _tool_config_: A hash containing config information. + # + def stderr_redirect_cmdline_append(tool_config) + return nil if (tool_config.nil? || tool_config[:stderr_redirect].nil?) + + config_redirect = tool_config[:stderr_redirect] + redirect = StdErrRedirect::NONE + + if (config_redirect == StdErrRedirect::AUTO) + if (@system_wrapper.windows?) + redirect = StdErrRedirect::WIN + elsif (@system_utils.tcsh_shell?) + redirect = StdErrRedirect::TCSH + else + redirect = StdErrRedirect::UNIX + end + end + + case redirect + # we may need more complicated processing after some learning with various environments + when StdErrRedirect::NONE then nil + when StdErrRedirect::WIN then '2>&1' + when StdErrRedirect::UNIX then '2>&1' + when StdErrRedirect::TCSH then '|&' + else redirect.to_s + end + end + + ## + # Returns the background execution append based on the config. + # ==== Attributes + # + # * _tool_config_: A hash containing config information. + # + def background_exec_cmdline_append(tool_config) + return nil if (tool_config.nil? || tool_config[:background_exec].nil?) + + config_exec = tool_config[:background_exec] + + # if :auto & windows, then we already prepended 'start' and should append nothing + return nil if ((config_exec == BackgroundExec::AUTO) and (@system_wrapper.windows?)) + + # if :auto & not windows, then we append standard '&' + return '&' if ((config_exec == BackgroundExec::AUTO) and (not @system_wrapper.windows?)) + + # if explicitly Unix, then append '&' + return '&' if (config_exec == BackgroundExec::UNIX) + + # * _command_str_: A hash containing config information. + # all other cases, including :none, :win, & anything unrecognized, append nothing + return nil + end + + ## + # Outputs success results if command succeeded and we have verbosity cranked up. + # ==== Attributes + # + # * _command_str_: The command ran. + # * _shell_results_: The outputs of the command including exit code and + # output. + # * _boom_: A boolean representing if a non zero result is erroneous. + # + def print_happy_results(command_str, shell_result, boom=true) + if ((shell_result[:exit_code] == 0) or ((shell_result[:exit_code] != 0) and not boom)) + output = "> Shell executed command:\n" + output += "'#{command_str}'\n" + output += "> Produced output:\n" if (not shell_result[:output].empty?) + output += "#{shell_result[:output].strip}\n" if (not shell_result[:output].empty?) + output += "> And exited with status: [#{shell_result[:exit_code]}].\n" if (shell_result[:exit_code] != 0) + output += "\n" + + @streaminator.stdout_puts(output, Verbosity::OBNOXIOUS) + end + end + + ## + # Outputs failures results if command failed and we have verbosity set to minimum error level. + # ==== Attributes + # + # * _command_str_: The command ran. + # * _shell_results_: The outputs of the command including exit code and + # output. + # * _boom_: A boolean representing if a non zero result is erroneous. + # + def print_error_results(command_str, shell_result, boom=true) + if ((shell_result[:exit_code] != 0) and boom) + output = "ERROR: Shell command failed.\n" + output += "> Shell executed command:\n" + output += "'#{command_str}'\n" + output += "> Produced output:\n" if (not shell_result[:output].empty?) + output += "#{shell_result[:output].strip}\n" if (not shell_result[:output].empty?) + output += "> And exited with status: [#{shell_result[:exit_code]}].\n" if (shell_result[:exit_code] != nil) + output += "> And then likely crashed.\n" if (shell_result[:exit_code] == nil) + output += "\n" + + @streaminator.stderr_puts(output, Verbosity::ERRORS) + end + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/verbosinator.rb b/CAN_App/vendor/ceedling/lib/ceedling/verbosinator.rb new file mode 100644 index 0000000..e8ed38d --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/verbosinator.rb @@ -0,0 +1,10 @@ + +class Verbosinator + + constructor :configurator + + def should_output?(level) + return (level <= @configurator.project_verbosity) + end + +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/version.rb b/CAN_App/vendor/ceedling/lib/ceedling/version.rb new file mode 100644 index 0000000..ebda10b --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/version.rb @@ -0,0 +1,54 @@ + +# @private +module Ceedling + module Version + { "UNITY" => File.join("unity","src","unity.h"), + "CMOCK" => File.join("cmock","src","cmock.h"), + "CEXCEPTION" => File.join("c_exception","lib","CException.h") + }.each_pair do |name, path| + # Check for local or global version of vendor directory in order to look up versions + path1 = File.expand_path( File.join("..","..","vendor",path) ) + path2 = File.expand_path( File.join(File.dirname(__FILE__),"..","..","vendor",path) ) + filename = if (File.exists?(path1)) + path1 + elsif (File.exists?(path2)) + path2 + elsif File.exists?(CEEDLING_VENDOR) + path3 = File.expand_path( File.join(CEEDLING_VENDOR,path) ) + if (File.exists?(path3)) + path3 + else + basepath = File.join( CEEDLING_VENDOR, path.split(/\\\//)[0], 'release') + begin + [ @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip, + @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip ].join('.') + rescue + "#{name}" + end + end + else + module_eval("#{name} = 'unknown'") + continue + end + + # Actually look up the versions + a = [0,0,0] + begin + File.readlines(filename).each do |line| + ["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i| + m = line.match(/#{name}_#{field}\s+(\d+)/) + a[i] = m[1] unless (m.nil?) + end + end + rescue + abort("Can't collect data for vendor component: \"#{filename}\" . \nPlease check your setup.") + end + + # splat it to return the final value + eval("#{name} = '#{a.join(".")}'") + end + + GEM = "0.31.1" + CEEDLING = GEM + end +end diff --git a/CAN_App/vendor/ceedling/lib/ceedling/yaml_wrapper.rb b/CAN_App/vendor/ceedling/lib/ceedling/yaml_wrapper.rb new file mode 100644 index 0000000..00ece51 --- /dev/null +++ b/CAN_App/vendor/ceedling/lib/ceedling/yaml_wrapper.rb @@ -0,0 +1,17 @@ +require 'yaml' +require 'erb' + + +class YamlWrapper + + def load(filepath) + return YAML.load(ERB.new(File.read(filepath)).result) + end + + def dump(filepath, structure) + File.open(filepath, 'w') do |output| + YAML.dump(structure, output) + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/beep/README.md b/CAN_App/vendor/ceedling/plugins/beep/README.md new file mode 100644 index 0000000..e59d881 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/beep/README.md @@ -0,0 +1,22 @@ +ceedling-beep +============= + +This is a simple plugin that just beeps at the end of a build and/or test sequence. Are you getting too distracted surfing +the internet, chatting with coworkers, or swordfighting while it's building or testing? The friendly beep will let you know +it's time to pay attention again. + +This plugin has very few configuration options. At this time it can beep on completion of a task and/or on an error condition. +For each of these, you can configure the method that it should beep. + +``` +:tools: + :beep_on_done: :bell + :beep_on_error: :bell +``` + +Each of these have the following options: + + - :bell - this option uses the ASCII bell character out stdout + - :speaker_test - this uses the linux speaker-test command if installed + +Very likely, we'll be adding to this list if people find this to be useful. diff --git a/CAN_App/vendor/ceedling/plugins/beep/lib/beep.rb b/CAN_App/vendor/ceedling/plugins/beep/lib/beep.rb new file mode 100644 index 0000000..6a6d01a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/beep/lib/beep.rb @@ -0,0 +1,40 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +class Beep < Plugin + + attr_reader :config + + def setup + @config = { + :on_done => ((defined? TOOLS_BEEP_ON_DONE) ? TOOLS_BEEP_ON_DONE : :bell ), + :on_error => ((defined? TOOLS_BEEP_ON_ERROR) ? TOOLS_BEEP_ON_ERROR : :bell ), + } + end + + def post_build + beep @config[:on_done] + end + + def post_error + beep @config[:on_error] + end + + private + + def beep(method = :none) + case method + when :bell + if (SystemWrapper.windows?) + puts "echo '\007'" + else + puts "echo -ne '\007'" + end + when :speaker_test + `speaker-test -t sine -f 1000 -l 1` + else + #do nothing with illegal or :none + end + end +end + diff --git a/CAN_App/vendor/ceedling/plugins/bullseye/README.md b/CAN_App/vendor/ceedling/plugins/bullseye/README.md new file mode 100644 index 0000000..ab0b53b --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/bullseye/README.md @@ -0,0 +1,76 @@ +ceedling-bullseye +================= + +# Plugin Overview + +Plugin for integrating Bullseye code coverage tool into Ceedling projects. +This plugin requires a working license to Bullseye code coverage tools. The tools +must be within the path or the path should be added to the environment in the +`project.yml file`. + +## Configuration + +The bullseye plugin supports configuration options via your `project.yml` provided +by Ceedling. The following is a typical configuration example: + +``` +:bullseye: + :auto_license: TRUE +:plugins: + :bullseye_lib_path: [] +:paths: + :bullseye_toolchain_include: [] + +:tools: + :bullseye_instrumentation: + :executable: covc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - ${1} + :bullseye_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_BULLSEYE_TOOLCHAIN_INCLUDE + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR + - -DBULLSEYE_COMPILER + - -c "${1}" + - -o "${2}" + :bullseye_linker: + :executable: gcc + :arguments: + - ${1} + - -o ${2} + - -L$: PLUGINS_BULLSEYE_LIB_PATH + - -lcov + :bullseye_fixture: + :executable: ${1} + :bullseye_report_covsrc: + :executable: covsrc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - -w140 + :bullseye_report_covfn: + :executable: covfn + :stderr_redirect: :auto + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - --width 120 + - --no-source + - '"${1}"' + :bullseye_browser: + :executable: CoverageBrowser + :background_exec: :auto + :optional: TRUE + :arguments: + - '"$"': ENVIRONMENT_COVFILE +``` + +## Example Usage + +```sh +ceedling bullseye:all utils:bullseye +``` diff --git a/CAN_App/vendor/ceedling/plugins/bullseye/assets/template.erb b/CAN_App/vendor/ceedling/plugins/bullseye/assets/template.erb new file mode 100644 index 0000000..504f855 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/bullseye/assets/template.erb @@ -0,0 +1,15 @@ +% function_string = hash[:coverage][:functions].to_s +% branch_string = hash[:coverage][:branches].to_s +% format_string = "%#{[function_string.length, branch_string.length].max}i" +<%=@ceedling[:plugin_reportinator].generate_banner("#{hash[:header]}: CODE COVERAGE SUMMARY")%> +% if (!hash[:coverage][:functions].nil?) +FUNCTIONS: <%=sprintf(format_string, hash[:coverage][:functions])%>% +% else +FUNCTIONS: none +% end +% if (!hash[:coverage][:branches].nil?) +BRANCHES: <%=sprintf(format_string, hash[:coverage][:branches])%>% +% else +BRANCHES: none +% end + diff --git a/CAN_App/vendor/ceedling/plugins/bullseye/bullseye.rake b/CAN_App/vendor/ceedling/plugins/bullseye/bullseye.rake new file mode 100644 index 0000000..11073e7 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/bullseye/bullseye.rake @@ -0,0 +1,173 @@ +directory(BULLSEYE_BUILD_OUTPUT_PATH) +directory(BULLSEYE_RESULTS_PATH) +directory(BULLSEYE_ARTIFACTS_PATH) +directory(BULLSEYE_DEPENDENCIES_PATH) + +CLEAN.include(File.join(BULLSEYE_BUILD_OUTPUT_PATH, '*')) +CLEAN.include(File.join(BULLSEYE_RESULTS_PATH, '*')) +CLEAN.include(File.join(BULLSEYE_DEPENDENCIES_PATH, '*')) + +CLOBBER.include(File.join(BULLSEYE_BUILD_PATH, '**/*')) +PLUGINS_BULLSEYE_LIB_PATH = 'C:\\tools\\BullseyeCoverage\\lib' if not defined?(PLUGINS_BULLSEYE_LIB_PATH) + +rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |object| + + if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX}|#{BULLSEYE_IGNORE_SOURCES.join('|')})/i + @ceedling[:generator].generate_object_file( + TOOLS_BULLSEYE_COMPILER, + OPERATION_COMPILE_SYM, + BULLSEYE_SYM, + object.source, + object.name, + @ceedling[:file_path_utils].form_test_build_list_filepath(object.name) + ) + else + @ceedling[BULLSEYE_SYM].generate_coverage_object_file(object.source, object.name) + end + +end + +rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| + lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() + @ceedling[:generator].generate_executable_file( + TOOLS_BULLSEYE_LINKER, + BULLSEYE_SYM, + bin_file.prerequisites, + bin_file.name, + @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name), + lib_args, + lib_paths + ) +end + +rule(/#{BULLSEYE_RESULTS_PATH}\/#{'.+\\'+EXTENSION_TESTPASS}$/ => [ + proc do |task_name| + @ceedling[:file_path_utils].form_test_executable_filepath(task_name) + end + ]) do |test_result| + @ceedling[:generator].generate_test_results(TOOLS_BULLSEYE_FIXTURE, BULLSEYE_SYM, test_result.source, test_result.name) +end + +rule(/#{BULLSEYE_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |dep| + @ceedling[:generator].generate_dependencies_file( + TOOLS_TEST_DEPENDENCIES_GENERATOR, + BULLSEYE_SYM, + dep.source, + File.join(BULLSEYE_BUILD_OUTPUT_PATH, File.basename(dep.source).ext(EXTENSION_OBJECT) ), + dep.name + ) +end + +task :directories => [BULLSEYE_BUILD_OUTPUT_PATH, BULLSEYE_RESULTS_PATH, BULLSEYE_DEPENDENCIES_PATH, BULLSEYE_ARTIFACTS_PATH] + +namespace BULLSEYE_SYM do + task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{BULLSEYE_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") + + desc 'Run code coverage for all tests' + task all: [:test_deps] do + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM) + @ceedling[:configurator].restore_config + end + + desc "Run single test w/ coverage ([*] real test or source file name, no path)." + task :* do + message = "\nOops! '#{BULLSEYE_ROOT_NAME}:*' isn't a real task. " + + "Use a real test or source file name (no path) in place of the wildcard.\n" + + "Example: rake #{BULLSEYE_ROOT_NAME}:foo.c\n\n" + + @ceedling[:streaminator].stdout_puts( message ) + end + + desc 'Run tests by matching regular expression pattern.' + task :pattern, [:regex] => [:test_deps] do |_t, args| + matches = [] + + COLLECTION_ALL_TESTS.each do |test| + matches << test if test =~ /#{args.regex}/ + end + + if !matches.empty? + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].setup_and_invoke(matches, BULLSEYE_SYM, force_run: false) + @ceedling[:configurator].restore_config + else + @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.") + end + end + + desc 'Run tests whose test path contains [dir] or [dir] substring.' + task :path, [:dir] => [:test_deps] do |_t, args| + matches = [] + + COLLECTION_ALL_TESTS.each do |test| + matches << test if File.dirname(test).include?(args.dir.tr('\\', '/')) + end + + if !matches.empty? + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].setup_and_invoke(matches, BULLSEYE_SYM, force_run: false) + @ceedling[:configurator].restore_config + else + @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.") + end + end + + desc 'Run code coverage for changed files' + task delta: [:test_deps] do + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM, {:force_run => false}) + @ceedling[:configurator].restore_config + end + + # use a rule to increase efficiency for large projects + # bullseye test tasks by regex + rule(/^#{BULLSEYE_TASK_ROOT}\S+$/ => [ + proc do |task_name| + test = task_name.sub(/#{BULLSEYE_TASK_ROOT}/, '') + test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" unless test.start_with?(PROJECT_TEST_FILE_PREFIX) + @ceedling[:file_finder].find_test_from_file_path(test) + end + ]) do |test| + @ceedling[:rake_wrapper][:test_deps].invoke + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].setup_and_invoke([test.source], BULLSEYE_SYM) + @ceedling[:configurator].restore_config + end + +end + +if PROJECT_USE_DEEP_DEPENDENCIES +namespace REFRESH_SYM do + task BULLSEYE_SYM do + @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) + @ceedling[BULLSEYE_SYM].enableBullseye(true) + @ceedling[:test_invoker].refresh_deep_dependencies + @ceedling[:configurator].restore_config + end +end +end + +namespace UTILS_SYM do + + desc "Open Bullseye code coverage browser" + task BULLSEYE_SYM do + command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BROWSER, []) + @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/bullseye/config/defaults.yml b/CAN_App/vendor/ceedling/plugins/bullseye/config/defaults.yml new file mode 100644 index 0000000..ed261d8 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/bullseye/config/defaults.yml @@ -0,0 +1,57 @@ +--- + +:bullseye: + :auto_license: TRUE +:plugins: + :bullseye_lib_path: [] +:paths: + :bullseye_toolchain_include: [] + +:tools: + :bullseye_instrumentation: + :executable: covc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - ${1} + :bullseye_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_BULLSEYE_TOOLCHAIN_INCLUDE + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR + - -DBULLSEYE_COMPILER + - -c "${1}" + - -o "${2}" + :bullseye_linker: + :executable: gcc + :arguments: + - ${1} + - -o ${2} + - -L$: PLUGINS_BULLSEYE_LIB_PATH + - -lcov + :bullseye_fixture: + :executable: ${1} + :bullseye_report_covsrc: + :executable: covsrc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - -w140 + :bullseye_report_covfn: + :executable: covfn + :stderr_redirect: :auto + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - --width 120 + - --no-source + - '"${1}"' + :bullseye_browser: + :executable: CoverageBrowser + :background_exec: :auto + :optional: TRUE + :arguments: + - '"$"': ENVIRONMENT_COVFILE + +... diff --git a/CAN_App/vendor/ceedling/plugins/bullseye/lib/bullseye.rb b/CAN_App/vendor/ceedling/plugins/bullseye/lib/bullseye.rb new file mode 100644 index 0000000..ffa444a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/bullseye/lib/bullseye.rb @@ -0,0 +1,194 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +BULLSEYE_ROOT_NAME = 'bullseye' +BULLSEYE_TASK_ROOT = BULLSEYE_ROOT_NAME + ':' +BULLSEYE_SYM = BULLSEYE_ROOT_NAME.to_sym + +BULLSEYE_BUILD_PATH = "#{PROJECT_BUILD_ROOT}/#{BULLSEYE_ROOT_NAME}" +BULLSEYE_BUILD_OUTPUT_PATH = "#{BULLSEYE_BUILD_PATH}/out" +BULLSEYE_RESULTS_PATH = "#{BULLSEYE_BUILD_PATH}/results" +BULLSEYE_DEPENDENCIES_PATH = "#{BULLSEYE_BUILD_PATH}/dependencies" +BULLSEYE_ARTIFACTS_PATH = "#{PROJECT_BUILD_ARTIFACTS_ROOT}/#{BULLSEYE_ROOT_NAME}" + +BULLSEYE_IGNORE_SOURCES = ['unity', 'cmock', 'cexception'] + + +class Bullseye < Plugin + + def setup + @result_list = [] + @environment = [ {:covfile => File.join( BULLSEYE_ARTIFACTS_PATH, 'test.cov' )} ] + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + @coverage_template_all = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb')) + end + + def config + { + :project_test_build_output_path => BULLSEYE_BUILD_OUTPUT_PATH, + :project_test_results_path => BULLSEYE_RESULTS_PATH, + :project_test_dependencies_path => BULLSEYE_DEPENDENCIES_PATH, + :defines_test => DEFINES_TEST + ['CODE_COVERAGE'], + :collection_defines_test_and_vendor => COLLECTION_DEFINES_TEST_AND_VENDOR + ['CODE_COVERAGE'] + } + end + + def generate_coverage_object_file(source, object) + arg_hash = {:tool => TOOLS_BULLSEYE_INSTRUMENTATION, :context => BULLSEYE_SYM, :source => source, :object => object} + @ceedling[:plugin_manager].pre_compile_execute(arg_hash) + + @ceedling[:streaminator].stdout_puts("Compiling #{File.basename(source)} with coverage...") + compile_command = + @ceedling[:tool_executor].build_command_line( + TOOLS_BULLSEYE_COMPILER, + @ceedling[:flaginator].flag_down( OPERATION_COMPILE_SYM, BULLSEYE_SYM, source ), + source, + object, + @ceedling[:file_path_utils].form_test_build_list_filepath( object ) ) + coverage_command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_INSTRUMENTATION, [], compile_command[:line] ) + + shell_result = @ceedling[:tool_executor].exec( coverage_command[:line], coverage_command[:options] ) + + arg_hash[:shell_result] = shell_result + @ceedling[:plugin_manager].post_compile_execute(arg_hash) + end + + def post_test_fixture_execute(arg_hash) + result_file = arg_hash[:result_file] + + if ((result_file =~ /#{BULLSEYE_RESULTS_PATH}/) and (not @result_list.include?(result_file))) + @result_list << arg_hash[:result_file] + end + end + + def post_build + return if (not @ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}/)) + + # test results + results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list) + hash = { + :header => BULLSEYE_ROOT_NAME.upcase, + :results => results + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) do + message = '' + message = 'Unit test failures.' if (results[:counts][:failed] > 0) + message + end + + # coverage results + return if (verify_coverage_file() == false) + if (@ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}(all|delta)/)) + command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVSRC, []) + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + report_coverage_results_all(shell_result[:output]) + else + report_per_function_coverage_results(@ceedling[:test_invoker].sources) + end + end + + def summary + return if (verify_coverage_file() == false) + result_list = @ceedling[:file_path_utils].form_pass_results_filelist( BULLSEYE_RESULTS_PATH, COLLECTION_ALL_TESTS ) + + # test results + # get test results for only those tests in our configuration and of those only tests with results on disk + hash = { + :header => BULLSEYE_ROOT_NAME.upcase, + :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false}) + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + + # coverage results + command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVSRC) + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + report_coverage_results_all(shell_result[:output]) + end + + def enableBullseye(enable) + if BULLSEYE_AUTO_LICENSE + if (enable) + args = ['push', 'on'] + @ceedling[:streaminator].stdout_puts("Enabling Bullseye") + else + args = ['pop'] + @ceedling[:streaminator].stdout_puts("Reverting Bullseye to previous state") + end + + args.each do |arg| + command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BUILD_ENABLE_DISABLE, [], arg) + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + + end + end + + private ################################### + + def report_coverage_results_all(coverage) + results = { + :header => BULLSEYE_ROOT_NAME.upcase, + :coverage => { + :functions => nil, + :branches => nil + } + } + + if (coverage =~ /^Total.*?=\s+([0-9]+)\%/) + results[:coverage][:functions] = $1.to_i + end + + if (coverage =~ /^Total.*=\s+([0-9]+)\%\s*$/) + results[:coverage][:branches] = $1.to_i + end + + @ceedling[:plugin_reportinator].run_report($stdout, @coverage_template_all, results) + end + + def report_per_function_coverage_results(sources) + banner = @ceedling[:plugin_reportinator].generate_banner( "#{BULLSEYE_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" ) + @ceedling[:streaminator].stdout_puts "\n" + banner + + coverage_sources = sources.clone + coverage_sources.delete_if {|item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{EXTENSION_SOURCE}$/} + coverage_sources.delete_if {|item| item =~ /#{BULLSEYE_IGNORE_SOURCES.join('|')}#{EXTENSION_SOURCE}$/} + + coverage_sources.each do |source| + command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVFN, [], source) + shell_results = @ceedling[:tool_executor].exec(command[:line], command[:options]) + coverage_results = shell_results[:output].deep_clone + coverage_results.sub!(/.*\n.*\n/,'') # Remove the Bullseye tool banner + if (coverage_results =~ /warning cov814: report is empty/) + coverage_results = "WARNING: #{source} contains no coverage data!\n\n" + @ceedling[:streaminator].stdout_puts(coverage_results, Verbosity::COMPLAIN) + else + coverage_results += "\n" + @ceedling[:streaminator].stdout_puts(coverage_results) + end + end + end + + def verify_coverage_file + exist = @ceedling[:file_wrapper].exist?( ENVIRONMENT_COVFILE ) + + if (!exist) + banner = @ceedling[:plugin_reportinator].generate_banner( "#{BULLSEYE_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" ) + @ceedling[:streaminator].stdout_puts "\n" + banner + "\nNo coverage file.\n\n" + end + + return exist + end + +end + + +# end blocks always executed following rake run +END { + # cache our input configurations to use in comparison upon next execution + if (@ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}/)) + @ceedling[:cacheinator].cache_test_config( @ceedling[:setupinator].config_hash ) + @ceedling[BULLSEYE_SYM].enableBullseye(false) + end +} diff --git a/CAN_App/vendor/ceedling/plugins/colour_report/README.md b/CAN_App/vendor/ceedling/plugins/colour_report/README.md new file mode 100644 index 0000000..4e0fcd4 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/colour_report/README.md @@ -0,0 +1,20 @@ +ceedling-colour-report +====================== + +## Overview + +The colour_report replaces the normal ceedling "pretty" output with +a colorized variant, in order to make the results easier to read from +a standard command line. This is very useful on developer machines, but +can occasionally cause problems with parsing on CI servers. + +## Setup + +Enable the plugin in your project.yml by adding `colour_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - colour_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/colour_report/lib/colour_report.rb b/CAN_App/vendor/ceedling/plugins/colour_report/lib/colour_report.rb new file mode 100644 index 0000000..1211eab --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/colour_report/lib/colour_report.rb @@ -0,0 +1,16 @@ +require 'ceedling/plugin' +require 'ceedling/streaminator' +require 'ceedling/constants' + +class ColourReport < Plugin + + def setup + @ceedling[:stream_wrapper].stdout_override(&ColourReport.method(:colour_stdout)) + end + + def self.colour_stdout(string) + require 'colour_reporter.rb' + report string + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/command_hooks/README.md b/CAN_App/vendor/ceedling/plugins/command_hooks/README.md new file mode 100644 index 0000000..8ac64af --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/command_hooks/README.md @@ -0,0 +1,53 @@ +ceedling-command-hooks +====================== + +Plugin for easily calling command line tools at various points in the build process + +Define any of these sections in :tools: to provide additional hooks to be called on demand: + +``` + :pre_mock_generate + :post_mock_generate + :pre_runner_generate + :post_runner_generate + :pre_compile_execute + :post_compile_execute + :pre_link_execute + :post_link_execute + :pre_test_fixture_execute + :pre_test + :post_test + :pre_release + :post_release + :pre_build + :post_build +``` + +Each of these tools can support an :executable string and an :arguments list, like so: + +``` +:tools: + :post_link_execute: + :executable: objcopy.exe + :arguments: + - ${1} #This is replaced with the executable name + - output.srec + - --strip-all +``` + +You may also specify an array of executables to be called in a particular place, like so: + +``` +:tools: + :post_test: + - :executable: echo + :arguments: "${1} was glorious!" + - :executable: echo + :arguments: + - it kinda made me cry a little. + - you? +``` + +Please note that it varies which arguments are being parsed down to the +hooks. For now see `command_hooks.rb` to figure out which suits you best. +Happy Tweaking! diff --git a/CAN_App/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb b/CAN_App/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb new file mode 100644 index 0000000..4bf8b53 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb @@ -0,0 +1,92 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +class CommandHooks < Plugin + + attr_reader :config + + def setup + @config = { + :pre_mock_generate => ((defined? TOOLS_PRE_MOCK_GENERATE) ? TOOLS_PRE_MOCK_GENERATE : nil ), + :post_mock_generate => ((defined? TOOLS_POST_MOCK_GENERATE) ? TOOLS_POST_MOCK_GENERATE : nil ), + :pre_runner_generate => ((defined? TOOLS_PRE_RUNNER_GENERATE) ? TOOLS_PRE_RUNNER_GENERATE : nil ), + :post_runner_generate => ((defined? TOOLS_POST_RUNNER_GENERATE) ? TOOLS_POST_RUNNER_GENERATE : nil ), + :pre_compile_execute => ((defined? TOOLS_PRE_COMPILE_EXECUTE) ? TOOLS_PRE_COMPILE_EXECUTE : nil ), + :post_compile_execute => ((defined? TOOLS_POST_COMPILE_EXECUTE) ? TOOLS_POST_COMPILE_EXECUTE : nil ), + :pre_link_execute => ((defined? TOOLS_PRE_LINK_EXECUTE) ? TOOLS_PRE_LINK_EXECUTE : nil ), + :post_link_execute => ((defined? TOOLS_POST_LINK_EXECUTE) ? TOOLS_POST_LINK_EXECUTE : nil ), + :pre_test_fixture_execute => ((defined? TOOLS_PRE_TEST_FIXTURE_EXECUTE) ? TOOLS_PRE_TEST_FIXTURE_EXECUTE : nil ), + :post_test_fixture_execute => ((defined? TOOLS_POST_TEST_FIXTURE_EXECUTE) ? TOOLS_POST_TEST_FIXTURE_EXECUTE : nil ), + :pre_test => ((defined? TOOLS_PRE_TEST) ? TOOLS_PRE_TEST : nil ), + :post_test => ((defined? TOOLS_POST_TEST) ? TOOLS_POST_TEST : nil ), + :pre_release => ((defined? TOOLS_PRE_RELEASE) ? TOOLS_PRE_RELEASE : nil ), + :post_release => ((defined? TOOLS_POST_RELEASE) ? TOOLS_POST_RELEASE : nil ), + :pre_build => ((defined? TOOLS_PRE_BUILD) ? TOOLS_PRE_BUILD : nil ), + :post_build => ((defined? TOOLS_POST_BUILD) ? TOOLS_POST_BUILD : nil ), + :post_error => ((defined? TOOLS_POST_ERROR) ? TOOLS_POST_ERROR : nil ), + } + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + end + + def pre_mock_generate(arg_hash); run_hook(:pre_mock_generate, arg_hash[:header_file] ); end + def post_mock_generate(arg_hash); run_hook(:post_mock_generate, arg_hash[:header_file] ); end + def pre_runner_generate(arg_hash); run_hook(:pre_runner_generate, arg_hash[:source ] ); end + def post_runner_generate(arg_hash); run_hook(:post_runner_generate, arg_hash[:runner_file] ); end + def pre_compile_execute(arg_hash); run_hook(:pre_compile_execute, arg_hash[:source_file] ); end + def post_compile_execute(arg_hash); run_hook(:post_compile_execute, arg_hash[:object_file] ); end + def pre_link_execute(arg_hash); run_hook(:pre_link_execute, arg_hash[:executable] ); end + def post_link_execute(arg_hash); run_hook(:post_link_execute, arg_hash[:executable] ); end + def pre_test_fixture_execute(arg_hash); run_hook(:pre_test_fixture_execute, arg_hash[:executable] ); end + def post_test_fixture_execute(arg_hash); run_hook(:post_test_fixture_execute, arg_hash[:executable] ); end + def pre_test(test); run_hook(:pre_test, test ); end + def post_test(test); run_hook(:post_test, test ); end + def pre_release; run_hook(:pre_release ); end + def post_release; run_hook(:post_release ); end + def pre_build; run_hook(:pre_build ); end + def post_build; run_hook(:post_build ); end + def post_error; run_hook(:post_error ); end + + private + + ## + # Run a hook if its available. + # + # :args: + # - hook: Name of the hook to run + # - name: Name of file (default: "") + # + # :return: + # shell_result. + # + def run_hook_step(hook, name="") + if (hook[:executable]) + # Handle argument replacemant ({$1}), and get commandline + cmd = @ceedling[:tool_executor].build_command_line( hook, [], name ) + shell_result = @ceedling[:tool_executor].exec(cmd[:line], cmd[:options]) + end + end + + ## + # Run a hook if its available. + # + # If __which_hook__ is an array, run each of them sequentially. + # + # :args: + # - which_hook: Name of the hook to run + # - name: Name of file + # + def run_hook(which_hook, name="") + if (@config[which_hook]) + @ceedling[:streaminator].stdout_puts("Running Hook #{which_hook}...", Verbosity::NORMAL) + if (@config[which_hook].is_a? Array) + @config[which_hook].each do |hook| + run_hook_step(hook, name) + end + elsif (@config[which_hook].is_a? Hash) + run_hook_step( @config[which_hook], name ) + else + @ceedling[:streaminator].stdout_puts("Hook #{which_hook} was poorly formed", Verbosity::COMPLAINT) + end + end + end +end + diff --git a/CAN_App/vendor/ceedling/plugins/compile_commands_json/README.md b/CAN_App/vendor/ceedling/plugins/compile_commands_json/README.md new file mode 100644 index 0000000..ea80b73 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/compile_commands_json/README.md @@ -0,0 +1,29 @@ +compile_commands_json +===================== + +## Overview + +Syntax highlighting and code completion are hard. Historically each editor or IDE has implemented their own and then competed amongst themselves to offer the best experience for developers. Often developers would still to an IDE that felt cumbersome and slow just because it had the best syntax highlighting on the market. If doing it for one language is hard (and it is) imagine doing it for dozens of them. Imagine a full stack developer who has to work with CSS, HTML, JavaScript and some Ruby - they need excellent support in all those languages which just made things even harder. + +In June of 2016, Microsoft with Red Hat and Codenvy got together to create a standard called the Language Server Protocol (LSP). The idea was simple, by standardising on one protocol, all the IDEs and editors out there would only have to support LSP, and not have custom plugins for each language. In turn, the backend code that actually does the highlighting can be written once and used by any IDE that supports LSP. Many editors already support it such as Sublime Text, vim and emacs. This means that if you're using a crufty old IDE or worse, you're using a shiny new editor without code completion, then this could be just the upgrade you're looking for! + +For C and C++ projects, many people use the `clangd` backend. So that it can do things like "go to definition", `clangd` needs to know how to build the project so that it can figure out all the pieces to the puzzle. There are manual tools such as `bear` which can be run with `gcc` or `clang` to extract this information it has a big limitation in that if run with `ceedling release` you won't get any auto completion for Unity and you'll also get error messages reported by your IDE because of what it perceives as missing headers. If you do the same with `ceedling test` now you get Unity but you might miss things that are only seen in the release build. + +This plugin resolves that issue. As it is run by Ceedling, it has access to all the build information it needs to create the perfect `compile_commands.json`. Once enabled, this plugin will generate that file and place it in `./build/artifacts/compile_commands.json`. `clangd` will search your project for this file, but it is easier to symlink it into the root directory (for example `ln -s ./build/artifacts/compile_commands.json`. + +For more information on LSP and to find out if your editor supports it, check out https://langserver.org/ + +## Setup + +Enable the plugin in your project.yml by adding `compile_commands_json` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - compile_commands_json +``` + +## Configuration + +There is no additional configuration necessary to run this plugin. diff --git a/CAN_App/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb b/CAN_App/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb new file mode 100644 index 0000000..269cea4 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb @@ -0,0 +1,35 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'json' + +class CompileCommandsJson < Plugin + def setup + @fullpath = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, "compile_commands.json") + @database = if (File.exists?(@fullpath)) + JSON.parse( File.read(@fullpath) ) + else + [] + end + end + + def post_compile_execute(arg_hash) + + # Create the new Entry + value = { + "directory" => Dir.pwd, + "command" => arg_hash[:shell_command], + "file" => arg_hash[:source] + } + + # Determine if we're updating an existing file description or adding a new one + index = @database.index {|h| h["file"] == arg_hash[:source]} + if index + @database[index] = value + else + @database << value + end + + # Update the Actual compile_commands.json file + File.open(@fullpath,'w') {|f| f << JSON.pretty_generate(@database)} + end +end diff --git a/CAN_App/vendor/ceedling/plugins/dependencies/README.md b/CAN_App/vendor/ceedling/plugins/dependencies/README.md new file mode 100644 index 0000000..256467d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/dependencies/README.md @@ -0,0 +1,254 @@ +ceedling-dependencies +===================== + +Plugin for supporting release dependencies. It's rare for an embedded project to +be built completely free of other libraries and modules. Some of these may be +standard internal libraries. Some of these may be 3rd party libraries. In either +case, they become part of the project's ecosystem. + +This plugin is intended to make that relationship easier. It allows you to specify +a source for dependencies. If required, it will automatically grab the appropriate +version of that dependency. + +Most 3rd party libraries have a method of building already in place. While we'd +love to convert the world to a place where everything downloads with a test suite +in Ceedling, that's not likely to happen anytime soon. Until then, this plugin +will allow the developer to specify what calls Ceedling should make to oversee +the build process of those third party utilities. Are they using Make? CMake? A +custom series of scripts that only a mad scientist could possibly understand? No +matter. Ceedling has you covered. Just specify what should be called, and Ceedling +will make it happen whenever it notices that the output artifacts are missing. + +Output artifacts? Sure! Things like static and dynamic libraries, or folders +containing header files that might want to be included by your release project. + +So how does all this magic work? + +First, you need to add the `:dependencies` plugin to your list. Then, we'll add a new +section called :dependencies. There, you can list as many dependencies as you desire. Each +has a series of fields which help Ceedling to understand your needs. Many of them are +optional. If you don't need that feature, just don't include it! In the end, it'll look +something like this: + +``` +:dependencies: + :libraries: + - :name: WolfSSL + :source_path: third_party/wolfssl/source + :build_path: third_party/wolfssl/build + :artifact_path: third_party/wolfssl/install + :fetch: + :method: :zip + :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip + :environment: + - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE + :build: + - "autoreconf -i" + - "./configure --enable-tls13 --enable-singlethreaded" + - make + - make install + :artifacts: + :static_libraries: + - lib/wolfssl.a + :dynamic_libraries: + - lib/wolfssl.so + :includes: + - include/** +``` + +Let's take a deeper look at each of these features. + +The Starting Dash & Name +------------------------ + +Yes, that opening dash tells the dependencies plugin that the rest of these fields +belong to our first dependency. If we had a second dependency, we'd have another +dash, lined up with the first, and followed by all the fields indented again. + +By convention, we use the `:name` field as the first field for each tool. Ceedling +honestly doesn't care which order the fields are given... but as humans, it makes +it easier for us to see the name of each dependency with starting dash. + +The name field is only used to print progress while we're running Ceedling. You may +call the name of the field whatever you wish. + +Working Folders +--------------- + +The `:source_path` field allows us to specify where the source code for each of our +dependencies is stored. If fetching the dependency from elsewhere, it will be fetched +to this location. All commands to build this dependency will be executed from +this location (override this by specifying a `:build_path`). Finally, the output +artifacts will be referenced to this location (override this by specifying a `:artifact_path`) + +If unspecified, the `:source_path` will be `dependencies\dep_name` where `dep_name` +is the name specified in `:name` above (with special characters removed). It's best, +though, if you specify exactly where you want your dependencies to live. + +If the dependency is directly included in your project (you've specified `:none` as the +`:method` for fetching), then `:source_path` should be where your Ceedling can find the +source for your dependency in you repo. + +All artifacts are relative to the `:artifact_path` (which defaults to be the same as +`:source_path`) + +Fetching Dependencies +--------------------- + +The `:dependencies` plugin supports the ability to automatically fetch your dependencies +for you... using some common methods of fetching source. This section contains only a +couple of fields: + +- `:method` -- This is the method that this dependency is fetched. + - `:none` -- This tells Ceedling that the code is already included in the project. + - `:zip` -- This tells Ceedling that we want to unpack a zip file to our source path. + - `:git` -- This tells Ceedling that we want to clone a git repo to our source path. + - `:svn` -- This tells Ceedling that we want to checkout a subversion repo to our source path. + - `:custom` -- This tells Ceedling that we want to use a custom command or commands to fetch the code. +- `:source` -- This is the path or url to fetch code when using the zip or git method. +- `:tag`/`:branch` -- This is the specific tag or branch that you wish to retrieve (git only. optional). +- `:hash` -- This is the specific SHA1 hash you want to fetch (git only. optional, requires a deep clone). +- `:revision` -- This is the specific revision you want to fetch (svn only. optional). +- `:executable` -- This is a list of commands to execute when using the `:custom` method + + +Environment Variables +--------------------- + +Many build systems support customization through environment variables. By specifying +an array of environment variables, Ceedling will customize the shell environment before +calling the build process. + +Environment variables may be specified in three ways. Let's look at one of each: + +``` + :environment: + - ARCHITECTURE=ARM9 + - CFLAGS+=-DADD_AWESOMENESS + - CFLAGS-=-DWASTE +``` + +In the first example, you see the most straightforward method. The environment variable +`ARCHITECTURE` is set to the value `ARM9`. That's it. Simple. + +The next two options modify an existing symbol. In the first one, we use `+=`, which tells +Ceedling to add the define `ADD_AWESOMENESS` to the environment variable `CFLAGS`. The second +tells Ceedling to remove the define `WASTE` from the same environment variable. + +There are a couple of things to note here. + +First, when adding to a variable, Ceedling has no way of knowing +what delimiter you are expecting. In this example you can see we manually added some whitespace. +If we had been modifying `PATH` instead, we might have had to use a `:` on a unux or `;` on +Windows. + +Second, removing an argument will have no effect on the argument if that argument isn't found +precisely. It's case sensitive and the entire string must match. If symbol doesn't already exist, +it WILL after executing this command... however it will be assigned to nothing. + +Building Dependencies +--------------------- + +The heart of the `:dependencies` plugin is the ability for you, the developer, to specify the +build process for each of your dependencies. You will need to have any required tools installed +before using this feature. + +The steps are specified as an array of strings. Ceedling will execute those steps in the order +specified, moving from step to step unless an error is encountered. By the end of the process, +the artifacts should have been created by your process... otherwise an error will be produced. + +Artifacts +--------- + +These are the outputs of the build process. There are there types of artifacts. Any dependency +may have none or some of these. Calling out these files tells Ceedling that they are important. +Your dependency's build process may produce many other files... but these are the files that +Ceedling understands it needs to act on. + +### `static_libraries` + +Specifying one or more static libraries will tell Ceedling where it should find static libraries +output by your build process. These libraries are automatically added to the list of dependencies +and will be linked with the rest of your code to produce the final release. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `dynamic_libraries` + +Specifying one or more dynamic libraries will tell Ceedling where it should find dynamic libraries +output by your build process. These libraries are automatically copied to the same folder as your +final release binary. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `includes` + +Often when libraries are built, the same process will output a collection of includes so that +your release code knows how to interact with that library. It's the public API for that library. +By specifying the directories that will contain these includes (don't specify the files themselves, +Ceedling only needs the directories), Ceedling is able to automatically add these to its internal +include list. This allows these files to be used while building your release code, as well we making +them mockable during unit testing. + +### `source` + +It's possible that your external dependency will just produce additional C files as its output. +In this case, Ceedling is able to automatically add these to its internal source list. This allows +these files to be used while building your release code. + +Tasks +----- + +Once configured correctly, the `:dependencies` plugin should integrate seamlessly into your +workflow and you shouldn't have to think about it. In the real world, that doesn't always happen. +Here are a number of tasks that are added or modified by this plugin. + +### `ceedling dependencies:clean` + +This can be issued in order to completely remove the dependency from its source path. On the +next build, it will be refetched and rebuilt from scratch. This can also apply to a particular +dependency. For example, by specifying `dependencies:clean:DepName`. + +### `ceedling dependencies:fetch` + +This can be issued in order to fetch each dependency from its origin. This will have no effect on +dependencies that don't have fetch instructions specified. This can also apply to a particular +dependency. For example, by specifying `dependencies:fetch:DepName`. + +### `ceedling dependencies:make` + +This will force the dependencies to all build. This should happen automatically when a release +has been triggered... but if you're just getting your dependency configured at this moment, you +may want to just use this feature instead. A single dependency can also be built by specifying its +name, like `dependencies:make:MyTunaBoat`. + +### `ceedling dependencies:deploy` + +This will force any dynamic libraries produced by your dependencies to be copied to your release +build directory... just in case you clobbered them. + +### `paths:include` + +Maybe you want to verify that all the include paths are correct. If you query Ceedling with this +request, it will list all the header file paths that it's found, including those produced by +dependencies. + +### `files:include` + +Maybe you want to take that query further and actually get a list of ALL the header files +Ceedling has found, including those belonging to your dependencies. + +Testing +======= + +Hopefully all your dependencies are fully tested... but we can't always depend on that. +In the event that they are tested with Ceedling, you'll probably want to consider using +the `:subprojects` plugin instead of this one. The purpose of this plugin is to pull in +third party code for release... and to provide a mockable interface for Ceedling to use +during its tests of other modules. + +If that's what you're after... you've found the right plugin! + +Happy Testing! diff --git a/CAN_App/vendor/ceedling/plugins/dependencies/config/defaults.yml b/CAN_App/vendor/ceedling/plugins/dependencies/config/defaults.yml new file mode 100644 index 0000000..0415f8e --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/dependencies/config/defaults.yml @@ -0,0 +1,5 @@ +--- +:dependencies: + :libraries: [] + +... diff --git a/CAN_App/vendor/ceedling/plugins/dependencies/dependencies.rake b/CAN_App/vendor/ceedling/plugins/dependencies/dependencies.rake new file mode 100644 index 0000000..08a1a48 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/dependencies/dependencies.rake @@ -0,0 +1,147 @@ + +DEPENDENCIES_LIBRARIES.each do |deplib| + + # Look up the name of this dependency library + deplib_name = @ceedling[DEPENDENCIES_SYM].get_name(deplib) + + # Make sure the required working directories exists + # (don't worry about the subdirectories. That's the job of the dep's build tool) + paths = @ceedling[DEPENDENCIES_SYM].get_working_paths(deplib) + paths.each {|path| directory(path) } + task :directories => paths + + all_deps = @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + + # Add a rule for building the actual libraries from dependency list + (@ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + ).each do |libpath| + file libpath do |filetask| + path = filetask.name + + # We double-check that it doesn't already exist, because this process sometimes + # produces multiple files, but they may have already been flagged as invoked + unless (File.exists?(path)) + + # Set Environment Variables, Fetch, and Build + @ceedling[DEPENDENCIES_SYM].set_env_if_required(path) + @ceedling[DEPENDENCIES_SYM].fetch_if_required(path) + @ceedling[DEPENDENCIES_SYM].build_if_required(path) + end + end + end + + # Add a rule for building the source and includes from dependency list + (@ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + ).each do |libpath| + task libpath do |filetask| + path = filetask.name + + unless (File.file?(path) || File.directory?(path)) + + # Set Environment Variables, Fetch, and Build + @ceedling[DEPENDENCIES_SYM].set_env_if_required(path) + @ceedling[DEPENDENCIES_SYM].fetch_if_required(path) + @ceedling[DEPENDENCIES_SYM].build_if_required(path) + end + end + end + + # Give ourselves a way to trigger individual dependencies + namespace DEPENDENCIES_SYM do + namespace :deploy do + # Add task to directly just build this dependency + task(deplib_name => @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib)) do |t,args| + @ceedling[DEPENDENCIES_SYM].deploy_if_required(deplib_name) + end + end + + namespace :make do + # Add task to directly just build this dependency + task(deplib_name => all_deps) + end + + namespace :clean do + # Add task to directly clobber this dependency + task(deplib_name) do + @ceedling[DEPENDENCIES_SYM].clean_if_required(deplib_name) + end + end + + namespace :fetch do + # Add task to directly clobber this dependency + task(deplib_name) do + @ceedling[DEPENDENCIES_SYM].fetch_if_required(deplib_name) + end + end + end + + # Add source files to our list of things to build during release + source_files = @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + task PROJECT_RELEASE_BUILD_TARGET => source_files + + # Finally, add the static libraries to our RELEASE build dependency list + static_libs = @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + task RELEASE_SYM => static_libs + + # Add the dynamic libraries to our RELEASE task dependency list so that they will be copied automatically + dynamic_libs = @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + task RELEASE_SYM => dynamic_libs + + # Add the include dirs / files to our list of dependencies for release + headers = @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + task RELEASE_SYM => headers + + # Paths to Libraries need to be Added to the Lib Path List + all_libs = static_libs + dynamic_libs + PATHS_LIBRARIES ||= [] + all_libs.each {|lib| PATHS_LIBRARIES << File.dirname(lib) } + PATHS_LIBRARIES.uniq! + PATHS_LIBRARIES.reject!{|s| s.empty?} + + # Libraries Need to be Added to the Library List + LIBRARIES_SYSTEM ||= [] + all_libs.each {|lib| LIBRARIES_SYSTEM << File.basename(lib,'.*').sub(/^lib/,'') } + LIBRARIES_SYSTEM.uniq! + LIBRARIES_SYSTEM.reject!{|s| s.empty?} +end + +# Add any artifact:include or :source folders to our release & test includes paths so linking and mocking work. +@ceedling[DEPENDENCIES_SYM].add_headers_and_sources() + +# Add tasks for building or cleaning ALL depencies +namespace DEPENDENCIES_SYM do + desc "Deploy missing dependencies." + task :deploy => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:deploy:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Build any missing dependencies." + task :make => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:make:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Clean all dependencies." + task :clean => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:clean:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Fetch all dependencies." + task :fetch => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:fetch:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} +end + +namespace :files do + desc "List all collected dependency libraries." + task :dependencies do + puts "dependency files:" + deps = [] + DEPENDENCIES_LIBRARIES.each do |deplib| + deps << @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + deps << @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + end + deps.flatten! + deps.sort.each {|dep| puts " - #{dep}"} + puts "file count: #{deps.size}" + end +end + +# Make sure that we build dependencies before attempting to tackle any of the unit tests +Rake::Task[:test_deps].enhance ['dependencies:make'] diff --git a/CAN_App/vendor/ceedling/plugins/dependencies/lib/dependencies.rb b/CAN_App/vendor/ceedling/plugins/dependencies/lib/dependencies.rb new file mode 100644 index 0000000..fc8ae99 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/dependencies/lib/dependencies.rb @@ -0,0 +1,237 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +DEPENDENCIES_ROOT_NAME = 'dependencies' +DEPENDENCIES_TASK_ROOT = DEPENDENCIES_ROOT_NAME + ':' +DEPENDENCIES_SYM = DEPENDENCIES_ROOT_NAME.to_sym + +class Dependencies < Plugin + + def setup + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Set up a fast way to look up dependencies by name or static lib path + @dependencies = {} + @dynamic_libraries = [] + DEPENDENCIES_LIBRARIES.each do |deplib| + + @dependencies[ deplib[:name] ] = deplib.clone + all_deps = get_static_libraries_for_dependency(deplib) + + get_dynamic_libraries_for_dependency(deplib) + + get_include_directories_for_dependency(deplib) + + get_source_files_for_dependency(deplib) + all_deps.each do |key| + @dependencies[key] = @dependencies[ deplib[:name] ] + end + + @dynamic_libraries += get_dynamic_libraries_for_dependency(deplib) + end + end + + def config + updates = { + :collection_paths_include => COLLECTION_PATHS_INCLUDE, + :collection_all_headers => COLLECTION_ALL_HEADERS, + } + + @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib).each do |incpath| + updates[:collection_paths_include] << incpath + Dir[ File.join(incpath, "*#{EXTENSION_HEADER}") ].each do |f| + updates[:collection_all_headers] << f + end + end + + return updates + end + + def get_name(deplib) + raise "Each dependency must have a name!" if deplib[:name].nil? + return deplib[:name].gsub(/\W*/,'') + end + + def get_source_path(deplib) + return deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_build_path(deplib) + return deplib[:build_path] || deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_artifact_path(deplib) + return deplib[:artifact_path] || deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_working_paths(deplib) + paths = [deplib[:source_path], deplib[:build_path], deplib[:artifact_paths]].compact.uniq + paths = [ File.join('dependencies', get_name(deplib)) ] if (paths.empty?) + return paths + end + + def get_static_libraries_for_dependency(deplib) + (deplib[:artifacts][:static_libraries] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_dynamic_libraries_for_dependency(deplib) + (deplib[:artifacts][:dynamic_libraries] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_source_files_for_dependency(deplib) + (deplib[:artifacts][:source] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_include_directories_for_dependency(deplib) + paths = (deplib[:artifacts][:includes] || []).map {|path| File.join(get_artifact_path(deplib), path)} + @ceedling[:file_system_utils].collect_paths(paths) + end + + def set_env_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + return if (blob[:environment].nil?) + return if (blob[:environment].empty?) + + blob[:environment].each do |e| + m = e.match(/^(\w+)\s*(\+?\-?=)\s*(.*)$/) + unless m.nil? + case m[2] + when "+=" + ENV[m[1]] = (ENV[m[1]] || "") + m[3] + when "-=" + ENV[m[1]] = (ENV[m[1]] || "").gsub(m[3],'') + else + ENV[m[1]] = m[3] + end + end + end + end + + def fetch_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + return if (blob[:fetch].nil?) + return if (blob[:fetch][:method].nil?) + return if (directory(blob[:source_path]) && !Dir.empty?(blob[:source_path])) + + steps = case blob[:fetch][:method] + when :none + return + when :zip + [ "gzip -d #{blob[:fetch][:source]}" ] + when :git + branch = blob[:fetch][:tag] || blob[:fetch][:branch] || '' + branch = ("-b " + branch) unless branch.empty? + unless blob[:fetch][:hash].nil? + # Do a deep clone to ensure the commit we want is available + retval = [ "git clone #{branch} #{blob[:fetch][:source]} ." ] + # Checkout the specified commit + retval << "git checkout #{blob[:fetch][:hash]}" + else + # Do a thin clone + retval = [ "git clone #{branch} --depth 1 #{blob[:fetch][:source]} ." ] + end + when :svn + revision = blob[:fetch][:revision] || '' + revision = ("--revision " + branch) unless branch.empty? + retval = [ "svn checkout #{revision} #{blob[:fetch][:source]} ." ] + retval + when :custom + blob[:fetch][:executable] + else + raise "Unknown fetch method '#{blob[:fetch][:method].to_s}' for dependency '#{blob[:name]}'" + end + + # Perform the actual fetching + @ceedling[:streaminator].stdout_puts("Fetching dependency #{blob[:name]}...", Verbosity::NORMAL) + Dir.chdir(get_source_path(blob)) do + steps.each do |step| + @ceedling[:tool_executor].exec( step ) + end + end + end + + def build_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't clean anything unless we know how to fetch a new copy + if (blob[:build].nil? || blob[:build].empty?) + @ceedling[:streaminator].stdout_puts("Nothing to build for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the build + @ceedling[:streaminator].stdout_puts("Building dependency #{blob[:name]}...", Verbosity::NORMAL) + Dir.chdir(get_build_path(blob)) do + blob[:build].each do |step| + @ceedling[:tool_executor].exec( step ) + end + end + end + + def clean_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't clean anything unless we know how to fetch a new copy + if (blob[:fetch].nil? || blob[:fetch][:method].nil? || (blob[:fetch][:method] == :none)) + @ceedling[:streaminator].stdout_puts("Nothing to clean for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the actual Cleaning + @ceedling[:streaminator].stdout_puts("Cleaning dependency #{blob[:name]}...", Verbosity::NORMAL) + get_working_paths(blob).each do |path| + FileUtils.rm_rf(path) if File.directory?(path) + end + end + + def deploy_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't need to deploy anything if there isn't anything to deploy + if (blob[:artifacts].nil? || blob[:artifacts][:dynamic_libraries].nil? || blob[:artifacts][:dynamic_libraries].empty?) + @ceedling[:streaminator].stdout_puts("Nothing to deploy for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the actual Deploying + @ceedling[:streaminator].stdout_puts("Deploying dependency #{blob[:name]}...", Verbosity::NORMAL) + FileUtils.cp( lib_path, File.dirname(PROJECT_RELEASE_BUILD_TARGET) ) + end + + def add_headers_and_sources() + # Search for header file paths and files to add to our collections + DEPENDENCIES_LIBRARIES.each do |deplib| + get_include_directories_for_dependency(deplib).each do |header| + cfg = @ceedling[:configurator].project_config_hash + cfg[:collection_paths_include] << header + cfg[:collection_paths_source_and_include] << header + cfg[:collection_paths_test_support_source_include] << header + cfg[:collection_paths_test_support_source_include_vendor] << header + cfg[:collection_paths_release_toolchain_include] << header + Dir[ File.join(header, "*#{EXTENSION_HEADER}") ].each do |f| + cfg[:collection_all_headers] << f + end + end + + get_source_files_for_dependency(deplib).each do |source| + cfg = @ceedling[:configurator].project_config_hash + cfg[:collection_paths_source_and_include] << source + cfg[:collection_paths_test_support_source_include] << source + cfg[:collection_paths_test_support_source_include_vendor] << source + cfg[:collection_paths_release_toolchain_include] << source + Dir[ File.join(source, "*#{EXTENSION_SOURCE}") ].each do |f| + cfg[:collection_all_source] << f + end + end + end + + # Make all these updated files findable by Ceedling + @ceedling[:file_finder].prepare_search_sources() + end +end + +# end blocks always executed following rake run +END { +} diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/README.md b/CAN_App/vendor/ceedling/plugins/fake_function_framework/README.md new file mode 100644 index 0000000..8042775 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/README.md @@ -0,0 +1,250 @@ +# A Fake Function Framework Plug-in for Ceedling + +This is a plug-in for [Ceedling](https://github.com/ThrowTheSwitch/Ceedling) to use the [Fake Function Framework](https://github.com/meekrosoft/fff) for mocking instead of CMock. + +Using fff provides less strict mocking than CMock, and allows for more loosely-coupled tests. +And, when tests fail -- since you get the actual line number of the failure -- it's a lot easier to figure out what went wrong. + +## Installing the plug-in + +To use the plugin you need to 1) get the contents of this repo and 2) configure your project to use it. + +### Get the source + +The easiest way to get the source is to just clone this repo into the Ceedling plugin folder for your existing Ceedling project. +(Don't have a Ceedling project already? [Here are instructions to create one.](http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling)) +From within `<your-project>/vendor/ceedling/plugins`, run: + +`git clone https://github.com/ElectronVector/fake_function_framework.git` + +This will create a new folder named `fake_function_framework` in the plugins folder. + +### Enable the plug-in. + +The plug-in is enabled from within your project.yml file. + +In the `:plugins` configuration, add `fake_function_framework` to the list of enabled plugins: + +```yaml +:plugins: + :load_paths: + - vendor/ceedling/plugins + :enabled: + - stdout_pretty_tests_report + - module_generator + - fake_function_framework +``` +*Note that you could put the plugin source in some other loaction. +In that case you'd need to add a new path the `:load_paths`.* + +## How to use it + +You use fff with Ceedling the same way you used to use CMock. +Modules can still be generated with the default module generator: `rake module:create[my_module]`. +If you want to "mock" `some_module.h` in your tests, just `#include "mock_some_module.h"`. +This creates a fake function for each of the functions defined in `some_module.h`. + +The name of each fake is the original function name with an appended `_fake`. +For example, if we're generating fakes for a stack module with `push` and `pop` functions, we would have the fakes `push_fake` and `pop_fake`. +These fakes are linked into our test executable so that any time our unit under test calls `push` or `pop` our fakes are called instead. + +Each of these fakes is actually a structure containing information about how the function was called, and what it might return. +We can use Unity to inspect these fakes in our tests, and verify the interactions of our units. +There is also a global structure named `fff` which we can use to check the sequence of calls. + +The fakes can also be configured to return particular values, so you can exercise the unit under test however you want. + +The examples below explain how to use fff to test a variety of module interactions. +Each example uses fakes for a "display" module, created from a display.h file with `#include "mock_display.h"`. The `display.h` file must exist and must contain the prototypes for the functions to be faked. + +### Test that a function was called once + +```c +void +test_whenTheDeviceIsReset_thenTheStatusLedIsTurnedOff() +{ + // When + event_deviceReset(); + + // Then + TEST_ASSERT_EQUAL(1, display_turnOffStatusLed_fake.call_count); +} +``` + +### Test that a function was NOT called + +```c +void +test_whenThePowerReadingIsLessThan5_thenTheStatusLedIsNotTurnedOn(void) +{ + // When + event_powerReadingUpdate(4); + + // Then + TEST_ASSERT_EQUAL(0, display_turnOnStatusLed_fake.call_count); +} +``` + +## Test that a single function was called with the correct argument + +```c +void +test_whenTheVolumeKnobIsMaxed_thenVolumeDisplayIsSetTo11(void) +{ + // When + event_volumeKnobMaxed(); + + // Then + TEST_ASSERT_EQUAL(1, display_setVolume_fake.call_count); + TEST_ASSERT_EQUAL(11, display_setVolume_fake.arg0_val); +} +``` + +## Test that calls are made in a particular sequence + +```c +void +test_whenTheModeSelectButtonIsPressed_thenTheDisplayModeIsCycled(void) +{ + // When + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + + // Then + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToMinimum, fff.call_history[0]); + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToMaximum, fff.call_history[1]); + TEST_ASSERT_EQUAL_PTR((void*)display_setModeToAverage, fff.call_history[2]); +} +``` + +## Fake a return value from a function + +```c +void +test_givenTheDisplayHasAnError_whenTheDeviceIsPoweredOn_thenTheDisplayIsPoweredDown(void) +{ + // Given + display_isError_fake.return_val = true; + + // When + event_devicePoweredOn(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); +} +``` + +## Fake a function with a value returned by reference + +```c +void +test_givenTheUserHasTypedSleep_whenItIsTimeToCheckTheKeyboard_theDisplayIsPoweredDown(void) +{ + // Given + char mockedEntry[] = "sleep"; + void return_mock_value(char * entry, int length) + { + if (length > strlen(mockedEntry)) + { + strncpy(entry, mockedEntry, length); + } + } + display_getKeyboardEntry_fake.custom_fake = return_mock_value; + + // When + event_keyboardCheckTimerExpired(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); +} +``` + +## Fake a function with a function pointer parameter + +``` +void +test_givenNewDataIsAvailable_whenTheDisplayHasUpdated_thenTheEventIsComplete(void) +{ + // A mock function for capturing the callback handler function pointer. + void(*registeredCallback)(void) = 0; + void mock_display_updateData(int data, void(*callback)(void)) + { + //Save the callback function. + registeredCallback = callback; + } + display_updateData_fake.custom_fake = mock_display_updateData; + + // Given + event_newDataAvailable(10); + + // When + if (registeredCallback != 0) + { + registeredCallback(); + } + + // Then + TEST_ASSERT_EQUAL(true, eventProcessor_isLastEventComplete()); +} +``` + +## Helper macros + +For convenience, there are also some helper macros that create new Unity-style asserts: + +- `TEST_ASSERT_CALLED(function)`: Asserts that a function was called once. +- `TEST_ASSERT_NOT_CALLED(function)`: Asserts that a function was never called. +- `TEST_ASSERT_CALLED_TIMES(times, function)`: Asserts that a function was called a particular number of times. +- `TEST_ASSERT_CALLED_IN_ORDER(order, function)`: Asserts that a function was called in a particular order. + +Here's how you might use one of these instead of simply checking the call_count value: + +```c +void +test_whenTheDeviceIsReset_thenTheStatusLedIsTurnedOff() +{ + // When + event_deviceReset(); + + // Then + // This how to directly use fff... + TEST_ASSERT_EQUAL(1, display_turnOffStatusLed_fake.call_count); + // ...and this is how to use the helper macro. + TEST_ASSERT_CALLED(display_turnOffStatusLed); +} +``` + +## Test setup + +All of the fake functions, and any fff global state are all reset automatically between each test. + +## CMock configuration + +Use still use some of the CMock configuration options for setting things like the mock prefix, and for including additional header files in the mock files. + +```yaml +:cmock: + :mock_prefix: mock_ + :includes: + - + :includes_h_pre_orig_header: + - + :includes_h_post_orig_header: + - + :includes_c_pre_header: + - + :includes_c_post_header: +``` + +## Running the tests + +There are unit and integration tests for the plug-in itself. +These are run with the default `rake` task. +The integration test runs the tests for the example project in examples/fff_example. +For the integration tests to succeed, this repository must be placed in a Ceedling tree in the plugins folder. + +## More examples + +There is an example project in examples/fff_example. +It shows how to use the plug-in with some full-size examples. diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/Rakefile b/CAN_App/vendor/ceedling/plugins/fake_function_framework/Rakefile new file mode 100644 index 0000000..bc55941 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/Rakefile @@ -0,0 +1,19 @@ +require 'rake' +require 'rspec/core/rake_task' + +desc "Run all rspecs" +RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = Dir.glob('spec/**/*_spec.rb') + t.rspec_opts = '--format documentation' + # t.rspec_opts << ' more options' +end + +desc "Run integration test on example" +task :integration_test do + chdir("./examples/fff_example") do + sh "rake clobber" + sh "rake test:all" + end +end + +task :default => [:spec, :integration_test] \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml new file mode 100644 index 0000000..6bda222 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml @@ -0,0 +1,71 @@ +--- + +# Notes: +# Sample project C code is not presently written to produce a release artifact. +# As such, release build options are disabled. +# This sample, therefore, only demonstrates running a collection of unit tests. + +:project: + :use_exceptions: FALSE + :use_test_preprocessor: TRUE + :use_auxiliary_dependencies: TRUE + :build_root: build +# :release_build: TRUE + :test_file_prefix: test_ + +#:release_build: +# :output: MyApp.out +# :use_assembly: FALSE + +:environment: + +:extension: + :executable: .out + +:paths: + :test: + - +:test/** + :source: + - src/** + :support: + +:defines: + # in order to add common defines: + # 1) remove the trailing [] from the :common: section + # 2) add entries to the :common: section (e.g. :test: has TEST defined) + :commmon: &common_defines [] + :test: + - *common_defines + - TEST + :test_preprocess: + - *common_defines + - TEST + +:cmock: + :mock_prefix: mock_ + :when_no_prototypes: :warn + :enforce_strict_ordering: TRUE + :plugins: + - :ignore + - :callback + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + +#:tools: +# Ceedling defaults to using gcc for compiling, linking, etc. +# As [:tools] is blank, gcc will be used (so long as it's in your system path) +# See documentation to configure a given toolchain for use + +:plugins: + :load_paths: + # This change from the default is for running Ceedling out of another folder. + - ../../../../plugins + :enabled: + - stdout_pretty_tests_report + - module_generator + - fake_function_framework +... diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb new file mode 100644 index 0000000..e484d5f --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb @@ -0,0 +1,7 @@ +# This change from the default is for running Ceedling out of another folder. +PROJECT_CEEDLING_ROOT = "../../../.." +load "#{PROJECT_CEEDLING_ROOT}/lib/ceedling.rb" + +Ceedling.load_project + +task :default => %w[ test:all release ] diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c new file mode 100644 index 0000000..6a40323 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c @@ -0,0 +1 @@ +#include "bar.h" diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h new file mode 100644 index 0000000..febc586 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h @@ -0,0 +1,14 @@ +#ifndef bar_H +#define bar_H + +#include "custom_types.h" + +void bar_turn_on(void); +void bar_print_message(const char * message); +void bar_print_message_formatted(const char * format, ...); +void bar_numbers(int one, int two, char three); +void bar_const_test(const char * a, char * const b, const int c); +custom_t bar_needs_custom_type(void); +const char * bar_return_const_ptr(int one); + +#endif // bar_H diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h new file mode 100644 index 0000000..b426b32 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h @@ -0,0 +1,6 @@ +#ifndef custom_types_H +#define custom_types_H + +typedef int custom_t; + +#endif diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c new file mode 100644 index 0000000..2f03449 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "display.h" + +void display_turnOffStatusLed(void) +{ + printf("Display: Status LED off"); +} \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h new file mode 100644 index 0000000..def2996 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h @@ -0,0 +1,16 @@ +#include <stdbool.h> + +void display_turnOffStatusLed(void); +void display_turnOnStatusLed(void); +void display_setVolume(int level); +void display_setModeToMinimum(void); +void display_setModeToMaximum(void); +void display_setModeToAverage(void); +bool display_isError(void); +void display_powerDown(void); +void display_updateData(int data, void(*updateCompleteCallback)(void)); + +/* + The entry is returned (up to `length` bytes) in the provided `entry` buffer. +*/ +void display_getKeyboardEntry(char * entry, int length); diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c new file mode 100644 index 0000000..916a923 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c @@ -0,0 +1,93 @@ +/* + This module implements some business logic to test. + + Signal events by calling the functions on the module. +*/ + +#include <stdio.h> +#include <string.h> +#include "event_processor.h" +#include "display.h" + +void event_deviceReset(void) +{ + //printf ("Device reset\n"); + display_turnOffStatusLed(); +} + +void event_volumeKnobMaxed(void) +{ + display_setVolume(11); +} + +void event_powerReadingUpdate(int powerReading) +{ + if (powerReading >= 5) + { + display_turnOnStatusLed(); + } +} + +void event_modeSelectButtonPressed(void) +{ + static int mode = 0; + + if (mode == 0) + { + display_setModeToMinimum(); + mode++; + } + else if (mode == 1) + { + display_setModeToMaximum(); + mode++; + } + else if (mode == 2) + { + display_setModeToAverage(); + mode++; + } + else + { + mode = 0; + } +} + +void event_devicePoweredOn(void) +{ + if (display_isError()) + { + display_powerDown(); + } +} + +void event_keyboardCheckTimerExpired(void) +{ + char userEntry[100]; + + display_getKeyboardEntry(userEntry, 100); + + if (strcmp(userEntry, "sleep") == 0) + { + display_powerDown(); + } +} + +static bool event_lastComplete = false; + +/* Function called when the display update is complete. */ +static void displayUpdateComplete(void) +{ + event_lastComplete = true; +} + +void event_newDataAvailable(int data) +{ + event_lastComplete = false; + display_updateData(data, displayUpdateComplete); +} + +bool eventProcessor_isLastEventComplete(void) +{ + return event_lastComplete; +} diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h new file mode 100644 index 0000000..a79e68c --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h @@ -0,0 +1,11 @@ +#include <stdbool.h> + +void event_deviceReset(void); +void event_volumeKnobMaxed(void); +void event_powerReadingUpdate(int powerReading); +void event_modeSelectButtonPressed(void); +void event_devicePoweredOn(void); +void event_keyboardCheckTimerExpired(void); +void event_newDataAvailable(int data); + +bool eventProcessor_isLastEventComplete(void); diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c new file mode 100644 index 0000000..c05b115 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c @@ -0,0 +1,16 @@ +#include "foo.h" +#include "bar.h" +#include "subfolder/zzz.h" + +void foo_turn_on(void) { + bar_turn_on(); + zzz_sleep(1, "sleepy"); +} + +void foo_print_message(const char * message) { + bar_print_message(message); +} + +void foo_print_special_message(void) { + bar_print_message_formatted("The numbers are %d, %d and %d", 1, 2, 3); +} diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h new file mode 100644 index 0000000..3fea699 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h @@ -0,0 +1,8 @@ +#ifndef foo_H +#define foo_H + +void foo_turn_on(void); +void foo_print_message(const char * message); +void foo_print_special_message(void); + +#endif // foo_H diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c new file mode 100644 index 0000000..85f370e --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c @@ -0,0 +1 @@ +#include "zzz.h" diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h new file mode 100644 index 0000000..32c5294 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h @@ -0,0 +1,6 @@ +#ifndef zzz_H +#define zzz_H + +int zzz_sleep(int time, char * name); + +#endif // zzz_H diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c new file mode 100644 index 0000000..9f99944 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c @@ -0,0 +1,155 @@ +#include "unity.h" +#include "event_processor.h" +#include "mock_display.h" +#include <string.h> + +void setUp (void) +{ +} + +void tearDown (void) +{ +} +/* + Test that a single function was called. +*/ +void +test_whenTheDeviceIsReset_thenTheStatusLedIsTurnedOff() +{ + // When + event_deviceReset(); + + // Then + TEST_ASSERT_EQUAL(1, display_turnOffStatusLed_fake.call_count); + // or use the helper macro... + TEST_ASSERT_CALLED(display_turnOffStatusLed); +} + +/* + Test that a single function is NOT called. +*/ +void +test_whenThePowerReadingIsLessThan5_thenTheStatusLedIsNotTurnedOn(void) +{ + // When + event_powerReadingUpdate(4); + + // Then + TEST_ASSERT_EQUAL(0, display_turnOnStatusLed_fake.call_count); + // or use the helper macro... + TEST_ASSERT_NOT_CALLED(display_turnOffStatusLed); +} + +/* + Test that a single function was called with the correct arugment. +*/ +void +test_whenTheVolumeKnobIsMaxed_thenVolumeDisplayIsSetTo11(void) +{ + // When + event_volumeKnobMaxed(); + + // Then + TEST_ASSERT_EQUAL(1, display_setVolume_fake.call_count); + // or use the helper macro... + TEST_ASSERT_CALLED(display_setVolume); + TEST_ASSERT_EQUAL(11, display_setVolume_fake.arg0_val); +} + +/* + Test a sequence of calls. +*/ + +void +test_whenTheModeSelectButtonIsPressed_thenTheDisplayModeIsCycled(void) +{ + // When + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + event_modeSelectButtonPressed(); + + // Then + TEST_ASSERT_EQUAL_PTR((void *)display_setModeToMinimum, fff.call_history[0]); + TEST_ASSERT_EQUAL_PTR((void *)display_setModeToMaximum, fff.call_history[1]); + TEST_ASSERT_EQUAL_PTR((void *)display_setModeToAverage, fff.call_history[2]); + // or use the helper macros... + TEST_ASSERT_CALLED_IN_ORDER(0, display_setModeToMinimum); + TEST_ASSERT_CALLED_IN_ORDER(1, display_setModeToMaximum); + TEST_ASSERT_CALLED_IN_ORDER(2, display_setModeToAverage); +} + +/* + Mock a return value from a function. +*/ +void +test_givenTheDisplayHasAnError_whenTheDeviceIsPoweredOn_thenTheDisplayIsPoweredDown(void) +{ + // Given + display_isError_fake.return_val = true; + + // When + event_devicePoweredOn(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); + // or use the helper macro... + TEST_ASSERT_CALLED(display_powerDown); +} + +/* + Mock a sequence of calls with return values. +*/ + +/* + Mocking a function with a value returned by reference. +*/ +void +test_givenTheUserHasTypedSleep_whenItIsTimeToCheckTheKeyboard_theDisplayIsPoweredDown(void) +{ + // Given + char mockedEntry[] = "sleep"; + void return_mock_value(char * entry, int length) + { + if (length > strlen(mockedEntry)) + { + strncpy(entry, mockedEntry, length); + } + } + display_getKeyboardEntry_fake.custom_fake = return_mock_value; + + // When + event_keyboardCheckTimerExpired(); + + // Then + TEST_ASSERT_EQUAL(1, display_powerDown_fake.call_count); + // or use the helper macro... + TEST_ASSERT_CALLED(display_powerDown); +} + +/* + Mock a function with a function pointer parameter. +*/ +void +test_givenNewDataIsAvailable_whenTheDisplayHasUpdated_thenTheEventIsComplete(void) +{ + // A mock function for capturing the callback handler function pointer. + void(*registeredCallback)(void) = 0; + void mock_display_updateData(int data, void(*callback)(void)) + { + //Save the callback function. + registeredCallback = callback; + } + display_updateData_fake.custom_fake = mock_display_updateData; + + // Given + event_newDataAvailable(10); + + // When + if (registeredCallback != 0) + { + registeredCallback(); + } + + // Then + TEST_ASSERT_EQUAL(true, eventProcessor_isLastEventComplete()); +} diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c new file mode 100644 index 0000000..12dd61a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c @@ -0,0 +1,47 @@ +#include "unity.h" +#include "foo.h" +#include "mock_bar.h" +#include "mock_zzz.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_foo(void) +{ + //When + foo_turn_on(); + + //Then + TEST_ASSERT_EQUAL(1, bar_turn_on_fake.call_count); + TEST_ASSERT_EQUAL(1, zzz_sleep_fake.call_count); + TEST_ASSERT_EQUAL_STRING("sleepy", zzz_sleep_fake.arg1_val); +} + +void test_foo_again(void) +{ + //When + foo_turn_on(); + + //Then + TEST_ASSERT_EQUAL(1, bar_turn_on_fake.call_count); +} + +void test_foo_mock_with_const(void) +{ + foo_print_message("123"); + + TEST_ASSERT_EQUAL(1, bar_print_message_fake.call_count); + TEST_ASSERT_EQUAL_STRING("123", bar_print_message_fake.arg0_val); +} + +void test_foo_mock_with_variable_args(void) +{ + foo_print_special_message(); + TEST_ASSERT_EQUAL(1, bar_print_message_formatted_fake.call_count); + TEST_ASSERT_EQUAL_STRING("The numbers are %d, %d and %d", bar_print_message_formatted_fake.arg0_val); +} diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb new file mode 100644 index 0000000..51a90b3 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb @@ -0,0 +1,87 @@ +require 'ceedling/plugin' +require 'fff_mock_generator' + +class FakeFunctionFramework < Plugin + + # Set up Ceedling to use this plugin. + def setup + # Get the location of this plugin. + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + puts "Using fake function framework (fff)..." + + # Switch out the cmock_builder with our own. + @ceedling[:cmock_builder].cmock = FffMockGeneratorForCMock.new(@ceedling[:setupinator].config_hash[:cmock]) + + # Add the path to fff.h to the include paths. + COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR << "#{@plugin_root}/vendor/fff" + COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR << "#{@plugin_root}/src" + end + + def post_runner_generate(arg_hash) + # After the test runner file has been created, append the FFF globals + # definition to the end of the test runner. These globals will be shared by + # all mocks linked into the test. + File.open(arg_hash[:runner_file], 'a') do |f| + f.puts + f.puts "//=======Defintions of FFF variables=====" + f.puts %{#include "fff.h"} + f.puts "DEFINE_FFF_GLOBALS;" + end + end + +end # class FakeFunctionFramework + +class FffMockGeneratorForCMock + + def initialize(options=nil) + @cm_config = CMockConfig.new(options) + @cm_parser = CMockHeaderParser.new(@cm_config) + @silent = (@cm_config.verbosity < 2) + + # These are the additional files to include in the mock files. + @includes_h_pre_orig_header = (@cm_config.includes || @cm_config.includes_h_pre_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} + @includes_h_post_orig_header = (@cm_config.includes_h_post_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} + @includes_c_pre_header = (@cm_config.includes_c_pre_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} + @includes_c_post_header = (@cm_config.includes_c_post_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} + end + + def setup_mocks(files) + [files].flatten.each do |src| + generate_mock (src) + end + end + + def generate_mock (header_file_to_mock) + module_name = File.basename(header_file_to_mock, '.h') + puts "Creating mock for #{module_name}..." unless @silent + mock_name = @cm_config.mock_prefix + module_name + @cm_config.mock_suffix + mock_path = @cm_config.mock_path + if @cm_config.subdir + # If a subdirectory has been configured, append it to the mock path. + mock_path = "#{mock_path}/#{@cm_config.subdir}" + end + full_path_for_mock = "#{mock_path}/#{mock_name}" + + # Parse the header file so we know what to mock. + parsed_header = @cm_parser.parse(module_name, File.read(header_file_to_mock)) + + # Create the directory if it doesn't exist. + mkdir_p full_path_for_mock.pathmap("%d") + + # Generate the mock header file. + puts "Creating mock: #{full_path_for_mock}.h" + + # Create the mock header. + File.open("#{full_path_for_mock}.h", 'w') do |f| + f.write(FffMockGenerator.create_mock_header(module_name, mock_name, parsed_header, + @includes_h_pre_orig_header, @includes_h_post_orig_header)) + end + + # Create the mock source file. + File.open("#{full_path_for_mock}.c", 'w') do |f| + f.write(FffMockGenerator.create_mock_source(mock_name, parsed_header, + @includes_c_pre_orig_header, @includes_c_post_orig_header)) + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb new file mode 100644 index 0000000..9dc03a6 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb @@ -0,0 +1,163 @@ +# Creates mock files from parsed header files that can be linked into applications. +# The mocks created are compatible with CMock for use with Ceedling. + +class FffMockGenerator + + def self.create_mock_header(module_name, mock_name, parsed_header, pre_includes=nil, + post_includes=nil) + output = StringIO.new + write_opening_include_guard(mock_name, output) + output.puts + write_extra_includes(pre_includes, output) + write_header_includes(module_name, output) + write_extra_includes(post_includes, output) + output.puts + write_typedefs(parsed_header, output) + output.puts + write_function_declarations(parsed_header, output) + output.puts + write_control_function_prototypes(mock_name, output) + output.puts + write_closing_include_guard(mock_name, output) + output.string + end + + def self.create_mock_source (mock_name, parsed_header, pre_includes=nil, + post_includes=nil) + output = StringIO.new + write_extra_includes(pre_includes, output) + write_source_includes(mock_name, output) + write_extra_includes(post_includes, output) + output.puts + write_function_definitions(parsed_header, output) + output.puts + write_control_function_definitions(mock_name, parsed_header, output) + output.string + end + + private + +# Header file generation functions. + + def self.write_opening_include_guard(mock_name, output) + output.puts "#ifndef #{mock_name}_H" + output.puts "#define #{mock_name}_H" + end + + def self.write_header_includes(module_name, output) + output.puts %{#include "fff.h"} + output.puts %{#include "fff_unity_helper.h"} + output.puts %{#include "#{module_name}.h"} + end + + def self.write_typedefs(parsed_header, output) + return unless parsed_header.key?(:typedefs) + parsed_header[:typedefs].each do |typedef| + output.puts typedef + end + end + + def self.write_function_declarations(parsed_header, output) + write_function_macros("DECLARE", parsed_header, output) + end + + + def self.write_control_function_prototypes(mock_name, output) + output.puts "void #{mock_name}_Init(void);" + output.puts "void #{mock_name}_Verify(void);" + output.puts "void #{mock_name}_Destroy(void);" + end + + def self.write_closing_include_guard(mock_name, output) + output.puts "#endif // #{mock_name}_H" + end + +# Source file generation functions. + + def self.write_source_includes (mock_name, output) + output.puts "#include <string.h>" + output.puts %{#include "fff.h"} + output.puts %{#include "#{mock_name}.h"} + end + + def self.write_function_definitions(parsed_header, output) + write_function_macros("DEFINE", parsed_header, output) + end + + def self.write_control_function_definitions(mock_name, parsed_header, output) + output.puts "void #{mock_name}_Init(void)" + output.puts "{" + # In the init function, reset the FFF globals. These are used for things + # like the call history. + output.puts " FFF_RESET_HISTORY();" + + # Also, reset all of the fakes. + if parsed_header[:functions] + parsed_header[:functions].each do |function| + output.puts " RESET_FAKE(#{function[:name]})" + end + end + output.puts "}" + output.puts "void #{mock_name}_Verify(void)" + output.puts "{" + output.puts "}" + output.puts "void #{mock_name}_Destroy(void)" + output.puts "{" + output.puts "}" + end + +# Shared functions. + + def self.write_extra_includes(includes, output) + if includes + includes.each {|inc| output.puts "#include #{inc}\n"} + end + end + + def self.write_function_macros(macro_type, parsed_header, output) + return unless parsed_header.key?(:functions) + parsed_header[:functions].each do |function| + name = function[:name] + return_type = function[:return][:type] + if function.has_key? :modifier + # Prepend any modifier. If there isn't one, trim any leading whitespace. + return_type = "#{function[:modifier]} #{return_type}".lstrip + end + arg_count = function[:args].size + + # Check for variable arguments. + var_arg_suffix = "" + if function[:var_arg] + # If there are are variable arguments, then we need to add this argument + # to the count, update the suffix that will get added to the macro. + arg_count += 1 + var_arg_suffix = "_VARARG" + end + + # Generate the correct macro. + if return_type == 'void' + output.print "#{macro_type}_FAKE_VOID_FUNC#{arg_count}#{var_arg_suffix}(#{name}" + else + output.print "#{macro_type}_FAKE_VALUE_FUNC#{arg_count}#{var_arg_suffix}(#{return_type}, #{name}" + end + + # Append each argument type. + function[:args].each do |arg| + output.print ", " + if arg[:const?] + output.print "const " + end + output.print "#{arg[:type]}" + end + + # If this argument list ends with a variable argument, add it here at the end. + if function[:var_arg] + output.print ", ..." + end + + # Close the declaration. + output.puts ");" + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb new file mode 100644 index 0000000..e6ac11d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb @@ -0,0 +1,304 @@ +require 'stringio' +require 'fff_mock_generator.rb' +require 'header_generator.rb' + +# Test the contents of the .h file created for the mock. +describe "FffMockGenerator.create_mock_header" do + + context "when there is nothing to mock," do + let(:mock_header) { + parsed_header = {} + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated header file starts with an opening include guard" do + expect(mock_header).to start_with( + "#ifndef mock_display_H\n" + + "#define mock_display_H") + end + it "then the generated file ends with a closing include guard" do + expect(mock_header).to end_with( + "#endif // mock_display_H\n") + end + it "then the generated file includes the fff header" do + expect(mock_header).to include( + %{#include "fff.h"\n}) + end + it "then the generated file has a prototype for the init function" do + expect(mock_header).to include( + "void mock_display_Init(void);") + end + it "then the generated file has a prototype for the verify function" do + expect(mock_header).to include( + "void mock_display_Verify(void);") + end + it "then the generated file has a prototype for the destroy function" do + expect(mock_header).to include( + "void mock_display_Destroy(void);") + end + end + + context "when there is a function with no args and a void return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'display_turnOffStatusLed', :return_type => 'void'}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated header file starts with an opening include guard" do + expect(mock_header).to start_with( + "#ifndef mock_display_H\n" + + "#define mock_display_H") + end + it "then the generated header file contains a fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC0(display_turnOffStatusLed);" + ) + end + it "then the generated file ends with a closing include guard" do + expect(mock_header).to end_with( + "#endif // mock_display_H\n") + end + end + + context "when there is a function with no args and a bool return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'display_isError', :return_type => 'bool'}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC0(bool, display_isError);" + ) + end + end + + context "when there is a function with no args and an int return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'display_isError', :return_type => 'int'}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC0(int, display_isError);" + ) + end + end + + context "when there is a function with args and a void return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'display_setVolume', :return_type => 'void', :args => ['int']}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC1(display_setVolume, int);" + ) + end + end + + context "when there is a function with args and a value return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'a_function', :return_type => 'int', :args => ['char *']}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the fake function declaration" do + expect(mock_header).to include( + "FAKE_VALUE_FUNC1(int, a_function, char *);" + ) + end + end + + context "when there is a function with many args and a void return," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [{:name => 'a_function', :return_type => 'void', + :args => ['int', 'char *', 'int', 'int', 'bool', 'applesauce']}]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC6(a_function, int, char *, int, int, bool, applesauce);" + ) + end + end + + context "when there are multiple functions," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + [ {:name => 'a_function', :return_type => 'int', :args => ['char *']}, + {:name => 'another_function', :return_type => 'void'}, + {:name => 'three', :return_type => 'bool', :args => ['float', 'int']} + ]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the first fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC1(int, a_function, char *);" + ) + end + it "then the generated file contains the second fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC0(another_function);" + ) + end + it "then the generated file contains the third fake function declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC2(bool, three, float, int);" + ) + end + end + + context "when there is a typedef," do + let(:mock_header) { + parsed_header = create_cmock_style_parsed_header( + nil, ["typedef void (*displayCompleteCallback) (void);"]) + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the typedef" do + expect(mock_header).to include( + "typedef void (*displayCompleteCallback) (void);" + ) + end + end + + context "when there is a void function with variable arguments" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "function_with_var_args", + :return => {:type => "void"}, + :var_arg => "...", + :args => [{:type => 'char *'}] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the vararg declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC2_VARARG(function_with_var_args, char *, ...)" + ) + end + end + + context "when there is a function with a return value and variable arguments" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "function_with_var_args", + :return => {:type => "int"}, + :var_arg => "...", + :args => [{:type => 'char *'}] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the vararg declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC2_VARARG(int, function_with_var_args, char *, ...)" + ) + end + end + + context "when there is a void function with variable arguments and " + + "additional arguments" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "function_with_var_args", + :return => {:type => "void"}, + :var_arg => "...", + :args => [{:type => 'char *'}, {:type => 'int'}] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the vararg declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC3_VARARG(function_with_var_args, char *, int, ...)" + ) + end + end + + context "when there is a function with a pointer to a const value" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "const_test_function", + :return => {:type => "void"}, + :args => [{:type => "char *", :name => "a", :ptr? => false, :const? => true}, + {:type => "char *", :name => "b", :ptr? => false, :const? => false}] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the correct const argument in the declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VOID_FUNC2(const_test_function, const char *, char *)" + ) + end + end + + context "when there is a function that returns a const pointer" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "return_const_pointer_test_function", + :modifier => "const", + :return => {:type => "char *" }, + :args => [{:type => "int", :name => "a"}] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the correct const return value in the declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC1(const char *, return_const_pointer_test_function, int)" + ) + end + end + + context "when there is a function that returns a const int" do + let(:mock_header){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "return_const_int_test_function", + :modifier => "const", + :return => {:type => "int" }, + :args => [] + }] + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header) + } + it "then the generated file contains the correct const return value in the declaration" do + expect(mock_header).to include( + "DECLARE_FAKE_VALUE_FUNC0(const int, return_const_int_test_function)" + ) + end + end + + context "when there are pre-includes" do + let(:mock_header) { + parsed_header = {} + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header, + [%{"another_header.h"}]) + } + it "then they are included before the other files" do + expect(mock_header).to include( + %{#include "another_header.h"\n} + + %{#include "fff.h"} + ) + end + end + + context "when there are post-includes" do + let(:mock_header) { + parsed_header = {} + FffMockGenerator.create_mock_header("display", "mock_display", parsed_header, + nil, [%{"another_header.h"}]) + } + it "then they are included after the other files" do + expect(mock_header).to include( + %{#include "display.h"\n} + + %{#include "another_header.h"\n} + ) + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb new file mode 100644 index 0000000..364f852 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb @@ -0,0 +1,149 @@ +require 'stringio' +require 'fff_mock_generator.rb' + +# Test the contents of the .c file created for the mock. +describe "FffMockGenerator.create_mock_source" do + + context "when there is nothing to mock," do + let(:mock_source) { + parsed_header = {} + FffMockGenerator.create_mock_source("mock_my_module", parsed_header) + } + it "then the generated file includes the fff header" do + expect(mock_source).to include( + # fff.h also requires including string.h + %{#include <string.h>\n} + + %{#include "fff.h"} + ) + end + it "then the generated file includes the mock header" do + expect(mock_source).to include( + %{#include "mock_my_module.h"\n} + ) + end + it "then the generated file defines the init function" do + expect(mock_source).to include( + "void mock_my_module_Init(void)\n" + + "{\n" + + " FFF_RESET_HISTORY();\n" + + "}" + ) + end + it "then the generated file defines the verify function" do + expect(mock_source).to include( + "void mock_my_module_Verify(void)\n" + + "{\n" + + "}" + ) + end + it "then the generated file defines the destroy function" do + expect(mock_source).to include( + "void mock_my_module_Destroy(void)\n" + + "{\n" + + "}" + ) + end + end + + context "when there are multiple functions," do + let(:mock_source) { + parsed_header = create_cmock_style_parsed_header( + [ {:name => 'a_function', :return_type => 'int', :args => ['char *']}, + {:name => 'another_function', :return_type => 'void'}, + {:name => 'three', :return_type => 'bool', :args => ['float', 'int']} + ]) + FffMockGenerator.create_mock_source("mock_display", parsed_header) + } + it "then the generated file contains the first fake function definition" do + expect(mock_source).to include( + "DEFINE_FAKE_VALUE_FUNC1(int, a_function, char *);" + ) + end + it "then the generated file contains the second fake function definition" do + expect(mock_source).to include( + "DEFINE_FAKE_VOID_FUNC0(another_function);" + ) + end + it "then the generated file contains the third fake function definition" do + expect(mock_source).to include( + "DEFINE_FAKE_VALUE_FUNC2(bool, three, float, int);" + ) + end + it "then the init function resets all of the fakes" do + expect(mock_source).to include( + "void mock_display_Init(void)\n" + + "{\n" + + " FFF_RESET_HISTORY();\n" + + " RESET_FAKE(a_function)\n" + + " RESET_FAKE(another_function)\n" + + " RESET_FAKE(three)\n" + + "}" + ) + end + end + + context "when there is a void function with variable arguments and " + + "additional arguments" do + let(:mock_source){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "function_with_var_args", + :return => {:type => "void"}, + :var_arg => "...", + :args => [{:type => 'char *'}, {:type => 'int'}] + }] + FffMockGenerator.create_mock_source("mock_display", parsed_header) + } + it "then the generated file contains the vararg definition" do + expect(mock_source).to include( + "DEFINE_FAKE_VOID_FUNC3_VARARG(function_with_var_args, char *, int, ...)" + ) + end + end + + context "when there is a function with a pointer to a const value" do + let(:mock_source){ + parsed_header = {} + parsed_header[:functions] = [{ + :name => "const_test_function", + :return => {:type => "void"}, + :args => [{:type => "char *", :name => "a", :ptr? => false, :const? => true}, + {:type => "char *", :name => "b", :ptr? => false, :const? => false}] + }] + FffMockGenerator.create_mock_source("mock_display", parsed_header) + } + it "then the generated file contains the correct const argument in the declaration" do + expect(mock_source).to include( + "DEFINE_FAKE_VOID_FUNC2(const_test_function, const char *, char *)" + ) + end + end + + context "when there are pre-includes" do + let(:mock_source) { + parsed_source = {} + FffMockGenerator.create_mock_source("mock_display", parsed_source, + [%{"another_header.h"}]) + } + it "then they are included before the other files" do + expect(mock_source).to include( + %{#include "another_header.h"\n} + + %{#include <string.h>} + ) + end + end + + context "when there are post-includes" do + let(:mock_source) { + parsed_source = {} + FffMockGenerator.create_mock_source("mock_display", parsed_source, + nil, [%{"another_header.h"}]) + } + it "then they are included before the other files" do + expect(mock_source).to include( + %{#include "mock_display.h"\n} + + %{#include "another_header.h"\n} + ) + end + end +end \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb new file mode 100644 index 0000000..cda2784 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb @@ -0,0 +1,51 @@ +# Create a CMock-style parsed header hash. This the type of hash created by +# CMock when parsing header files for automock generation. It contains all of +# includes, typedefs and functions (with return types and arguments) parsed from +# the header file. +def create_cmock_style_parsed_header(functions, typedefs = nil) + parsed_header = { + :includes => nil, + :functions => [], + :typedefs => [] + } + + # Add the typedefs. + if typedefs + typedefs.each do |typedef| + parsed_header[:typedefs] << typedef + end + end + + # Add the functions. + if functions + functions.each do |function| + # Build the array of arguments. + args = [] + if function.key?(:args) + function[:args].each do |arg| + args << { + :type => arg + } + end + end + parsed_header[:functions] << { + :name => function[:name], + :modifier => "", + :return => { + :type => function[:return_type], + :name => "cmock_to_return", + :ptr? => false, + :const? => false, + :str => "void cmock_to_return", + :void? => true + }, + :var_arg => nil, + :args_string => "void", + :args => args, + :args_call => "", + :contains_ptr? => false + } + end + end + parsed_header +end \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb new file mode 100644 index 0000000..25dc80a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # These two settings work together to allow you to limit a spec run + # to individual examples or groups you care about by tagging them with + # `:focus` metadata. When nothing is tagged with `:focus`, all examples + # get run. + config.filter_run :focus + config.run_all_when_everything_filtered = true + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/CAN_App/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h b/CAN_App/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h new file mode 100644 index 0000000..de3db44 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h @@ -0,0 +1,33 @@ +#ifndef fff_unity_helper_H +#define fff_unity_helper_H + +/* + FFF helper macros for Unity. +*/ + +/* + Fail if the function was not called the expected number of times. +*/ +#define TEST_ASSERT_CALLED_TIMES(times_, function_) \ + TEST_ASSERT_EQUAL_MESSAGE(times_, \ + function_ ## _fake.call_count, \ + "Function " #function_ " called the incorrect number of times.") +/* + Fail if the function was not called exactly once. +*/ +#define TEST_ASSERT_CALLED(function_) TEST_ASSERT_CALLED_TIMES(1, function_) + +/* + Fail if the function was called 1 or more times. +*/ +#define TEST_ASSERT_NOT_CALLED(function_) TEST_ASSERT_CALLED_TIMES(0, function_) + +/* + Fail if the function was not called in this particular order. +*/ +#define TEST_ASSERT_CALLED_IN_ORDER(order_, function_) \ + TEST_ASSERT_EQUAL_PTR_MESSAGE((void *) function_, \ + fff.call_history[order_], \ + "Function " #function_ " not called in order " #order_ ) + +#endif \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/gcov/README.md b/CAN_App/vendor/ceedling/plugins/gcov/README.md new file mode 100644 index 0000000..b144e3b --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/README.md @@ -0,0 +1,433 @@ +ceedling-gcov +============= + +# Plugin Overview + +Plugin for integrating GNU GCov code coverage tool into Ceedling projects. +Currently only designed for the gcov command (like LCOV for example). In the +future we could configure this to work with other code coverage tools. + +This plugin currently uses [gcovr](https://www.gcovr.com/) and / or +[ReportGenerator](https://danielpalme.github.io/ReportGenerator/) +as utilities to generate HTML, XML, JSON, or Text reports. The normal gcov +plugin _must_ be run first for these reports to generate. + +## Installation + +gcovr can be installed via pip like so: + +```sh +pip install gcovr +``` + +ReportGenerator can be installed via .NET Core like so: + +```sh +dotnet tool install -g dotnet-reportgenerator-globaltool +``` + +It is not required to install both `gcovr` and `ReportGenerator`. Either utility +may be installed to create reports. + +## Configuration + +The gcov plugin supports configuration options via your `project.yml` provided +by Ceedling. + +### Utilities + +Gcovr and / or ReportGenerator may be enabled to create coverage reports. + +```yaml +:gcov: + :utilities: + - gcovr # Use gcovr to create the specified reports (default). + - ReportGenerator # Use ReportGenerator to create the specified reports. +``` + +### Reports + +Various reports are available and may be enabled with the following +configuration item. See the specific report sections in this README +for additional options and information. All generated reports will be found in `build/artifacts/gcov`. + +```yaml +:gcov: + # Specify one or more reports to generate. + # Defaults to HtmlBasic. + :reports: + # Make an HTML summary report. + # Supported utilities: gcovr, ReportGenerator + - HtmlBasic + + # Make an HTML report with line by line coverage of each source file. + # Supported utilities: gcovr, ReportGenerator + - HtmlDetailed + + # Make a Text report, which may be output to the console with gcovr or a file in both gcovr and ReportGenerator. + # Supported utilities: gcovr, ReportGenerator + - Text + + # Make a Cobertura XML report. + # Supported utilities: gcovr, ReportGenerator + - Cobertura + + # Make a SonarQube XML report. + # Supported utilities: gcovr, ReportGenerator + - SonarQube + + # Make a JSON report. + # Supported utilities: gcovr + - JSON + + # Make a detailed HTML report with CSS and JavaScript included in every HTML page. Useful for build servers. + # Supported utilities: ReportGenerator + - HtmlInline + + # Make a detailed HTML report with a light theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzure + + # Make a detailed HTML report with a dark theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzureDark + + # Make a single HTML file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - HtmlChart + + # Make a detailed HTML report in a single file. + # Supported utilities: ReportGenerator + - MHtml + + # Make SVG and PNG files that show line and / or branch coverage information. + # Supported utilities: ReportGenerator + - Badges + + # Make a single CSV file containing coverage information per file. + # Supported utilities: ReportGenerator + - CsvSummary + + # Make a single TEX file containing a summary for all files and detailed reports for each files. + # Supported utilities: ReportGenerator + - Latex + + # Make a single TEX file containing a summary for all files. + # Supported utilities: ReportGenerator + - LatexSummary + + # Make a single PNG file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - PngChart + + # Command line output interpreted by TeamCity. + # Supported utilities: ReportGenerator + - TeamCitySummary + + # Make a text file in lcov format. + # Supported utilities: ReportGenerator + - lcov + + # Make a XML file containing a summary for all classes and detailed reports for each class. + # Supported utilities: ReportGenerator + - Xml + + # Make a single XML file containing a summary for all files. + # Supported utilities: ReportGenerator + - XmlSummary +``` + +### Gcovr HTML Reports + +Generation of Gcovr HTML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable HTML reports or set to 'false' to disable. + # Defaults to enabled. (gcovr --html) + # Deprecated - See the :reports: configuration option. + :html_report: [true|false] + + # Gcovr supports generating two types of HTML reports. Use 'basic' to create + # an HTML report with only the overall file information. Use 'detailed' to create + # an HTML report with line by line coverage of each source file. + # Defaults to 'basic'. Set to 'detailed' for (gcovr --html-details). + # Deprecated - See the :reports: configuration option. + :html_report_type: [basic|detailed] + + + :gcovr: + # HTML report filename. + :html_artifact_filename: <output> + + # Use 'title' as title for the HTML report. + # Default is 'Head'. (gcovr --html-title) + :html_title: <title> + + # If the coverage is below MEDIUM, the value is marked as low coverage in the HTML report. + # MEDIUM has to be lower than or equal to value of html_high_threshold. + # If MEDIUM is equal to value of html_high_threshold the report has only high and low coverage. + # Default is 75.0. (gcovr --html-medium-threshold) + :html_medium_threshold: 75 + + # If the coverage is below HIGH, the value is marked as medium coverage in the HTML report. + # HIGH has to be greater than or equal to value of html_medium_threshold. + # If HIGH is equal to value of html_medium_threshold the report has only high and low coverage. + # Default is 90.0. (gcovr -html-high-threshold) + :html_high_threshold: 90 + + # Set to 'true' to use absolute paths to link the 'detailed' reports. + # Defaults to relative links. (gcovr --html-absolute-paths) + :html_absolute_paths: [true|false] + + # Override the declared HTML report encoding. Defaults to UTF-8. (gcovr --html-encoding) + :html_encoding: <html_encoding> +``` + +### Cobertura XML Reports + +Generation of Cobertura XML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable Cobertura XML reports or set to 'false' to disable. + # Defaults to disabled. (gcovr --xml) + # Deprecated - See the :reports: configuration option. + :xml_report: [true|false] + + + :gcovr: + # Set to 'true' to pretty-print the Cobertura XML report, otherwise set to 'false'. + # Defaults to disabled. (gcovr --xml-pretty) + :xml_pretty: [true|false] + :cobertura_pretty: [true|false] + + # Cobertura XML report filename. + :xml_artifact_filename: <output> + :cobertura_artifact_filename: <output> +``` + +### SonarQube XML Reports + +Generation of SonarQube XML reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # SonarQube XML report filename. + :sonarqube_artifact_filename: <output> +``` + +### JSON Reports + +Generation of JSON reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # Set to 'true' to pretty-print the JSON report, otherwise set 'false'. + # Defaults to disabled. (gcovr --json-pretty) + :json_pretty: [true|false] + + # JSON report filename. + :json_artifact_filename: <output> +``` + +### Text Reports + +Generation of text reports may be modified with the following configuration items. +Text reports may be printed to the console or output to a file. + +```yaml +:gcov: + :gcovr: + # Text report filename. + # The text report is printed to the console when no filename is provided. + :text_artifact_filename: <output> +``` + +### Common Report Options + +There are a number of options to control which files are considered part of +the coverage report. Most often, we only care about coverage on our source code, and not +on tests or automatically generated mocks, runners, etc. However, there are times +where this isn't true... or there are times where we've moved ceedling's directory +structure so that the project file isn't at the root of the project anymore. In these +cases, you may need to tweak `report_include`, `report_exclude`, and `exclude_directories`. + +One important note about `report_root`: gcovr will take only a single root folder, unlike +Ceedling's ability to take as many as you like. So you will need to choose a folder which is +a superset of ALL the folders you want, and then use the include or exclude options to set up +patterns of files to pay attention to or ignore. It's not ideal, but it works. + +Finally, there are a number of settings which can be specified to adjust the +default behaviors of gcovr: + +```yaml +:gcov: + :gcovr: + # The root directory of your source files. Defaults to ".", the current directory. + # File names are reported relative to this root. The report_root is the default report_include. + :report_root: "." + + # Load the specified configuration file. + # Defaults to gcovr.cfg in the report_root directory. (gcovr --config) + :config_file: <config_file> + + # Exit with a status of 2 if the total line coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_branch' option. (gcovr --fail-under-line) + :fail_under_line: 30 + + # Exit with a status of 4 if the total branch coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_line' option. (gcovr --fail-under-branch) + :fail_under_branch: 30 + + # Select the source file encoding. + # Defaults to the system default encoding (UTF-8). (gcovr --source-encoding) + :source_encoding: <source_encoding> + + # Report the branch coverage instead of the line coverage. For text report only. (gcovr --branches). + :branches: [true|false] + + # Sort entries by increasing number of uncovered lines. + # For text and HTML report. (gcovr --sort-uncovered) + :sort_uncovered: [true|false] + + # Sort entries by increasing percentage of uncovered lines. + # For text and HTML report. (gcovr --sort-percentage) + :sort_percentage: [true|false] + + # Print a small report to stdout with line & branch percentage coverage. + # This is in addition to other reports. (gcovr --print-summary). + :print_summary: [true|false] + + # Keep only source files that match this filter. (gcovr --filter). + :report_include: "^src" + + # Exclude source files that match this filter. (gcovr --exclude). + :report_exclude: "^vendor.*|^build.*|^test.*|^lib.*" + + # Keep only gcov data files that match this filter. (gcovr --gcov-filter). + :gcov_filter: <gcov_filter> + + # Exclude gcov data files that match this filter. (gcovr --gcov-exclude). + :gcov_exclude: <gcov_exclude> + + # Exclude directories that match this regex while searching + # raw coverage files. (gcovr --exclude-directories). + :exclude_directories: <exclude_dirs> + + # Use a particular gcov executable. (gcovr --gcov-executable). + :gcov_executable: <gcov_cmd> + + # Exclude branch coverage from lines without useful + # source code. (gcovr --exclude-unreachable-branches). + :exclude_unreachable_branches: [true|false] + + # For branch coverage, exclude branches that the compiler + # generates for exception handling. (gcovr --exclude-throw-branches). + :exclude_throw_branches: [true|false] + + # Use existing gcov files for analysis. Default: False. (gcovr --use-gcov-files) + :use_gcov_files: [true|false] + + # Skip lines with parse errors in GCOV files instead of + # exiting with an error. (gcovr --gcov-ignore-parse-errors). + :gcov_ignore_parse_errors: [true|false] + + # Override normal working directory detection. (gcovr --object-directory) + :object_directory: <objdir> + + # Keep gcov files after processing. (gcovr --keep). + :keep: [true|false] + + # Delete gcda files after processing. (gcovr --delete). + :delete: [true|false] + + # Set the number of threads to use in parallel. (gcovr -j). + :num_parallel_threads: <num_threads> + + # When scanning the code coverage, if any files are found that do not have + # associated coverage data, the command will abort with an error message. + :abort_on_uncovered: true + + # When using the ``abort_on_uncovered`` option, the files in this list will not + # trigger a failure. + # Ceedling globs described in the Ceedling packet ``Path`` section can be used + # when directories are placed on the list. Globs are limited to matching directories + # and not files. + :uncovered_ignore_list: [] +``` + +### ReportGenerator Configuration + +The ReportGenerator utility may be configured with the following configuration items. +All generated reports may be found in `build/artifacts/gcov/ReportGenerator`. + +```yaml +:gcov: + :report_generator: + # Optional directory for storing persistent coverage information. + # Can be used in future reports to show coverage evolution. + :history_directory: <history_directory> + + # Optional plugin files for custom reports or custom history storage (separated by semicolon). + :plugins: CustomReports.dll + + # Optional list of assemblies that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :assembly_filters: "+Included;-Excluded" + + # Optional list of classes that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :class_filters: "+Included;-Excluded" + + # Optional list of files that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :file_filters: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*" + + # The verbosity level of the log messages. + # Values: Verbose, Info, Warning, Error, Off + :verbosity: Warning + + # Optional tag or build version. + :tag: <tag> + + # Optional list of one or more regular expressions to exclude gcov notes files that match these filters. + :gcov_exclude: + - <exclude_regex1> + - <exclude_regex2> + + # Optionally use a particular gcov executable. Defaults to gcov. + :gcov_executable: <gcov_cmd> + + # Optionally set the number of threads to use in parallel. Defaults to 1. + :num_parallel_threads: <num_threads> + + # Optional list of one or more command line arguments to pass to Report Generator. + # Useful for configuring Risk Hotspots and Other Settings. + # https://github.com/danielpalme/ReportGenerator/wiki/Settings + :custom_args: + - <custom_arg1> + - <custom_arg2> +``` + +## Example Usage + +```sh +ceedling gcov:all utils:gcov +``` + +## To-Do list + +- Generate overall report (combined statistics from all files with coverage) + +## Citations + +Most of the comment text which describes the options was taken from the +[Gcovr User Guide](https://www.gcovr.com/en/stable/guide.html) and the +[ReportGenerator Wiki](https://github.com/danielpalme/ReportGenerator/wiki). +The text is repeated here to provide the most accurate option functionality. diff --git a/CAN_App/vendor/ceedling/plugins/gcov/assets/template.erb b/CAN_App/vendor/ceedling/plugins/gcov/assets/template.erb new file mode 100644 index 0000000..5e5a174 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/assets/template.erb @@ -0,0 +1,15 @@ +% function_string = hash[:coverage][:functions].to_s +% branch_string = hash[:coverage][:branches].to_s +% format_string = "%#{[function_string.length, branch_string.length].max}i" +<%=@ceedling[:plugin_reportinator].generate_banner("#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY")%> +% if (!hash[:coverage][:functions].nil?) +FUNCTIONS: <%=sprintf(format_string, hash[:coverage][:functions])%>% +% else +FUNCTIONS: none +% end +% if (!hash[:coverage][:branches].nil?) +BRANCHES: <%=sprintf(format_string, hash[:coverage][:branches])%>% +% else +BRANCHES: none +% end + diff --git a/CAN_App/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb b/CAN_App/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb new file mode 100644 index 0000000..e3ce340 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb @@ -0,0 +1,118 @@ + +DEFAULT_GCOV_COMPILER_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_gcov_compiler'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-g".freeze, + "-fprofile-arcs".freeze, + "-ftest-coverage".freeze, + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + "-DGCOV_COMPILER".freeze, + "-DCODE_COVERAGE".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + "-c \"${1}\"".freeze, + "-o \"${2}\"".freeze + ].freeze + } + + +DEFAULT_GCOV_LINKER_TOOL = { + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], + :name => 'default_gcov_linker'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-g".freeze, + "-fprofile-arcs".freeze, + "-ftest-coverage".freeze, + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, + "\"${1}\"".freeze, + "-o \"${2}\"".freeze, + "${4}".freeze, + "${5}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split + ].freeze + } + +DEFAULT_GCOV_FIXTURE_TOOL = { + :executable => '${1}'.freeze, + :name => 'default_gcov_fixture'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [].freeze + } + +DEFAULT_GCOV_REPORT_TOOL = { + :executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0], + :name => 'default_gcov_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-n".freeze, + "-p".freeze, + "-b".freeze, + {"-o \"$\"" => 'GCOV_BUILD_OUTPUT_PATH'}.freeze, + "\"${1}\"".freeze + ].freeze + } + +DEFAULT_GCOV_GCOV_POST_REPORT_TOOL = { + :executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0], + :name => 'default_gcov_gcov_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "-b".freeze, + "-c".freeze, + "-r".freeze, + "-x".freeze, + "${1}".freeze + ].freeze + } + +DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL = { + :executable => 'gcovr'.freeze, + :name => 'default_gcov_gcovr_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "${1}".freeze + ].freeze + } + +DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT = { + :executable => 'reportgenerator'.freeze, + :name => 'default_gcov_reportgenerator_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "${1}".freeze + ].freeze + } + +def get_default_config + return :tools => { + :gcov_compiler => DEFAULT_GCOV_COMPILER_TOOL, + :gcov_linker => DEFAULT_GCOV_LINKER_TOOL, + :gcov_fixture => DEFAULT_GCOV_FIXTURE_TOOL, + :gcov_report => DEFAULT_GCOV_REPORT_TOOL, + :gcov_gcov_post_report => DEFAULT_GCOV_GCOV_POST_REPORT_TOOL, + :gcov_gcovr_post_report => DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL, + :gcov_reportgenerator_post_report => DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT + } +end diff --git a/CAN_App/vendor/ceedling/plugins/gcov/gcov.rake b/CAN_App/vendor/ceedling/plugins/gcov/gcov.rake new file mode 100644 index 0000000..1467564 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/gcov.rake @@ -0,0 +1,209 @@ +require 'reportgenerator_reportinator' +require 'gcovr_reportinator' + +directory(GCOV_BUILD_OUTPUT_PATH) +directory(GCOV_RESULTS_PATH) +directory(GCOV_ARTIFACTS_PATH) +directory(GCOV_DEPENDENCIES_PATH) + +CLEAN.include(File.join(GCOV_BUILD_OUTPUT_PATH, '*')) +CLEAN.include(File.join(GCOV_RESULTS_PATH, '*')) +CLEAN.include(File.join(GCOV_ARTIFACTS_PATH, '*')) +CLEAN.include(File.join(GCOV_DEPENDENCIES_PATH, '*')) + +CLOBBER.include(File.join(GCOV_BUILD_PATH, '**/*')) + +rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |object| + + if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX})|(#{VENDORS_FILES.map{|source| '\b' + source + '\b'}.join('|')})/ + @ceedling[:generator].generate_object_file( + TOOLS_GCOV_COMPILER, + OPERATION_COMPILE_SYM, + GCOV_SYM, + object.source, + object.name, + @ceedling[:file_path_utils].form_test_build_list_filepath(object.name) + ) + else + @ceedling[GCOV_SYM].generate_coverage_object_file(object.source, object.name) + end +end + +rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_EXECUTABLE}$/) do |bin_file| + lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() + @ceedling[:generator].generate_executable_file( + TOOLS_GCOV_LINKER, + GCOV_SYM, + bin_file.prerequisites, + bin_file.name, + @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name), + lib_args, + lib_paths + ) +end + +rule(/#{GCOV_RESULTS_PATH}\/#{'.+\\' + EXTENSION_TESTPASS}$/ => [ + proc do |task_name| + @ceedling[:file_path_utils].form_test_executable_filepath(task_name) + end + ]) do |test_result| + @ceedling[:generator].generate_test_results(TOOLS_GCOV_FIXTURE, GCOV_SYM, test_result.source, test_result.name) +end + +rule(/#{GCOV_DEPENDENCIES_PATH}\/#{'.+\\' + EXTENSION_DEPENDENCIES}$/ => [ + proc do |task_name| + @ceedling[:file_finder].find_compilation_input_file(task_name) + end + ]) do |dep| + @ceedling[:generator].generate_dependencies_file( + TOOLS_TEST_DEPENDENCIES_GENERATOR, + GCOV_SYM, + dep.source, + File.join(GCOV_BUILD_OUTPUT_PATH, File.basename(dep.source).ext(EXTENSION_OBJECT)), + dep.name + ) +end + +task directories: [GCOV_BUILD_OUTPUT_PATH, GCOV_RESULTS_PATH, GCOV_DEPENDENCIES_PATH, GCOV_ARTIFACTS_PATH] + +namespace GCOV_SYM do + task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") + + desc 'Run code coverage for all tests' + task all: [:test_deps] do + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM) + @ceedling[:configurator].restore_config + end + + desc 'Run single test w/ coverage ([*] real test or source file name, no path).' + task :* do + message = "\nOops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " \ + "Use a real test or source file name (no path) in place of the wildcard.\n" \ + "Example: rake #{GCOV_ROOT_NAME}:foo.c\n\n" + + @ceedling[:streaminator].stdout_puts(message) + end + + desc 'Run tests by matching regular expression pattern.' + task :pattern, [:regex] => [:test_deps] do |_t, args| + matches = [] + + COLLECTION_ALL_TESTS.each do |test| + matches << test if test =~ /#{args.regex}/ + end + + if !matches.empty? + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, force_run: false) + @ceedling[:configurator].restore_config + else + @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.") + end + end + + desc 'Run tests whose test path contains [dir] or [dir] substring.' + task :path, [:dir] => [:test_deps] do |_t, args| + matches = [] + + COLLECTION_ALL_TESTS.each do |test| + matches << test if File.dirname(test).include?(args.dir.tr('\\', '/')) + end + + if !matches.empty? + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, force_run: false) + @ceedling[:configurator].restore_config + else + @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.") + end + end + + desc 'Run code coverage for changed files' + task delta: [:test_deps] do + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, force_run: false) + @ceedling[:configurator].restore_config + end + + # use a rule to increase efficiency for large projects + # gcov test tasks by regex + rule(/^#{GCOV_TASK_ROOT}\S+$/ => [ + proc do |task_name| + test = task_name.sub(/#{GCOV_TASK_ROOT}/, '') + test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" unless test.start_with?(PROJECT_TEST_FILE_PREFIX) + @ceedling[:file_finder].find_test_from_file_path(test) + end + ]) do |test| + @ceedling[:rake_wrapper][:test_deps].invoke + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM) + @ceedling[:configurator].restore_config + end +end + +if PROJECT_USE_DEEP_DEPENDENCIES + namespace REFRESH_SYM do + task GCOV_SYM do + @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) + @ceedling[:test_invoker].refresh_deep_dependencies + @ceedling[:configurator].restore_config + end + end +end + +namespace UTILS_SYM do + # Report Creation Utilities + UTILITY_NAME_GCOVR = "gcovr" + UTILITY_NAME_REPORT_GENERATOR = "ReportGenerator" + UTILITY_NAMES = [UTILITY_NAME_GCOVR, UTILITY_NAME_REPORT_GENERATOR] + + # Returns true is the given utility is enabled, otherwise returns false. + def is_utility_enabled(opts, utility_name) + return !(opts.nil?) && !(opts[:gcov_utilities].nil?) && (opts[:gcov_utilities].map(&:upcase).include? utility_name.upcase) + end + + + desc "Create gcov code coverage html/xml/json/text report(s). (Note: Must run 'ceedling gcov' first)." + task GCOV_SYM do + # Get the gcov options from project.yml. + opts = @ceedling[:configurator].project_config_hash + + # Create the artifacts output directory. + if !File.directory? GCOV_ARTIFACTS_PATH + FileUtils.mkdir_p GCOV_ARTIFACTS_PATH + end + + # Remove unsupported reporting utilities. + if !(opts[:gcov_utilities].nil?) + opts[:gcov_utilities].reject! { |item| !(UTILITY_NAMES.map(&:upcase).include? item.upcase) } + end + + # Default to gcovr when no reporting utilities are specified. + if opts[:gcov_utilities].nil? || opts[:gcov_utilities].empty? + opts[:gcov_utilities] = [UTILITY_NAME_GCOVR] + end + + if opts[:gcov_reports].nil? + opts[:gcov_reports] = [] + end + + gcovr_reportinator = GcovrReportinator.new(@ceedling) + gcovr_reportinator.support_deprecated_options(opts) + + if is_utility_enabled(opts, UTILITY_NAME_GCOVR) + gcovr_reportinator.make_reports(opts) + end + + if is_utility_enabled(opts, UTILITY_NAME_REPORT_GENERATOR) + reportgenerator_reportinator = ReportGeneratorReportinator.new(@ceedling) + reportgenerator_reportinator.make_reports(opts) + end + + end +end diff --git a/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov.rb b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov.rb new file mode 100644 index 0000000..30c6326 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov.rb @@ -0,0 +1,136 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'gcov_constants' + +class Gcov < Plugin + attr_reader :config + + def setup + @result_list = [] + + @config = { + project_test_build_output_path: GCOV_BUILD_OUTPUT_PATH, + project_test_build_output_c_path: GCOV_BUILD_OUTPUT_PATH, + project_test_results_path: GCOV_RESULTS_PATH, + project_test_dependencies_path: GCOV_DEPENDENCIES_PATH, + defines_test: DEFINES_TEST + ['CODE_COVERAGE'], + gcov_html_report_filter: GCOV_FILTER_EXCLUDE + } + + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + @coverage_template_all = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb')) + end + + def generate_coverage_object_file(source, object) + lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() + compile_command = + @ceedling[:tool_executor].build_command_line( + TOOLS_GCOV_COMPILER, + @ceedling[:flaginator].flag_down(OPERATION_COMPILE_SYM, GCOV_SYM, source), + source, + object, + @ceedling[:file_path_utils].form_test_build_list_filepath(object), + lib_args + ) + @ceedling[:streaminator].stdout_puts("Compiling #{File.basename(source)} with coverage...") + @ceedling[:tool_executor].exec(compile_command[:line], compile_command[:options]) + end + + def post_test_fixture_execute(arg_hash) + result_file = arg_hash[:result_file] + + if (result_file =~ /#{GCOV_RESULTS_PATH}/) && !@result_list.include?(result_file) + @result_list << arg_hash[:result_file] + end + end + + def post_build + return unless @ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/) + + # test results + results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list) + hash = { + header: GCOV_ROOT_NAME.upcase, + results: results + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) do + message = '' + message = 'Unit test failures.' if results[:counts][:failed] > 0 + message + end + + report_per_file_coverage_results(@ceedling[:test_invoker].sources) + end + + def summary + result_list = @ceedling[:file_path_utils].form_pass_results_filelist(GCOV_RESULTS_PATH, COLLECTION_ALL_TESTS) + + # test results + # get test results for only those tests in our configuration and of those only tests with results on disk + hash = { + header: GCOV_ROOT_NAME.upcase, + results: @ceedling[:plugin_reportinator].assemble_test_results(result_list, boom: false) + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + end + + private ################################### + + def report_per_file_coverage_results(sources) + banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" + @ceedling[:streaminator].stdout_puts "\n" + banner + + coverage_sources = @ceedling[:project_config_manager].filter_internal_sources(sources) + coverage_sources.each do |source| + basename = File.basename(source) + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, [], [basename]) + shell_results = @ceedling[:tool_executor].exec(command[:line], command[:options]) + coverage_results = shell_results[:output] + + if coverage_results.strip =~ /(File\s+'#{Regexp.escape(source)}'.+$)/m + report = Regexp.last_match(1).lines.to_a[1..-1].map { |line| basename + ' ' + line }.join('') + @ceedling[:streaminator].stdout_puts(report + "\n\n") + end + end + + ignore_path_list = @ceedling[:file_system_utils].collect_paths(@ceedling[:configurator].project_config_hash[:gcov_uncovered_ignore_list] || []) + ignore_uncovered_list = @ceedling[:file_wrapper].instantiate_file_list + ignore_path_list.each do |path| + if File.exists?(path) and not File.directory?(path) + ignore_uncovered_list.include(path) + else + ignore_uncovered_list.include(File.join(path, "*#{EXTENSION_SOURCE}")) + end + end + + found_uncovered = false + COLLECTION_ALL_SOURCE.each do |source| + unless coverage_sources.include?(source) + v = Verbosity::DEBUG + msg = "Could not find coverage results for " + source + if ignore_uncovered_list.include?(source) + msg += " [IGNORED]" + else + found_uncovered = true + v = Verbosity::NORMAL + end + msg += "\n" + @ceedling[:streaminator].stdout_puts(msg, v) + end + end + if found_uncovered + if @ceedling[:configurator].project_config_hash[:gcov_abort_on_uncovered] + @ceedling[:streaminator].stderr_puts("There were files with no coverage results: aborting.\n") + exit(-1) + end + end + end +end + +# end blocks always executed following rake run +END { + # cache our input configurations to use in comparison upon next execution + @ceedling[:cacheinator].cache_test_config(@ceedling[:setupinator].config_hash) if @ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/) +} diff --git a/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb new file mode 100644 index 0000000..74c9bbd --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb @@ -0,0 +1,48 @@ + +GCOV_ROOT_NAME = 'gcov'.freeze +GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':' +GCOV_SYM = GCOV_ROOT_NAME.to_sym + +GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME) +GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out") +GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results") +GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies") +GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME) +GCOV_REPORT_GENERATOR_PATH = File.join(GCOV_ARTIFACTS_PATH, "ReportGenerator") + +GCOV_ARTIFACTS_FILE_HTML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html") +GCOV_ARTIFACTS_FILE_COBERTURA = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageCobertura.xml") +GCOV_ARTIFACTS_FILE_SONARQUBE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageSonarQube.xml") +GCOV_ARTIFACTS_FILE_JSON = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverage.json") + +GCOV_FILTER_EXCLUDE_PATHS = ['vendor', 'build', 'test', 'lib'] + +# gcovr supports regular expressions. +GCOV_FILTER_EXCLUDE = GCOV_FILTER_EXCLUDE_PATHS.map{|path| '^'.concat(*path).concat('.*')}.join('|') + +# ReportGenerator supports text with wildcard characters. +GCOV_REPORT_GENERATOR_FILE_FILTERS = GCOV_FILTER_EXCLUDE_PATHS.map{|path| File.join('-.', *path, '*')}.join(';') + +# Report Types +class ReportTypes + HTML_BASIC = "HtmlBasic" + HTML_DETAILED = "HtmlDetailed" + HTML_CHART = "HtmlChart" + HTML_INLINE = "HtmlInline" + HTML_INLINE_AZURE = "HtmlInlineAzure" + HTML_INLINE_AZURE_DARK = "HtmlInlineAzureDark" + MHTML = "MHtml" + TEXT = "Text" + COBERTURA = "Cobertura" + SONARQUBE = "SonarQube" + JSON = "JSON" + BADGES = "Badges" + CSV_SUMMARY = "CsvSummary" + LATEX = "Latex" + LATEX_SUMMARY = "LatexSummary" + PNG_CHART = "PngChart" + TEAM_CITY_SUMMARY = "TeamCitySummary" + LCOV = "lcov" + XML = "Xml" + XML_SUMMARY = "XmlSummary" +end diff --git a/CAN_App/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb new file mode 100644 index 0000000..5317c5d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb @@ -0,0 +1,331 @@ +require 'reportinator_helper' + +class GcovrReportinator + + def initialize(system_objects) + @ceedling = system_objects + @reportinator_helper = ReportinatorHelper.new + end + + + # Generate the gcovr report(s) specified in the options. + def make_reports(opts) + # Get the gcovr version number. + gcovr_version_info = get_gcovr_version() + + # Build the common gcovr arguments. + args_common = args_builder_common(opts) + + if ((gcovr_version_info[0] == 4) && (gcovr_version_info[1] >= 2)) || (gcovr_version_info[0] > 4) + # gcovr version 4.2 and later supports generating multiple reports with a single call. + args = args_common + args += args_builder_cobertura(opts, false) + args += args_builder_sonarqube(opts, false) + args += args_builder_json(opts, true) + # As of gcovr version 4.2, the --html argument must appear last. + args += args_builder_html(opts, false) + + print "Creating gcov results report(s) in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the report(s). + run(args) + else + # gcovr version 4.1 and earlier supports HTML and Cobertura XML reports. + # It does not support SonarQube and JSON reports. + # Reports must also be generated separately. + args_cobertura = args_builder_cobertura(opts, true) + args_html = args_builder_html(opts, true) + + if args_html.length > 0 + print "Creating a gcov HTML report in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the HTML report. + run(args_common + args_html) + end + + if args_cobertura.length > 0 + print "Creating a gcov XML report in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the Cobertura XML report. + run(args_common + args_cobertura) + end + end + + # Determine if the gcovr text report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::TEXT) + make_text_report(opts, args_common) + end + end + + + def support_deprecated_options(opts) + # Support deprecated :html_report: and ":html_report_type: basic" options. + if !is_report_enabled(opts, ReportTypes::HTML_BASIC) && (opts[:gcov_html_report] || (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("basic") == 0)) + opts[:gcov_reports].push(ReportTypes::HTML_BASIC) + end + + # Support deprecated ":html_report_type: detailed" option. + if !is_report_enabled(opts, ReportTypes::HTML_DETAILED) && (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0) + opts[:gcov_reports].push(ReportTypes::HTML_DETAILED) + end + + # Support deprecated :xml_report: option. + if opts[:gcov_xml_report] + opts[:gcov_reports].push(ReportTypes::COBERTURA) + end + + # Default to HTML basic report when no report types are defined. + if opts[:gcov_reports].empty? && opts[:gcov_html_report_type].nil? && opts[:gcov_xml_report].nil? + opts[:gcov_reports] = [ReportTypes::HTML_BASIC] + + puts "In your project.yml, define one or more of the" + puts "following to specify which reports to generate." + puts "For now, creating only an #{ReportTypes::HTML_BASIC} report." + puts "" + puts ":gcov:" + puts " :reports:" + puts " - #{ReportTypes::HTML_BASIC}" + puts " - #{ReportTypes::HTML_DETAILED}" + puts " - #{ReportTypes::TEXT}" + puts " - #{ReportTypes::COBERTURA}" + puts " - #{ReportTypes::SONARQUBE}" + puts " - #{ReportTypes::JSON}" + puts "" + end + end + + + private + + GCOVR_SETTING_PREFIX = "gcov_gcovr" + + # Build the gcovr report generation common arguments. + def args_builder_common(opts) + gcovr_opts = get_opts(opts) + + args = "" + args += "--root \"#{gcovr_opts[:report_root] || '.'}\" " + args += "--config \"#{gcovr_opts[:config_file]}\" " unless gcovr_opts[:config_file].nil? + args += "--filter \"#{gcovr_opts[:report_include]}\" " unless gcovr_opts[:report_include].nil? + args += "--exclude \"#{gcovr_opts[:report_exclude] || GCOV_FILTER_EXCLUDE}\" " + args += "--gcov-filter \"#{gcovr_opts[:gcov_filter]}\" " unless gcovr_opts[:gcov_filter].nil? + args += "--gcov-exclude \"#{gcovr_opts[:gcov_exclude]}\" " unless gcovr_opts[:gcov_exclude].nil? + args += "--exclude-directories \"#{gcovr_opts[:exclude_directories]}\" " unless gcovr_opts[:exclude_directories].nil? + args += "--branches " if gcovr_opts[:branches].nil? || gcovr_opts[:branches] # Defaults to enabled. + args += "--sort-uncovered " if gcovr_opts[:sort_uncovered] + args += "--sort-percentage " if gcovr_opts[:sort_percentage].nil? || gcovr_opts[:sort_percentage] # Defaults to enabled. + args += "--print-summary " if gcovr_opts[:print_summary] + args += "--gcov-executable \"#{gcovr_opts[:gcov_executable]}\" " unless gcovr_opts[:gcov_executable].nil? + args += "--exclude-unreachable-branches " if gcovr_opts[:exclude_unreachable_branches] + args += "--exclude-throw-branches " if gcovr_opts[:exclude_throw_branches] + args += "--use-gcov-files " if gcovr_opts[:use_gcov_files] + args += "--gcov-ignore-parse-errors " if gcovr_opts[:gcov_ignore_parse_errors] + args += "--keep " if gcovr_opts[:keep] + args += "--delete " if gcovr_opts[:delete] + args += "-j #{gcovr_opts[:num_parallel_threads]} " if !(gcovr_opts[:num_parallel_threads].nil?) && (gcovr_opts[:num_parallel_threads].is_a? Integer) + + [:fail_under_line, :fail_under_branch, :source_encoding, :object_directory].each do |opt| + unless gcovr_opts[opt].nil? + + value = gcovr_opts[opt] + if (opt == :fail_under_line) || (opt == :fail_under_branch) + if not value.is_a? Integer + puts "Option value #{opt} has to be an integer" + value = nil + elsif (value < 0) || (value > 100) + puts "Option value #{opt} has to be a percentage from 0 to 100" + value = nil + end + end + args += "--#{opt.to_s.gsub('_','-')} #{value} " unless value.nil? + end + end + + return args + end + + + # Build the gcovr Cobertura XML report generation arguments. + def args_builder_cobertura(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the Cobertura XML report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::COBERTURA) + # Determine the Cobertura XML report file name. + artifacts_file_cobertura = GCOV_ARTIFACTS_FILE_COBERTURA + if !(gcovr_opts[:cobertura_artifact_filename].nil?) + artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:cobertura_artifact_filename]) + elsif !(gcovr_opts[:xml_artifact_filename].nil?) + artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:xml_artifact_filename]) + end + + args += "--xml-pretty " if gcovr_opts[:xml_pretty] || gcovr_opts[:cobertura_pretty] + args += "--xml #{use_output_option ? "--output " : ""} \"#{artifacts_file_cobertura}\" " + end + + return args + end + + + # Build the gcovr SonarQube report generation arguments. + def args_builder_sonarqube(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr SonarQube XML report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::SONARQUBE) + # Determine the SonarQube XML report file name. + artifacts_file_sonarqube = GCOV_ARTIFACTS_FILE_SONARQUBE + if !(gcovr_opts[:sonarqube_artifact_filename].nil?) + artifacts_file_sonarqube = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:sonarqube_artifact_filename]) + end + + args += "--sonarqube #{use_output_option ? "--output " : ""} \"#{artifacts_file_sonarqube}\" " + end + + return args + end + + + # Build the gcovr JSON report generation arguments. + def args_builder_json(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr JSON report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::JSON) + # Determine the JSON report file name. + artifacts_file_json = GCOV_ARTIFACTS_FILE_JSON + if !(gcovr_opts[:json_artifact_filename].nil?) + artifacts_file_json = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:json_artifact_filename]) + end + + args += "--json-pretty " if gcovr_opts[:json_pretty] + # Note: In gcovr 4.2, the JSON report is output only when the --output option is specified. + # Hopefully we can remove --output after a future gcovr release. + args += "--json #{use_output_option ? "--output " : ""} \"#{artifacts_file_json}\" " + end + + return args + end + + + # Build the gcovr HTML report generation arguments. + def args_builder_html(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr HTML report is enabled. Defaults to enabled. + html_enabled = (opts[:gcov_html_report].nil? && opts[:gcov_reports].empty?) || + is_report_enabled(opts, ReportTypes::HTML_BASIC) || + is_report_enabled(opts, ReportTypes::HTML_DETAILED) + + if html_enabled + # Determine the HTML report file name. + artifacts_file_html = GCOV_ARTIFACTS_FILE_HTML + if !(gcovr_opts[:html_artifact_filename].nil?) + artifacts_file_html = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:html_artifact_filename]) + end + + is_html_report_type_detailed = (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0) + + args += "--html-details " if is_html_report_type_detailed || is_report_enabled(opts, ReportTypes::HTML_DETAILED) + args += "--html-title \"#{gcovr_opts[:html_title]}\" " unless gcovr_opts[:html_title].nil? + args += "--html-absolute-paths " if !(gcovr_opts[:html_absolute_paths].nil?) && gcovr_opts[:html_absolute_paths] + args += "--html-encoding \"#{gcovr_opts[:html_encoding]}\" " unless gcovr_opts[:html_encoding].nil? + + [:html_medium_threshold, :html_high_threshold].each do |opt| + args += "--#{opt.to_s.gsub('_','-')} #{gcovr_opts[opt]} " unless gcovr_opts[opt].nil? + end + + # The following option must be appended last for gcovr version <= 4.2 to properly work. + args += "--html #{use_output_option ? "--output " : ""} \"#{artifacts_file_html}\" " + end + + return args + end + + + # Generate a gcovr text report. + def make_text_report(opts, args_common) + gcovr_opts = get_opts(opts) + args_text = "" + message_text = "Creating a gcov text report" + + if !(gcovr_opts[:text_artifact_filename].nil?) + artifacts_file_txt = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:text_artifact_filename]) + args_text += "--output \"#{artifacts_file_txt}\" " + message_text += " in '#{GCOV_ARTIFACTS_PATH}'... " + else + message_text += "... " + end + + print message_text + STDOUT.flush + + # Generate the text report. + run(args_common + args_text) + end + + + # Get the gcovr options from the project options. + def get_opts(opts) + return opts[GCOVR_SETTING_PREFIX.to_sym] || {} + end + + + # Run gcovr with the given arguments. + def run(args) + begin + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], args) + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + @reportinator_helper.print_shell_result(shell_result) + rescue + # handle any unforeseen issues with called tool + exitcode = $?.exitstatus + show_gcovr_message(exitcode) + exit(exitcode) + end + end + + + # Get the gcovr version number as components. + # Returns [major, minor]. + def get_gcovr_version() + version_number_major = 0 + version_number_minor = 0 + + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], "--version") + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + version_number_match_data = shell_result[:output].match(/gcovr ([0-9]+)\.([0-9]+)/) + + if !(version_number_match_data.nil?) && !(version_number_match_data[1].nil?) && !(version_number_match_data[2].nil?) + version_number_major = version_number_match_data[1].to_i + version_number_minor = version_number_match_data[2].to_i + end + + return version_number_major, version_number_minor + end + + + # Show a more human-friendly message on gcovr return code + def show_gcovr_message(exitcode) + if ((exitcode & 2) == 2) + puts "The line coverage is less than the minimum" + end + if ((exitcode & 4) == 4) + puts "The branch coverage is less than the minimum" + end + end + + + # Returns true if the given report type is enabled, otherwise returns false. + def is_report_enabled(opts, report_type) + return !(opts.nil?) && !(opts[:gcov_reports].nil?) && (opts[:gcov_reports].map(&:upcase).include? report_type.upcase) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb b/CAN_App/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb new file mode 100644 index 0000000..d4a885c --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb @@ -0,0 +1,195 @@ +require 'benchmark' +require 'reportinator_helper' + +class ReportGeneratorReportinator + + def initialize(system_objects) + @ceedling = system_objects + @reportinator_helper = ReportinatorHelper.new + end + + + # Generate the ReportGenerator report(s) specified in the options. + def make_reports(opts) + shell_result = nil + total_time = Benchmark.realtime do + rg_opts = get_opts(opts) + + print "Creating gcov results report(s) with ReportGenerator in '#{GCOV_REPORT_GENERATOR_PATH}'... " + STDOUT.flush + + # Cleanup any existing .gcov files to avoid reporting old coverage results. + for gcov_file in Dir.glob("*.gcov") + File.delete(gcov_file) + end + + # Use a custom gcov executable, if specified. + GCOV_TOOL_CONFIG[:executable] = rg_opts[:gcov_executable] unless rg_opts[:gcov_executable].nil? + + # Avoid running gcov on the mock, test, unity, and cexception gcov notes files to save time. + gcno_exclude_str = "#{opts[:cmock_mock_prefix]}.*" + gcno_exclude_str += "|#{opts[:project_test_file_prefix]}.*" + gcno_exclude_str += "|#{VENDORS_FILES.join('|')}" + + # Avoid running gcov on custom specified .gcno files. + if !(rg_opts.nil?) && !(rg_opts[:gcov_exclude].nil?) && !(rg_opts[:gcov_exclude].empty?) + for gcno_exclude_expression in rg_opts[:gcov_exclude] + if !(gcno_exclude_expression.nil?) && !(gcno_exclude_expression.empty?) + # We want to filter .gcno files, not .gcov files. + # We will generate .gcov files from .gcno files. + gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcov") + gcno_exclude_expression = gcno_exclude_expression.chomp(".gcov") + # The .gcno extension will be added later as we create the regex. + gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcno") + gcno_exclude_expression = gcno_exclude_expression.chomp(".gcno") + # Append the custom expression. + gcno_exclude_str += "|#{gcno_exclude_expression}" + end + end + end + + gcno_exclude_regex = /(\/|\\)(#{gcno_exclude_str})\.gcno/ + + # Generate .gcov files by running gcov on gcov notes files (*.gcno). + for gcno_filepath in Dir.glob(File.join(GCOV_BUILD_PATH, "**", "*.gcno")) + match_data = gcno_filepath.match(gcno_exclude_regex) + if match_data.nil? || (match_data[1].nil? && match_data[1].nil?) + # Ensure there is a matching gcov data file. + if File.file?(gcno_filepath.gsub(".gcno", ".gcda")) + run_gcov("\"#{gcno_filepath}\"") + end + end + end + + if Dir.glob("*.gcov").length > 0 + # Build the command line arguments. + args = args_builder(opts) + + # Generate the report(s). + shell_result = run(args) + else + puts "\nWarning: No matching .gcno coverage files found." + end + + # Cleanup .gcov files. + for gcov_file in Dir.glob("*.gcov") + File.delete(gcov_file) + end + end + + if shell_result + shell_result[:time] = total_time + @reportinator_helper.print_shell_result(shell_result) + end + end + + + private + + # A dictionary of report types defined in this plugin to ReportGenerator report types. + REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME = { + ReportTypes::HTML_BASIC.upcase => "HtmlSummary", + ReportTypes::HTML_DETAILED.upcase => "Html", + ReportTypes::HTML_CHART.upcase => "HtmlChart", + ReportTypes::HTML_INLINE.upcase => "HtmlInline", + ReportTypes::HTML_INLINE_AZURE.upcase => "HtmlInline_AzurePipelines", + ReportTypes::HTML_INLINE_AZURE_DARK.upcase => "HtmlInline_AzurePipelines_Dark", + ReportTypes::MHTML.upcase => "MHtml", + ReportTypes::TEXT.upcase => "TextSummary", + ReportTypes::COBERTURA.upcase => "Cobertura", + ReportTypes::SONARQUBE.upcase => "SonarQube", + ReportTypes::BADGES.upcase => "Badges", + ReportTypes::CSV_SUMMARY.upcase => "CsvSummary", + ReportTypes::LATEX.upcase => "Latex", + ReportTypes::LATEX_SUMMARY.upcase => "LatexSummary", + ReportTypes::PNG_CHART.upcase => "PngChart", + ReportTypes::TEAM_CITY_SUMMARY.upcase => "TeamCitySummary", + ReportTypes::LCOV.upcase => "lcov", + ReportTypes::XML.upcase => "Xml", + ReportTypes::XML_SUMMARY.upcase => "XmlSummary", + } + + REPORT_GENERATOR_SETTING_PREFIX = "gcov_report_generator" + + # Deep clone the gcov tool config, so we can modify it locally if specified via options. + GCOV_TOOL_CONFIG = Marshal.load(Marshal.dump(TOOLS_GCOV_GCOV_POST_REPORT)) + + # Build the ReportGenerator arguments. + def args_builder(opts) + rg_opts = get_opts(opts) + report_type_count = 0 + + args = "" + args += "\"-reports:*.gcov\" " + args += "\"-targetdir:\"#{GCOV_REPORT_GENERATOR_PATH}\"\" " + + # Build the report types argument. + if !(opts.nil?) && !(opts[:gcov_reports].nil?) && !(opts[:gcov_reports].empty?) + args += "\"-reporttypes:" + + for report_type in opts[:gcov_reports] + rg_report_type = REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME[report_type.upcase] + if !(rg_report_type.nil?) + args += rg_report_type + ";" + report_type_count = report_type_count + 1 + end + end + + # Removing trailing ';' after the last report type. + args = args.chomp(";") + + # Append a space seperator after the report type. + args += "\" " + end + + # Build the source directories argument. + args += "\"-sourcedirs:.;" + if !(opts[:collection_paths_source].nil?) + args += opts[:collection_paths_source].join(';') + end + args = args.chomp(";") + args += "\" " + + args += "\"-historydir:#{rg_opts[:history_directory]}\" " unless rg_opts[:history_directory].nil? + args += "\"-plugins:#{rg_opts[:plugins]}\" " unless rg_opts[:plugins].nil? + args += "\"-assemblyfilters:#{rg_opts[:assembly_filters]}\" " unless rg_opts[:assembly_filters].nil? + args += "\"-classfilters:#{rg_opts[:class_filters]}\" " unless rg_opts[:class_filters].nil? + file_filters = rg_opts[:file_filters] || @ceedling[:tool_executor_helper].osify_path_separators(GCOV_REPORT_GENERATOR_FILE_FILTERS) + args += "\"-filefilters:#{file_filters}\" " + args += "\"-verbosity:#{rg_opts[:verbosity] || "Warning"}\" " + args += "\"-tag:#{rg_opts[:tag]}\" " unless rg_opts[:tag].nil? + args += "\"settings:createSubdirectoryForAllReportTypes=true\" " unless report_type_count <= 1 + args += "\"settings:numberOfReportsParsedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil? + args += "\"settings:numberOfReportsMergedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil? + + # Append custom arguments. + if !(rg_opts[:custom_args].nil?) && !(rg_opts[:custom_args].empty?) + for custom_arg in rg_opts[:custom_args] + args += "\"#{custom_arg}\" " unless custom_arg.nil? || custom_arg.empty? + end + end + + return args + end + + + # Get the ReportGenerator options from the project options. + def get_opts(opts) + return opts[REPORT_GENERATOR_SETTING_PREFIX.to_sym] || {} + end + + + # Run ReportGenerator with the given arguments. + def run(args) + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORTGENERATOR_POST_REPORT, [], args) + return @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + + + # Run gcov with the given arguments. + def run_gcov(args) + command = @ceedling[:tool_executor].build_command_line(GCOV_TOOL_CONFIG, [], args) + return @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb b/CAN_App/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb new file mode 100644 index 0000000..92617fb --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb @@ -0,0 +1,15 @@ + +class ReportinatorHelper + + # Output the shell result to the console. + def print_shell_result(shell_result) + if !(shell_result.nil?) + puts "Done in %.3f seconds." % shell_result[:time] + + if !(shell_result[:output].nil?) && (shell_result[:output].length > 0) + puts shell_result[:output] + end + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/json_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/json_tests_report/README.md new file mode 100644 index 0000000..8e5a1e5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/json_tests_report/README.md @@ -0,0 +1,36 @@ +json_tests_report +================= + +## Overview + +The json_tests_report plugin creates a JSON file of test results, which is +handy for Continuous Integration build servers or as input into other +reporting tools. The JSON file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `json_tests_report` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - json_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.json`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:json_tests_report: + :artifact_filename: report_spectuluarly.json +``` diff --git a/CAN_App/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb b/CAN_App/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb new file mode 100644 index 0000000..e7023db --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb @@ -0,0 +1,83 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'json' + +class JsonTestsReport < Plugin + def setup + @results_list = {} + @test_counter = 0 + end + + def post_test_fixture_execute(arg_hash) + context = arg_hash[:context] + + @results_list[context] = [] if @results_list[context].nil? + + @results_list[context] << arg_hash[:result_file] + end + + def post_build + @results_list.each_key do |context| + results = @ceedling[:plugin_reportinator].assemble_test_results(@results_list[context]) + + artifact_filename = @ceedling[:configurator].project_config_hash[:json_tests_report_artifact_filename] || 'report.json' + artifact_fullpath = @ceedling[:configurator].project_config_hash[:json_tests_report_path] || File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s) + file_path = File.join(artifact_fullpath, artifact_filename) + + @ceedling[:file_wrapper].open(file_path, 'w') do |f| + @test_counter = 1 + + json = { + "FailedTests" => write_failures(results[:failures]), + "PassedTests" => write_tests(results[:successes]), + "IgnoredTests" => write_tests(results[:ignores]), + "Summary" => write_statistics(results[:counts]) + } + + f << JSON.pretty_generate(json) + end + end + end + + private + + def write_failures(results) + retval = [] + results.each do |result| + result[:collection].each do |item| + @test_counter += 1 + retval << { + "file" => File.join(result[:source][:path], result[:source][:file]), + "test" => item[:test], + "line" => item[:line], + "message" => item[:message] + } + end + end + return retval.uniq + end + + def write_tests(results) + retval = [] + results.each do |result| + result[:collection].each do |item| + @test_counter += 1 + retval << { + "file" => File.join(result[:source][:path], result[:source][:file]), + "test" => item[:test] + } + end + end + return retval + end + + def write_statistics(counts) + return { + "total_tests" => counts[:total], + "passed" => (counts[:total] - counts[:ignored] - counts[:failed]), + "ignored" => counts[:ignored], + "failures" => counts[:failed] + } + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/junit_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/junit_tests_report/README.md new file mode 100644 index 0000000..1259fd6 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/junit_tests_report/README.md @@ -0,0 +1,36 @@ +junit_tests_report +==================== + +## Overview + +The junit_tests_report plugin creates an XML file of test results in JUnit +format, which is handy for Continuous Integration build servers or as input +into other reporting tools. The XML file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `junit_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - junit_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.xml`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:junit_tests_report: + :artifact_filename: report_junit.xml +``` diff --git a/CAN_App/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb b/CAN_App/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb new file mode 100644 index 0000000..3104393 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb @@ -0,0 +1,134 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +class JunitTestsReport < Plugin + + def setup + @results_list = {} + @test_counter = 0 + @time_result = [] + end + + def post_test_fixture_execute(arg_hash) + context = arg_hash[:context] + + @results_list[context] = [] if (@results_list[context].nil?) + + @results_list[context] << arg_hash[:result_file] + @time_result << arg_hash[:shell_result][:time] + + end + + def post_build + @results_list.each_key do |context| + results = @ceedling[:plugin_reportinator].assemble_test_results(@results_list[context]) + + artifact_filename = @ceedling[:configurator].project_config_hash[:junit_tests_report_artifact_filename] || 'report.xml' + artifact_fullpath = @ceedling[:configurator].project_config_hash[:junit_tests_report_path] || File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s) + file_path = File.join(artifact_fullpath, artifact_filename) + + @ceedling[:file_wrapper].open( file_path, 'w' ) do |f| + @testsuite_counter = 0 + @testcase_counter = 0 + suites = reorganise_results( results ) + + write_header( results, f ) + suites.each{|suite| write_suite( suite, f ) } + write_footer( f ) + end + end + end + + private + + def write_header( results, stream ) + results[:counts][:time] = @time_result.reduce(0, :+) + stream.puts '<?xml version="1.0" encoding="utf-8" ?>' + stream.puts('<testsuites tests="%<total>d" failures="%<failed>d" time="%<time>.3f">' % results[:counts]) + end + + def write_footer( stream ) + stream.puts '</testsuites>' + end + + def reorganise_results( results ) + # Reorganise the output by test suite instead of by result + suites = Hash.new{ |h,k| h[k] = {collection: [], total: 0, success: 0, failed: 0, ignored: 0, errors: 0, stdout: []} } + results[:successes].each do |result| + source = result[:source] + name = source[:file].sub(/\..{1,4}$/, "") + suites[name][:collection] += result[:collection].map{|test| test.merge(result: :success)} + suites[name][:total] += result[:collection].length + suites[name][:success] += result[:collection].length + end + results[:failures].each do |result| + source = result[:source] + name = source[:file].sub(/\..{1,4}$/, "") + suites[name][:collection] += result[:collection].map{|test| test.merge(result: :failed)} + suites[name][:total] += result[:collection].length + suites[name][:failed] += result[:collection].length + end + results[:ignores].each do |result| + source = result[:source] + name = source[:file].sub(/\..{1,4}$/, "") + suites[name][:collection] += result[:collection].map{|test| test.merge(result: :ignored)} + suites[name][:total] += result[:collection].length + suites[name][:ignored] += result[:collection].length + end + results[:stdout].each do |result| + source = result[:source] + name = source[:file].sub(/\..{1,4}$/, "") + suites[name][:stdout] += result[:collection] + end + suites.map{|name, data| data.merge(name: name) } + end + + def write_suite( suite, stream ) + suite[:time] = @time_result.shift + stream.puts(' <testsuite name="%<name>s" tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" errors="%<errors>d" time="%<time>.3f">' % suite) + + suite[:collection].each do |test| + write_test( test, stream ) + end + + unless suite[:stdout].empty? + stream.puts(' <system-out>') + suite[:stdout].each do |line| + line.gsub!(/&/, '&') + line.gsub!(/</, '<') + line.gsub!(/>/, '>') + line.gsub!(/"/, '"') + line.gsub!(/'/, ''') + stream.puts(line) + end + stream.puts(' </system-out>') + end + + stream.puts(' </testsuite>') + end + + def write_test( test, stream ) + test[:test].gsub!(/&/, '&') + test[:test].gsub!(/</, '<') + test[:test].gsub!(/>/, '>') + test[:test].gsub!(/"/, '"') + test[:test].gsub!(/'/, ''') + + case test[:result] + when :success + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f"/>' % test) + when :failed + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test) + if test[:message].empty? + stream.puts(' <failure />') + else + stream.puts(' <failure message="%s" />' % test[:message]) + end + stream.puts(' </testcase>') + when :ignored + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test) + stream.puts(' <skipped />') + stream.puts(' </testcase>') + end + end +end diff --git a/CAN_App/vendor/ceedling/plugins/module_generator/README.md b/CAN_App/vendor/ceedling/plugins/module_generator/README.md new file mode 100644 index 0000000..a3c2c7a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/module_generator/README.md @@ -0,0 +1,119 @@ +ceedling-module-generator +========================= + +## Overview + +The module_generator plugin adds a pair of new commands to Ceedling, allowing +you to make or remove modules according to predefined templates. WIth a single call, +Ceedling can generate a source, header, and test file for a new module. If given a +pattern, it can even create a series of submodules to support specific design patterns. +Finally, it can just as easily remove related modules, avoiding the need to delete +each individually. + +Let's say, for example, that you want to create a single module named `MadScience`. + +``` +ceedling module:create[MadScience] +``` + +It says we're speaking to the module plugin, and we want to create a new module. The +name of that module is between the brackets. It will keep this case, unless you have +specified a different default (see configuration). It will create three files: +`MadScience.c`, `MadScience.h`, and `TestMadScience.c`. *NOTE* that it is important that +there are no spaces between the brackets. We know, it's annoying... but it's the rules. + +You can also create an entire pattern of files. To do that, just add a second argument +to the pattern ID. Something like this: + +``` +ceedling module:create[SecretLair,mch] +``` + +In this example, we'd create 9 files total: 3 headers, 3 source files, and 3 test files. These +files would be named `SecretLairModel`, `SecretLairConductor`, and `SecretLairHardware`. Isn't +that nice? + +Similarly, you can create stubs for all functions in a header file just by making a single call +to your handy `stub` feature, like this: + +``` +ceedling module:stub[SecretLair] +``` + +This call will look in SecretLair.h and will generate a file SecretLair.c that contains a stub +for each function declared in the header! Even better, if SecretLair.c already exists, it will +add only new functions, leaving your existing calls alone so that it doesn't cause any problems. + +## Configuration + +Enable the plugin in your project.yml by adding `module_generator` +to the list of enabled plugins. + +Then, like much of Ceedling, you can just run as-is with the defaults, or you can override those +defaults for your own needs. For example, new source and header files will be automatically +placed in the `src/` folder while tests will go in the `test/` folder. That's great if your project +follows the default ceedling structure... but what if you have a different structure? + +``` +:module_generator: + :project_root: ./ + :source_root: source/ + :inc_root: includes/ + :test_root: tests/ +``` + +Now I've redirected the location where modules are going to be generated. + +### Includes + +You can make it so that all of your files are generated with a standard include list. This is done +by adding to the `:includes` array. For example: + +``` +:module_generator: + :includes: + :tst: + - defs.h + - board.h + :src: + - board.h +``` + +### Boilerplates + +You can specify the actual boilerplate used for each of your files. This is the handy place to +put that corporate copyright notice (or maybe a copyleft notice, if that's your perference?) + +``` +:module_generator: + :boilerplates: | + /*************************** + * This file is Awesome. * + * That is All. * + ***************************/ +``` + +### Test Defines + +You can specify the "#ifdef TEST" at the top of the test files with a custom define. +This example will put a "#ifdef CEEDLING_TEST" at the top of the test files. + +``` +:module_generator: + :test_define: CEEDLING_TEST +``` + +### Naming Convention + +Finally, you can force a particular naming convention. Even if someone calls the generator +with something like `MyNewModule`, if they have the naming convention set to `:caps`, it will +generate files like `MY_NEW_MODULE.c`. This keeps everyone on your team behaving the same way. + +Your options are as follows: + + - `:bumpy` - BumpyFilesLooksLikeSo + - `:camel` - camelFilesAreSimilarButStartLow + - `:snake` - snake_case_is_all_lower_and_uses_underscores + - `:caps` - CAPS_FEELS_LIKE_YOU_ARE_SCREAMING + + diff --git a/CAN_App/vendor/ceedling/plugins/module_generator/config/module_generator.yml b/CAN_App/vendor/ceedling/plugins/module_generator/config/module_generator.yml new file mode 100644 index 0000000..cdb2da2 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/module_generator/config/module_generator.yml @@ -0,0 +1,4 @@ +:module_generator: + :project_root: ./ + :source_root: src/ + :test_root: test/ \ No newline at end of file diff --git a/CAN_App/vendor/ceedling/plugins/module_generator/lib/module_generator.rb b/CAN_App/vendor/ceedling/plugins/module_generator/lib/module_generator.rb new file mode 100644 index 0000000..d14288c --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/module_generator/lib/module_generator.rb @@ -0,0 +1,80 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'erb' +require 'fileutils' + +class ModuleGenerator < Plugin + + attr_reader :config + + def create(module_name, optz={}) + + require "generate_module.rb" #From Unity Scripts + + if ((!optz.nil?) && (optz[:destroy])) + UnityModuleGenerator.new( divine_options(optz) ).destroy(module_name) + else + UnityModuleGenerator.new( divine_options(optz) ).generate(module_name) + end + end + + def stub_from_header(module_name, optz={}) + require "cmock.rb" #From CMock + stuboptz = divine_options(optz) + pathname = optz[:path_inc] || optz[:path_src] || "src" + filename = File.expand_path(optz[:module_root_path], File.join(pathname, module_name + ".h")) + CMock.new(stuboptz).setup_skeletons(filename) + end + + private + + def divine_options(optz={}) + unity_generator_options = + { + :path_src => ((defined? MODULE_GENERATOR_SOURCE_ROOT ) ? MODULE_GENERATOR_SOURCE_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') : "src" ), + :path_inc => ((defined? MODULE_GENERATOR_INC_ROOT ) ? + MODULE_GENERATOR_INC_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') + : (defined? MODULE_GENERATOR_SOURCE_ROOT ) ? + MODULE_GENERATOR_SOURCE_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') + : "src" ), + :path_tst => ((defined? MODULE_GENERATOR_TEST_ROOT ) ? MODULE_GENERATOR_TEST_ROOT.gsub( '\\', '/').sub(/^\//, '').sub(/\/$/, '') : "test" ), + :pattern => optz[:pattern], + :test_prefix => ((defined? PROJECT_TEST_FILE_PREFIX ) ? PROJECT_TEST_FILE_PREFIX : "Test" ), + :mock_prefix => ((defined? CMOCK_MOCK_PREFIX ) ? CMOCK_MOCK_PREFIX : "Mock" ), + :includes => ((defined? MODULE_GENERATOR_INCLUDES ) ? MODULE_GENERATOR_INCLUDES : {} ), + :boilerplates => ((defined? MODULE_GENERATOR_BOILERPLATES) ? MODULE_GENERATOR_BOILERPLATES : {} ), + :naming => ((defined? MODULE_GENERATOR_NAMING ) ? MODULE_GENERATOR_NAMING : nil ), + :update_svn => ((defined? MODULE_GENERATOR_UPDATE_SVN ) ? MODULE_GENERATOR_UPDATE_SVN : false ), + :skeleton_path=> ((defined? MODULE_GENERATOR_SOURCE_ROOT ) ? MODULE_GENERATOR_SOURCE_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') : "src" ), + :test_define => ((defined? MODULE_GENERATOR_TEST_DEFINE ) ? MODULE_GENERATOR_TEST_DEFINE : "TEST" ), + } + + # Read Boilerplate template file. + if (defined? MODULE_GENERATOR_BOILERPLATE_FILES) + + bf = MODULE_GENERATOR_BOILERPLATE_FILES + + if !bf[:src].nil? && File.exists?(bf[:src]) + unity_generator_options[:boilerplates][:src] = File.read(bf[:src]) + end + + if !bf[:inc].nil? && File.exists?(bf[:inc]) + unity_generator_options[:boilerplates][:inc] = File.read(bf[:inc]) + end + + if !bf[:tst].nil? && File.exists?(bf[:tst]) + unity_generator_options[:boilerplates][:tst] = File.read(bf[:tst]) + end + end + + # If using "create[<module_root>:<module_name>]" option from command line. + unless optz[:module_root_path].to_s.empty? + unity_generator_options[:path_src] = File.join(optz[:module_root_path], unity_generator_options[:path_src]) + unity_generator_options[:path_inc] = File.join(optz[:module_root_path], unity_generator_options[:path_inc]) + unity_generator_options[:path_tst] = File.join(optz[:module_root_path], unity_generator_options[:path_tst]) + end + + return unity_generator_options + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/module_generator/module_generator.rake b/CAN_App/vendor/ceedling/plugins/module_generator/module_generator.rake new file mode 100644 index 0000000..f4ed9f1 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/module_generator/module_generator.rake @@ -0,0 +1,62 @@ + +namespace :module do + module_root_separator = ":" + + desc "Generate module (source, header and test files)" + task :create, :module_path do |t, args| + files = [args[:module_path]] + (args.extras || []) + optz = { :module_root_path => "" } + ["dh", "dih", "mch", "mvp", "src", "test"].each do |pat| + p = files.delete(pat) + optz[:pattern] = p unless p.nil? + end + files.each do |v| + module_root_path, module_name = v.split(module_root_separator, 2) + if module_name + optz[:module_root_path] = module_root_path + v = module_name + end + if (v =~ /^test_?/i) + # If the name of the file starts with test, automatically treat it as one + @ceedling[:module_generator].create(v.sub(/^test_?/i,''), optz.merge({:pattern => 'test'})) + else + # Otherwise, go through the normal procedure + @ceedling[:module_generator].create(v, optz) + end + end + end + + desc "Generate module stubs from header" + task :stub, :module_path do |t, args| + files = [args[:module_path]] + (args.extras || []) + optz = { :module_root_path => "" } + files.each do |v| + module_root_path, module_name = v.split(module_root_separator, 2) + if module_name + optz[:module_root_path] = module_root_path + v = module_name + end + # Otherwise, go through the normal procedure + @ceedling[:module_generator].stub_from_header(v, optz) + end + end + + desc "Destroy module (source, header and test files)" + task :destroy, :module_path do |t, args| + files = [args[:module_path]] + (args.extras || []) + optz = { :destroy => true, :module_root_path => "" } + ["dh", "dih", "mch", "mvp", "src", "test"].each do |pat| + p = files.delete(pat) + optz[:pattern] = p unless p.nil? + end + files.each do |v| + module_root_path, module_name = v.split(module_root_separator, 2) + if module_name + optz[:module_root_path] = module_root_path + v = module_name + end + @ceedling[:module_generator].create(v, optz) + end + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/raw_output_report/README.md b/CAN_App/vendor/ceedling/plugins/raw_output_report/README.md new file mode 100644 index 0000000..330e87d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/raw_output_report/README.md @@ -0,0 +1,19 @@ +ceedling-raw-output-report +========================== + +## Overview + +The raw-output-report allows you to capture all the output from the called +tools in a single document, so you can trace back through it later. This is +useful for debugging... but can eat through memory quickly if left running. + +## Setup + +Enable the plugin in your project.yml by adding `raw_output_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - raw_output_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb b/CAN_App/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb new file mode 100644 index 0000000..014e677 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb @@ -0,0 +1,41 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +class RawOutputReport < Plugin + def setup + @log_paths = {} + end + + def post_test_fixture_execute(arg_hash) + output = strip_output(arg_hash[:shell_result][:output]) + write_raw_output_log(arg_hash, output) + end + + private + + def strip_output(raw_output) + output = "" + raw_output.each_line do |line| + next if line =~ /^\n$/ + next if line =~ /^.*:\d+:.*:(IGNORE|PASS|FAIL)/ + return output if line =~/^-----------------------\n$/ + output << line + end + end + def write_raw_output_log(arg_hash, output) + logging = generate_log_path(arg_hash) + @ceedling[:file_wrapper].write(logging[:path], output , logging[:flags]) unless logging.nil? + end + + def generate_log_path(arg_hash) + f_name = File.basename(arg_hash[:result_file], '.pass') + base_path = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, arg_hash[:context].to_s) + file_path = File.join(base_path, f_name + '.log') + + if @ceedling[:file_wrapper].exist?(base_path) + return { path: file_path, flags: 'w' } + end + + nil + end +end diff --git a/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md new file mode 100644 index 0000000..9ab6084 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md @@ -0,0 +1,19 @@ +ceedling-stdout-gtestlike-tests-report +====================== + +## Overview + +The stdout_gtestlike_tests_report replaces the normal ceedling "pretty" output with +a variant that resembles the output of gtest. This is most helpful when trying to +integrate into an IDE or CI that is meant to work with google test. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_gtestlike_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_gtestlike_tests_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb new file mode 100644 index 0000000..fb8e3b1 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb @@ -0,0 +1,84 @@ +% ignored = hash[:results][:counts][:ignored] +% failed = hash[:results][:counts][:failed] +% stdout_count = hash[:results][:counts][:stdout] +% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '') +% banner_width = 25 + header_prepend.length # widest message +% results = {} +% hash[:results][:successes].each do |testresult| +% results[ testresult[:source][:file] ] = testresult[:collection] +% results[ testresult[:source][:file] ].length.times do |i| +% results[ testresult[:source][:file] ][i][:pass] = true +% end +% end +% hash[:results][:ignores].each do |testresult| +% if (results[ testresult[:source][:file] ].nil?) +% results[ testresult[:source][:file] ] = testresult[:collection] +% else +% results[ testresult[:source][:file] ] += testresult[:collection] +% end +% results[ testresult[:source][:file] ].length.times do |i| +% results[ testresult[:source][:file] ][i][:pass] = true +% end +% end +% hash[:results][:failures].each do |testresult| +% if (results[ testresult[:source][:file] ].nil?) +% results[ testresult[:source][:file] ] = testresult[:collection] +% else +% results[ testresult[:source][:file] ] += testresult[:collection] +% end +% end + + +[==========] Running <%=hash[:results][:counts][:total].to_s%> tests from <%=results.length.to_s%> test cases. +[----------] Global test environment set-up. +% results.each_pair do |modulename, moduledetails| +[----------] <%=moduledetails.length.to_s%> tests from <%=modulename%> +% moduledetails.each do |item| +[ RUN ] <%=modulename%>.<%=item[:test]%> +% if (not item[:pass]) +% if (not item[:message].empty?) +<%=modulename%>(<%=item[:line]%>): error: <%=item[:message]%> + +% m = item[:message].match(/Expected\s+(.*)\s+Was\s+([^\.]*)\./) +% if m.nil? + Actual: FALSE + Expected: TRUE +% else + Actual: <%=m[2]%> + Expected: <%=m[1]%> +% end +% else +<%=modulename%>(<%=item[:line]%>): fail: <%=item[:message]%> + Actual: FALSE + Expected: TRUE +% end +[ FAILED ] <%=modulename%>.<%=item[:test]%> (0 ms) +% else +[ OK ] <%=modulename%>.<%=item[:test]%> (0 ms) +% end +% end +[----------] <%=moduledetails.length.to_s%> tests from <%=modulename%> (0 ms total) +% end + +% if (hash[:results][:counts][:total] > 0) +[----------] Global test environment tear-down. +[==========] <%=hash[:results][:counts][:total].to_s%> tests from <%=hash[:results][:stdout].length.to_s%> test cases ran. +[ PASSED ] <%=hash[:results][:counts][:passed].to_s%> tests. +% if (failed == 0) +[ FAILED ] 0 tests. + + 0 FAILED TESTS +% else +[ FAILED ] <%=failed.to_s%> tests, listed below: +% hash[:results][:failures].each do |failure| +% failure[:collection].each do |item| +[ FAILED ] <%=failure[:source][:file]%>.<%=item[:test]%> +% end +% end +% end + + <%=failed.to_s%> FAILED TESTS +% else + +No tests executed. +% end diff --git a/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy new file mode 100644 index 0000000..a90f495 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy @@ -0,0 +1,59 @@ +% ignored = hash[:results][:counts][:ignored] +% failed = hash[:results][:counts][:failed] +% stdout_count = hash[:results][:counts][:stdout] +% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '') +% banner_width = 25 + header_prepend.length # widest message + + +% if (stdout_count > 0) +[==========] Running <%=hash[:results][:counts][:total].to_s%> tests from <%=hash[:results][:stdout].length.to_s%> test cases. +[----------] Global test environment set-up. +% end +% if (failed > 0) +% hash[:results][:failures].each do |failure| +[----------] <%=failure[:collection].length.to_s%> tests from <%=failure[:source][:file]%> +% failure[:collection].each do |item| +[ RUN ] <%=failure[:source][:file]%>.<%=item[:test]%> +% if (not item[:message].empty?) +<%=failure[:source][:file]%>(<%=item[:line]%>): error: <%=item[:message]%> + +% m = item[:message].match(/Expected\s+(.*)\s+Was\s+([^\.]*)\./) +% if m.nil? + Actual: FALSE + Expected: TRUE +% else + Actual: <%=m[2]%> + Expected: <%=m[1]%> +% end +% else +<%=failure[:source][:file]%>(<%=item[:line]%>): fail: <%=item[:message]%> + Actual: FALSE + Expected: TRUE +% end +[ FAILED ] <%=failure[:source][:file]%>.<%=item[:test]%> (0 ms) +% end +[----------] <%=failure[:collection].length.to_s%> tests from <%=failure[:source][:file]%> (0 ms total) +% end +% end +% if (hash[:results][:counts][:total] > 0) +[----------] Global test environment tear-down. +[==========] <%=hash[:results][:counts][:total].to_s%> tests from <%=hash[:results][:stdout].length.to_s%> test cases ran. +[ PASSED ] <%=hash[:results][:counts][:passed].to_s%> tests. +% if (failed == 0) +[ FAILED ] 0 tests. + + 0 FAILED TESTS +% else +[ FAILED ] <%=failed.to_s%> tests, listed below: +% hash[:results][:failures].each do |failure| +% failure[:collection].each do |item| +[ FAILED ] <%=failure[:source][:file]%>.<%=item[:test]%> +% end +% end + + <%=failed.to_s%> FAILED TESTS +% end +% else + +No tests executed. +% end diff --git a/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml new file mode 100644 index 0000000..c25acf5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml @@ -0,0 +1,4 @@ +--- +:plugins: + # tell Ceedling we got results display taken care of + :display_raw_test_results: FALSE diff --git a/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb new file mode 100644 index 0000000..a51438a --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb @@ -0,0 +1,43 @@ +require 'ceedling/plugin' +require 'ceedling/defaults' + +class StdoutGtestlikeTestsReport < Plugin + + def setup + @result_list = [] + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + template = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb')) + @ceedling[:plugin_reportinator].register_test_results_template( template ) + end + + def post_test_fixture_execute(arg_hash) + return if not (arg_hash[:context] == TEST_SYM) + + @result_list << arg_hash[:result_file] + end + + def post_build + return if not (@ceedling[:task_invoker].test_invoked?) + + results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list) + hash = { + :header => '', + :results => results + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + end + + def summary + result_list = @ceedling[:file_path_utils].form_pass_results_filelist( PROJECT_TEST_RESULTS_PATH, COLLECTION_ALL_TESTS ) + + # get test results for only those tests in our configuration and of those only tests with results on disk + hash = { + :header => '', + :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false}) + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/README.md new file mode 100644 index 0000000..ed6c655 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/README.md @@ -0,0 +1,18 @@ +ceedling-stdout-ide-tests-report +================================ + +## Overview + +The stdout_ide_tests_report replaces the normal ceedling "pretty" output with +a simplified variant intended to be easily parseable. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_ide_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_ide_tests_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml new file mode 100644 index 0000000..c25acf5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml @@ -0,0 +1,4 @@ +--- +:plugins: + # tell Ceedling we got results display taken care of + :display_raw_test_results: FALSE diff --git a/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb new file mode 100644 index 0000000..48b3e81 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb @@ -0,0 +1,44 @@ +require 'ceedling/plugin' +require 'ceedling/defaults' + +class StdoutIdeTestsReport < Plugin + + def setup + @result_list = [] + end + + def post_test_fixture_execute(arg_hash) + return if not (arg_hash[:context] == TEST_SYM) + + @result_list << arg_hash[:result_file] + end + + def post_build + return if (not @ceedling[:task_invoker].test_invoked?) + + results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list) + hash = { + :header => '', + :results => results + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) do + message = '' + message = 'Unit test failures.' if (hash[:results][:counts][:failed] > 0) + message + end + end + + def summary + result_list = @ceedling[:file_path_utils].form_pass_results_filelist( PROJECT_TEST_RESULTS_PATH, COLLECTION_ALL_TESTS ) + + # get test results for only those tests in our configuration and of those only tests with results on disk + hash = { + :header => '', + :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false}) + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md new file mode 100644 index 0000000..7e1be23 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md @@ -0,0 +1,20 @@ +ceedling-pretty-tests-report +============================ + +## Overview + +The stdout_pretty_tests_report is the default output of ceedling. Instead of +showing most of the raw output of CMock, Ceedling, etc., it shows a simplified +view. It also creates a nice summary at the end of execution which groups the +results into ignored and failed tests. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_pretty_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_pretty_tests_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb new file mode 100644 index 0000000..52b29f7 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb @@ -0,0 +1,59 @@ +% ignored = hash[:results][:counts][:ignored] +% failed = hash[:results][:counts][:failed] +% stdout_count = hash[:results][:counts][:stdout] +% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '') +% banner_width = 25 + header_prepend.length # widest message + +% if (stdout_count > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'TEST OUTPUT')%> +% hash[:results][:stdout].each do |string| +[<%=string[:source][:file]%>] +% string[:collection].each do |item| + - "<%=item%>" +% end + +% end +% end +% if (ignored > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'IGNORED TEST SUMMARY')%> +% hash[:results][:ignores].each do |ignore| +[<%=ignore[:source][:file]%>] +% ignore[:collection].each do |item| + Test: <%=item[:test]%> +% if (not item[:message].empty?) + At line (<%=item[:line]%>): "<%=item[:message]%>" +% else + At line (<%=item[:line]%>) +% end + +% end +% end +% end +% if (failed > 0) +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'FAILED TEST SUMMARY')%> +% hash[:results][:failures].each do |failure| +[<%=failure[:source][:file]%>] +% failure[:collection].each do |item| + Test: <%=item[:test]%> +% if (not item[:message].empty?) + At line (<%=item[:line]%>): "<%=item[:message]%>" +% else + At line (<%=item[:line]%>) +% end + +% end +% end +% end +% total_string = hash[:results][:counts][:total].to_s +% format_string = "%#{total_string.length}i" +<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'OVERALL TEST SUMMARY')%> +% if (hash[:results][:counts][:total] > 0) +TESTED: <%=hash[:results][:counts][:total].to_s%> +PASSED: <%=sprintf(format_string, hash[:results][:counts][:passed])%> +FAILED: <%=sprintf(format_string, failed)%> +IGNORED: <%=sprintf(format_string, ignored)%> +% else + +No tests executed. +% end + diff --git a/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml new file mode 100644 index 0000000..c25acf5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml @@ -0,0 +1,4 @@ +--- +:plugins: + # tell Ceedling we got results display taken care of + :display_raw_test_results: FALSE diff --git a/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb new file mode 100644 index 0000000..018388f --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb @@ -0,0 +1,47 @@ +require 'ceedling/plugin' +require 'ceedling/defaults' + +class StdoutPrettyTestsReport < Plugin + + def setup + @result_list = [] + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + template = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb')) + @ceedling[:plugin_reportinator].register_test_results_template( template ) + end + + def post_test_fixture_execute(arg_hash) + return if not (arg_hash[:context] == TEST_SYM) + + @result_list << arg_hash[:result_file] + end + + def post_build + return if not (@ceedling[:task_invoker].test_invoked?) + + results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list) + hash = { + :header => '', + :results => results + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) do + message = '' + message = 'Unit test failures.' if (results[:counts][:failed] > 0) + message + end + end + + def summary + result_list = @ceedling[:file_path_utils].form_pass_results_filelist( PROJECT_TEST_RESULTS_PATH, COLLECTION_ALL_TESTS ) + + # get test results for only those tests in our configuration and of those only tests with results on disk + hash = { + :header => '', + :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false}) + } + + @ceedling[:plugin_reportinator].run_test_results_report(hash) + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/subprojects/README.md b/CAN_App/vendor/ceedling/plugins/subprojects/README.md new file mode 100644 index 0000000..e51a4e6 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/subprojects/README.md @@ -0,0 +1,63 @@ +ceedling-subprojects +==================== + +Plugin for supporting subprojects that are built as static libraries. It continues to support +dependency tracking, without getting confused between your main project files and your +subproject files. It accepts different compiler flags and linker flags, allowing you to +optimize for your situation. + +First, you're going to want to add the extension to your list of known extensions: + +``` +:extension: + :subprojects: '.a' +``` + +Define a new section called :subprojects. There, you can list as many subprojects +as you may need under the :paths key. For each, you specify a unique place to build +and a unique name. + +``` +:subprojects: + :paths: + - :name: libprojectA + :source: + - ./subprojectA/first/dir + - ./subprojectA/second/dir + :include: + - ./subprojectA/include/dir + :build_root: ./subprojectA/build/dir + :defines: + - DEFINE_JUST_FOR_THIS_FILE + - AND_ANOTHER + - :name: libprojectB + :source: + - ./subprojectB/only/dir + :include: + - ./subprojectB/first/include/dir + - ./subprojectB/second/include/dir + :build_root: ./subprojectB/build/dir + :defines: [] #none for this one +``` + +You can specify the compiler and linker, just as you would a release build: + +``` +:tools: + :subprojects_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_SUBPROJECTS + - -D$: COLLECTION_DEFINES_SUBPROJECTS + - -c "${1}" + - -o "${2}" + :subprojects_linker: + :executable: ar + :arguments: + - rcs + - ${2} + - ${1} +``` + +That's all there is to it! Happy Hacking! diff --git a/CAN_App/vendor/ceedling/plugins/subprojects/config/defaults.yml b/CAN_App/vendor/ceedling/plugins/subprojects/config/defaults.yml new file mode 100644 index 0000000..1045a59 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/subprojects/config/defaults.yml @@ -0,0 +1,33 @@ +--- +#:extension: +# :subprojects: '.a' + +:subprojects: + :paths: [] +# - :name: subprojectA +# :source: +# - ./first/subproject/dir +# - ./second/subproject/dir +# :include: +# - ./first/include/dir +# :build_root: ./subproject/build/dir +# :defines: +# - FIRST_DEFINE + +:tools: + :subprojects_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_SUBPROJECTS + - -D$: COLLECTION_DEFINES_SUBPROJECTS + - -c "${1}" + - -o "${2}" + :subprojects_linker: + :executable: ar + :arguments: + - rcs + - ${2} + - ${1} + +... diff --git a/CAN_App/vendor/ceedling/plugins/subprojects/lib/subprojects.rb b/CAN_App/vendor/ceedling/plugins/subprojects/lib/subprojects.rb new file mode 100644 index 0000000..559251e --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/subprojects/lib/subprojects.rb @@ -0,0 +1,92 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +SUBPROJECTS_ROOT_NAME = 'subprojects' +SUBPROJECTS_TASK_ROOT = SUBPROJECTS_ROOT_NAME + ':' +SUBPROJECTS_SYM = SUBPROJECTS_ROOT_NAME.to_sym + +class Subprojects < Plugin + + def setup + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Add to the test paths + SUBPROJECTS_PATHS.each do |subproj| + subproj[:source].each do |path| + COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR << path + end + subproj[:include].each do |path| + COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR << path + end + end + + # Gather information about the subprojects + @subprojects = {} + @subproject_lookup_by_path = {} + SUBPROJECTS_PATHS.each do |subproj| + @subprojects[ subproj[:name] ] = subproj.clone + @subprojects[ subproj[:name] ][:c] = [] + @subprojects[ subproj[:name] ][:asm] = [] + subproj[:source].each do |path| + search_path = "#{path[-1].match(/\\|\//) ? path : "#{path}/"}*#{EXTENSION_SOURCE}" + @subprojects[ subproj[:name] ][:c] += Dir[search_path] + if (EXTENSION_ASSEMBLY && !EXTENSION_ASSEMBLY.empty?) + search_path = "#{path[-1].match(/\\|\//) ? path : "#{path}/"}*#{EXTENSION_ASSEMBLY}" + @subprojects[ subproj[:name] ][:asm] += Dir[search_path] + end + end + @subproject_lookup_by_path[ subproj[:build_root] ] = subproj[:name] + end + end + + def find_my_project( c_file, file_type = :c ) + @subprojects.each_pair do |subprojname, subproj| + return subprojname if (subproj[file_type].include?(c_file)) + end + end + + def find_my_paths( c_file, file_type = :c ) + @subprojects.each_pair do |subprojname, subproj| + return (subproj[:source] + (subproj[:include] || [])) if (subproj[file_type].include?(c_file)) + end + return [] + end + + def find_my_defines( c_file, file_type = :c ) + @subprojects.each_pair do |subprojname, subproj| + return (subproj[:defines] || []) if (subproj[file_type].include?(c_file)) + end + return [] + end + + def list_all_object_files_for_subproject( lib_name ) + subproj = File.basename(lib_name, EXTENSION_SUBPROJECTS) + objpath = "#{@subprojects[subproj][:build_root]}/out/c" + bbb = @subprojects[subproj][:c].map{|f| "#{objpath}/#{File.basename(f,EXTENSION_SOURCE)}#{EXTENSION_OBJECT}" } + bbb + end + + def find_library_source_file_for_object( obj_name ) + cname = "#{File.basename(obj_name, EXTENSION_OBJECT)}#{EXTENSION_SOURCE}" + dname = File.dirname(obj_name)[0..-7] + pname = @subproject_lookup_by_path[dname] + return @ceedling[:file_finder].find_file_from_list(cname, @subprojects[pname][:c], :error) + end + + def find_library_assembly_file_for_object( obj_name ) + cname = "#{File.basename(obj_name, EXTENSION_OBJECT)}#{EXTENSION_ASEMBLY}" + dname = File.dirname(obj_name)[0..-7] + pname = @subproject_lookup_by_path[dname] + return @ceedling[:file_finder].find_file_from_list(cname, @subprojects[pname][:asm], :error) + end + + def replace_constant(constant, new_value) + Object.send(:remove_const, constant.to_sym) if (Object.const_defined? constant) + Object.const_set(constant, new_value) + end + +end + +# end blocks always executed following rake run +END { +} diff --git a/CAN_App/vendor/ceedling/plugins/subprojects/subprojects.rake b/CAN_App/vendor/ceedling/plugins/subprojects/subprojects.rake new file mode 100644 index 0000000..0025c3e --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/subprojects/subprojects.rake @@ -0,0 +1,78 @@ + + +SUBPROJECTS_PATHS.each do |subproj| + + subproj_source = subproj[:source] + subproj_include = subproj[:include] + subproj_name = subproj[:name] + subproj_build_root = subproj[:build_root] + subproj_build_out = "#{subproj[:build_root]}/out" + subproj_build_c = "#{subproj[:build_root]}/out/c" + subproj_build_asm = "#{subproj[:build_root]}/out/asm" + subproj_directories = [ subproj_build_root, subproj_build_out, subproj_build_c, subproj_build_asm ] + + subproj_directories.each do |subdir| + directory(subdir) + end + + CLEAN.include(File.join(subproj_build_root, '*')) + CLEAN.include(File.join(subproj_build_out, '*')) + + CLOBBER.include(File.join(subproj_build_root, '**/*')) + + # Add a rule for building the actual static library from our object files + rule(/#{subproj_build_root}#{'.+\\'+EXTENSION_SUBPROJECTS}$/ => [ + proc do |task_name| + @ceedling[SUBPROJECTS_SYM].list_all_object_files_for_subproject(task_name) + end + ]) do |bin_file| + @ceedling[:generator].generate_executable_file( + TOOLS_SUBPROJECTS_LINKER, + SUBPROJECTS_SYM, + bin_file.prerequisites, + bin_file.name, + @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name)) + end + + # Add a rule for building object files from assembly files to link into a library + if (RELEASE_BUILD_USE_ASSEMBLY) + rule(/#{subproj_build_asm}#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[SUBPROJECTS_SYM].find_library_assembly_file_for_object(task_name) + end + ]) do |object| + @ceedling[SUBPROJECTS_SYM].replace_constant(:COLLECTION_PATHS_SUBPROJECTS, @ceedling[SUBPROJECTS_SYM].find_my_paths(object.source, :asm)) + @ceedling[SUBPROJECTS_SYM].replace_constant(:COLLECTION_DEFINES_SUBPROJECTS, @ceedling[SUBPROJECTS_SYM].find_my_defines(object.source, :asm)) + @ceedling[:generator].generate_object_file( + TOOLS_SUBPROJECTS_ASSEMBLER, + OPERATION_ASSEMBLE_SYM, + SUBPROJECTS_SYM, + object.source, + object.name ) + end + end + + # Add a rule for building object files from C files to link into a library + rule(/#{subproj_build_c}#{'.+\\'+EXTENSION_OBJECT}$/ => [ + proc do |task_name| + @ceedling[SUBPROJECTS_SYM].find_library_source_file_for_object(task_name) + end + ]) do |object| + @ceedling[SUBPROJECTS_SYM].replace_constant(:COLLECTION_PATHS_SUBPROJECTS, @ceedling[SUBPROJECTS_SYM].find_my_paths(object.source, :c)) + @ceedling[SUBPROJECTS_SYM].replace_constant(:COLLECTION_DEFINES_SUBPROJECTS, @ceedling[SUBPROJECTS_SYM].find_my_defines(object.source, :c)) + @ceedling[:generator].generate_object_file( + TOOLS_SUBPROJECTS_COMPILER, + OPERATION_COMPILE_SYM, + SUBPROJECTS_SYM, + object.source, + object.name, + @ceedling[:file_path_utils].form_release_build_c_list_filepath( object.name ) ) + end + + # Add the subdirectories involved to our list of those that should be autogenerated + task :directories => subproj_directories.clone + + # Finally, add the static library to our RELEASE build dependency list + task RELEASE_SYM => ["#{subproj_build_root}/#{subproj_name}#{EXTENSION_SUBPROJECTS}"] +end + diff --git a/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/README.md new file mode 100644 index 0000000..9fcda7d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/README.md @@ -0,0 +1,18 @@ +ceedling-teamcity-tests-report +============================== + +## Overview + +The teamcity_tests_report replaces the normal ceedling "pretty" output with +a version that has results tagged to be consumed with the teamcity CI server. + +## Setup + +Enable the plugin in your project.yml by adding `teamcity_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - teamcity_tests_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml new file mode 100644 index 0000000..c25acf5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml @@ -0,0 +1,4 @@ +--- +:plugins: + # tell Ceedling we got results display taken care of + :display_raw_test_results: FALSE diff --git a/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb new file mode 100644 index 0000000..33d8548 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb @@ -0,0 +1,57 @@ +require 'ceedling/plugin' +require 'ceedling/defaults' + +class TeamcityTestsReport < Plugin + + def setup + @suite_started = nil + @output_enabled = !defined?(TEAMCITY_BUILD) || TEAMCITY_BUILD + end + + def escape(string) + string.gsub(/['|\[\]]/, '|\0').gsub('\r', '|r').gsub('\n', '|n') + end + + def pre_test(test) + teamcity_message "testSuiteStarted name='#{File.basename(test, '.c')}'" + @suite_started = Time.now + end + + def post_test(test) + teamcity_message "testSuiteFinished name='#{File.basename(test, '.c')}'" + end + + def post_test_fixture_execute(arg_hash) + duration = (Time.now - @suite_started) * 1000 + results = @ceedling[:plugin_reportinator].assemble_test_results([arg_hash[:result_file]]) + avg_duration = (duration / [1, results[:counts][:passed] + results[:counts][:failed]].max).round + + results[:successes].each do |success| + success[:collection].each do |test| + teamcity_message "testStarted name='#{test[:test]}'" + teamcity_message "testFinished name='#{test[:test]}' duration='#{avg_duration}'" + end + end + + results[:failures].each do |failure| + failure[:collection].each do |test| + teamcity_message "testStarted name='#{test[:test]}'" + teamcity_message "testFailed name='#{test[:test]}' message='#{escape(test[:message])}' details='File: #{failure[:source][:path]}/#{failure[:source][:file]} Line: #{test[:line]}'" + teamcity_message "testFinished name='#{test[:test]}' duration='#{avg_duration}'" + end + end + + results[:ignores].each do |failure| + failure[:collection].each do |test| + teamcity_message "testIgnored name='#{test[:test]}' message='#{escape(test[:message])}'" + end + end + + # We ignore stdout + end + + def teamcity_message(content) + puts "##teamcity[#{content}]" unless !@output_enabled + end + +end diff --git a/CAN_App/vendor/ceedling/plugins/warnings_report/README.md b/CAN_App/vendor/ceedling/plugins/warnings_report/README.md new file mode 100644 index 0000000..fd7fae5 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/warnings_report/README.md @@ -0,0 +1,19 @@ +warnings-report +=============== + +## Overview + +The warnings_report captures all warnings throughout the build process +and collects them into a single report at the end of execution. It places all +of this into a warnings file in the output artifact directory. + +## Setup + +Enable the plugin in your project.yml by adding `warnings_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - warnings_report +``` diff --git a/CAN_App/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb b/CAN_App/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb new file mode 100644 index 0000000..d4f43fb --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb @@ -0,0 +1,69 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +class WarningsReport < Plugin + def setup + @stderr_redirect = nil + @log_paths = {} + end + + def pre_compile_execute(arg_hash) + # at beginning of compile, override tool's stderr_redirect so we can parse $stderr + $stdout + set_stderr_redirect(arg_hash) + end + + def post_compile_execute(arg_hash) + # after compilation, grab output for parsing/logging, restore stderr_redirect, log warning if it exists + output = arg_hash[:shell_result][:output] + restore_stderr_redirect(arg_hash) + write_warning_log(arg_hash[:context], output) + end + + def pre_link_execute(arg_hash) + # at beginning of link, override tool's stderr_redirect so we can parse $stderr + $stdout + set_stderr_redirect(arg_hash) + end + + def post_link_execute(arg_hash) + # after linking, grab output for parsing/logging, restore stderr_redirect, log warning if it exists + output = arg_hash[:shell_result][:output] + restore_stderr_redirect(arg_hash) + write_warning_log(arg_hash[:context], output) + end + + private + + def set_stderr_redirect(hash) + @stderr_redirect = hash[:tool][:stderr_redirect] + hash[:tool][:stderr_redirect] = StdErrRedirect::AUTO + end + + def restore_stderr_redirect(hash) + hash[:tool][:stderr_redirect] = @stderr_redirect + end + + def write_warning_log(context, output) + # if $stderr/$stdout contain "warning", log it + if output =~ /warning/i + # generate a log path & file io write flags + logging = generate_log_path(context) + @ceedling[:file_wrapper].write(logging[:path], output + "\n", logging[:flags]) unless logging.nil? + end + end + + def generate_log_path(context) + # if path has already been generated, return it & 'append' file io flags (append to log) + return { path: @log_paths[context], flags: 'a' } unless @log_paths[context].nil? + + # first time through, generate path & 'write' file io flags (create new log) + base_path = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s) + file_path = File.join(base_path, 'warnings.log') + + if @ceedling[:file_wrapper].exist?(base_path) + @log_paths[context] = file_path + return { path: file_path, flags: 'w' } + end + + nil + end +end diff --git a/CAN_App/vendor/ceedling/plugins/xml_tests_report/README.md b/CAN_App/vendor/ceedling/plugins/xml_tests_report/README.md new file mode 100644 index 0000000..6200c7d --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/xml_tests_report/README.md @@ -0,0 +1,36 @@ +xml_tests_report +================ + +## Overview + +The xml_tests_report plugin creates an XML file of test results in xUnit +format, which is handy for Continuous Integration build servers or as input +into other reporting tools. The XML file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `xml_tests_report` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - xml_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.xml`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:xml_tests_report: + :artifact_filename: report_xunit.xml +``` diff --git a/CAN_App/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb b/CAN_App/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb new file mode 100644 index 0000000..ed4e996 --- /dev/null +++ b/CAN_App/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb @@ -0,0 +1,110 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +class XmlTestsReport < Plugin + def setup + @results_list = {} + @test_counter = 0 + end + + def post_test_fixture_execute(arg_hash) + context = arg_hash[:context] + + @results_list[context] = [] if @results_list[context].nil? + + @results_list[context] << arg_hash[:result_file] + end + + def post_build + @results_list.each_key do |context| + results = @ceedling[:plugin_reportinator].assemble_test_results(@results_list[context]) + + artifact_filename = @ceedling[:configurator].project_config_hash[:xml_tests_report_artifact_filename] || 'report.xml' + artifact_fullpath = @ceedling[:configurator].project_config_hash[:xml_tests_report_path] || File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s) + file_path = File.join(artifact_fullpath, artifact_filename) + + @ceedling[:file_wrapper].open(file_path, 'w') do |f| + @test_counter = 1 + write_results(results, f) + end + end + end + + private + + def write_results(results, stream) + write_header(stream) + write_failures(results[:failures], stream) + write_tests(results[:successes], stream, 'SuccessfulTests') + write_tests(results[:ignores], stream, 'IgnoredTests') + write_statistics(results[:counts], stream) + write_footer(stream) + end + + def write_header(stream) + stream.puts "<?xml version='1.0' encoding='utf-8' ?>" + stream.puts '<TestRun>' + end + + def write_failures(results, stream) + if results.size.zero? + stream.puts "\t<FailedTests/>" + return + end + + stream.puts "\t<FailedTests>" + + results.each do |result| + result[:collection].each do |item| + filename = File.join(result[:source][:path], result[:source][:file]) + + stream.puts "\t\t<Test id=\"#{@test_counter}\">" + stream.puts "\t\t\t<Name>#{filename}::#{item[:test]}</Name>" + stream.puts "\t\t\t<FailureType>Assertion</FailureType>" + stream.puts "\t\t\t<Location>" + stream.puts "\t\t\t\t<File>#{filename}</File>" + stream.puts "\t\t\t\t<Line>#{item[:line]}</Line>" + stream.puts "\t\t\t</Location>" + stream.puts "\t\t\t<Message>#{item[:message]}</Message>" + stream.puts "\t\t</Test>" + @test_counter += 1 + end + end + + stream.puts "\t</FailedTests>" + end + + def write_tests(results, stream, tag) + if results.size.zero? + stream.puts "\t<#{tag}/>" + return + end + + stream.puts "\t<#{tag}>" + + results.each do |result| + result[:collection].each do |item| + stream.puts "\t\t<Test id=\"#{@test_counter}\">" + stream.puts "\t\t\t<Name>#{File.join(result[:source][:path], result[:source][:file])}::#{item[:test]}</Name>" + stream.puts "\t\t</Test>" + @test_counter += 1 + end + end + + stream.puts "\t</#{tag}>" + end + + def write_statistics(counts, stream) + stream.puts "\t<Statistics>" + stream.puts "\t\t<Tests>#{counts[:total]}</Tests>" + stream.puts "\t\t<Ignores>#{counts[:ignored]}</Ignores>" + stream.puts "\t\t<FailuresTotal>#{counts[:failed]}</FailuresTotal>" + stream.puts "\t\t<Errors>0</Errors>" + stream.puts "\t\t<Failures>#{counts[:failed]}</Failures>" + stream.puts "\t</Statistics>" + end + + def write_footer(stream) + stream.puts '</TestRun>' + end +end diff --git a/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.c b/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.c new file mode 100644 index 0000000..fdff8f4 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.c @@ -0,0 +1,46 @@ +#include "CException.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +volatile CEXCEPTION_FRAME_T CExceptionFrames[CEXCEPTION_NUM_ID] = {{ 0 }}; +#pragma GCC diagnostic pop + +//------------------------------------------------------------------------------------------ +// Throw +//------------------------------------------------------------------------------------------ +void Throw(CEXCEPTION_T ExceptionID) +{ + unsigned int MY_ID = CEXCEPTION_GET_ID; + CExceptionFrames[MY_ID].Exception = ExceptionID; + if (CExceptionFrames[MY_ID].pFrame) + { + longjmp(*CExceptionFrames[MY_ID].pFrame, 1); + } + CEXCEPTION_NO_CATCH_HANDLER(ExceptionID); +} + +//------------------------------------------------------------------------------------------ +// Explanation of what it's all for: +//------------------------------------------------------------------------------------------ +/* +#define Try + { <- give us some local scope. most compilers are happy with this + jmp_buf *PrevFrame, NewFrame; <- prev frame points to the last try block's frame. new frame gets created on stack for this Try block + unsigned int MY_ID = CEXCEPTION_GET_ID; <- look up this task's id for use in frame array. always 0 if single-tasking + PrevFrame = CExceptionFrames[CEXCEPTION_GET_ID].pFrame; <- set pointer to point at old frame (which array is currently pointing at) + CExceptionFrames[MY_ID].pFrame = &NewFrame; <- set array to point at my new frame instead, now + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; <- initialize my exception id to be NONE + if (setjmp(NewFrame) == 0) { <- do setjmp. it returns 1 if longjump called, otherwise 0 + if (&PrevFrame) <- this is here to force proper scoping. it requires braces or a single line to be but after Try, otherwise won't compile. This is always true at this point. + +#define Catch(e) + else { } <- this also forces proper scoping. Without this they could stick their own 'else' in and it would get ugly + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; <- no errors happened, so just set the exception id to NONE (in case it was corrupted) + } + else <- an exception occurred + { e = CExceptionFrames[MY_ID].Exception; e=e;} <- assign the caught exception id to the variable passed in. + CExceptionFrames[MY_ID].pFrame = PrevFrame; <- make the pointer in the array point at the previous frame again, as if NewFrame never existed. + } <- finish off that local scope we created to have our own variables + if (CExceptionFrames[CEXCEPTION_GET_ID].Exception != CEXCEPTION_NONE) <- start the actual 'catch' processing if we have an exception id saved away + */ + diff --git a/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.h b/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.h new file mode 100644 index 0000000..be9e186 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/c_exception/lib/CException.h @@ -0,0 +1,115 @@ +#ifndef _CEXCEPTION_H +#define _CEXCEPTION_H + +#include <setjmp.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define CEXCEPTION_VERSION_MAJOR 1 +#define CEXCEPTION_VERSION_MINOR 3 +#define CEXCEPTION_VERSION_BUILD 3 +#define CEXCEPTION_VERSION ((CEXCEPTION_VERSION_MAJOR << 16) | (CEXCEPTION_VERSION_MINOR << 8) | CEXCEPTION_VERSION_BUILD) + +//To Use CException, you have a number of options: +//1. Just include it and run with the defaults +//2. Define any of the following symbols at the command line to override them +//3. Include a header file before CException.h everywhere which defines any of these +//4. Create an Exception.h in your path, and just define EXCEPTION_USE_CONFIG_FILE first + +#ifdef CEXCEPTION_USE_CONFIG_FILE +#include "CExceptionConfig.h" +#endif + +//This is the value to assign when there isn't an exception +#ifndef CEXCEPTION_NONE +#define CEXCEPTION_NONE (0x5A5A5A5A) +#endif + +//This is number of exception stacks to keep track of (one per task) +#ifndef CEXCEPTION_NUM_ID +#define CEXCEPTION_NUM_ID (1) //there is only the one stack by default +#endif + +//This is the method of getting the current exception stack index (0 if only one stack) +#ifndef CEXCEPTION_GET_ID +#define CEXCEPTION_GET_ID (0) //use the first index always because there is only one anyway +#endif + +//The type to use to store the exception values. +#ifndef CEXCEPTION_T +#define CEXCEPTION_T unsigned int +#endif + +//This is an optional special handler for when there is no global Catch +#ifndef CEXCEPTION_NO_CATCH_HANDLER +#define CEXCEPTION_NO_CATCH_HANDLER(id) +#endif + +//These hooks allow you to inject custom code into places, particularly useful for saving and restoring additional state +#ifndef CEXCEPTION_HOOK_START_TRY +#define CEXCEPTION_HOOK_START_TRY +#endif +#ifndef CEXCEPTION_HOOK_HAPPY_TRY +#define CEXCEPTION_HOOK_HAPPY_TRY +#endif +#ifndef CEXCEPTION_HOOK_AFTER_TRY +#define CEXCEPTION_HOOK_AFTER_TRY +#endif +#ifndef CEXCEPTION_HOOK_START_CATCH +#define CEXCEPTION_HOOK_START_CATCH +#endif + +//exception frame structures +typedef struct { + jmp_buf* pFrame; + CEXCEPTION_T volatile Exception; +} CEXCEPTION_FRAME_T; + +//actual root frame storage (only one if single-tasking) +extern volatile CEXCEPTION_FRAME_T CExceptionFrames[]; + +//Try (see C file for explanation) +#define Try \ + { \ + jmp_buf *PrevFrame, NewFrame; \ + unsigned int MY_ID = CEXCEPTION_GET_ID; \ + PrevFrame = CExceptionFrames[MY_ID].pFrame; \ + CExceptionFrames[MY_ID].pFrame = (jmp_buf*)(&NewFrame); \ + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ + CEXCEPTION_HOOK_START_TRY; \ + if (setjmp(NewFrame) == 0) { \ + if (1) + +//Catch (see C file for explanation) +#define Catch(e) \ + else { } \ + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ + CEXCEPTION_HOOK_HAPPY_TRY; \ + } \ + else \ + { \ + e = CExceptionFrames[MY_ID].Exception; \ + (void)e; \ + CEXCEPTION_HOOK_START_CATCH; \ + } \ + CExceptionFrames[MY_ID].pFrame = PrevFrame; \ + CEXCEPTION_HOOK_AFTER_TRY; \ + } \ + if (CExceptionFrames[CEXCEPTION_GET_ID].Exception != CEXCEPTION_NONE) + +//Throw an Error +void Throw(CEXCEPTION_T ExceptionID); + +//Just exit the Try block and skip the Catch. +#define ExitTry() Throw(CEXCEPTION_NONE) + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // _CEXCEPTION_H diff --git a/CAN_App/vendor/ceedling/vendor/c_exception/lib/meson.build b/CAN_App/vendor/ceedling/vendor/c_exception/lib/meson.build new file mode 100644 index 0000000..2770122 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/c_exception/lib/meson.build @@ -0,0 +1,11 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mark VanderVoord. +# +# license: MIT +# +cexception_dir = include_directories('.') + +cexception_lib = static_library(meson.project_name(), + files('CException.c'), + include_directories : cexception_dir) diff --git a/CAN_App/vendor/ceedling/vendor/cmock/config/production_environment.rb b/CAN_App/vendor/ceedling/vendor/cmock/config/production_environment.rb new file mode 100644 index 0000000..082b63f --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/config/production_environment.rb @@ -0,0 +1,12 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# Setup our load path: +[ + 'lib' +].each do |dir| + $:.unshift(File.join(__dir__ + '/../', dir)) +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/config/test_environment.rb b/CAN_App/vendor/ceedling/vendor/cmock/config/test_environment.rb new file mode 100644 index 0000000..aeae3a3 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/config/test_environment.rb @@ -0,0 +1,16 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# Setup our load path: +[ + './lib', + './vendor/behaviors/lib', + './vendor/hardmock/lib', + './vendor/unity/auto/', + './test/system/' +].each do |dir| + $:.unshift(File.join(File.expand_path(File.dirname(__FILE__) + '/../'), dir)) +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock.rb new file mode 100644 index 0000000..72f8641 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock.rb @@ -0,0 +1,111 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +['../config/production_environment', + 'cmock_header_parser', + 'cmock_generator', + 'cmock_file_writer', + 'cmock_config', + 'cmock_plugin_manager', + 'cmock_generator_utils', + 'cmock_unityhelper_parser'].each { |req| require "#{__dir__}/#{req}" } + +class CMock + def initialize(options = nil) + cm_config = CMockConfig.new(options) + cm_unityhelper = CMockUnityHelperParser.new(cm_config) + cm_writer = CMockFileWriter.new(cm_config) + cm_gen_utils = CMockGeneratorUtils.new(cm_config, + :unity_helper => cm_unityhelper) + cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils) + @cm_parser = CMockHeaderParser.new(cm_config) + @cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils, + cm_gen_plugins) + @silent = (cm_config.verbosity < 2) + end + + def setup_mocks(files, folder = nil) + [files].flatten.each do |src| + generate_mock(src, folder) + end + end + + def setup_skeletons(files) + [files].flatten.each do |src| + generate_skeleton src + end + end + + private ############################### + + def generate_mock(src, folder) + name = File.basename(src, '.*') + ext = File.extname(src) + puts "Creating mock for #{name}..." unless @silent + @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder) + end + + def generate_skeleton(src) + name = File.basename(src, '.*') + puts "Creating skeleton for #{name}..." unless @silent + @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src))) + end +end + +def option_maker(options, key, val) + options ||= {} + options[key.to_sym] = + if val.chr == ':' + val[1..-1].to_sym + elsif val.include? ';' + val.split(';') + elsif val == 'true' + true + elsif val == 'false' + false + elsif val =~ /^\d+$/ + val.to_i + else + val + end + options +end + +# Command Line Support ############################### + +if $0 == __FILE__ + usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock" + + unless ARGV[0] + puts usage + exit 1 + end + + options = {} + filelist = [] + ARGV.each do |arg| + if arg =~ /^-o\"?([a-zA-Z0-9@._\\\/:\s]+)\"?/ + options.merge! CMockConfig.load_config_file_from_yaml(arg.gsub(/^-o/, '')) + elsif arg == '--skeleton' + options[:skeleton] = true + elsif arg =~ /^--strippables=\"?(.*)\"?/ + # --strippables are dealt with separately since the user is allowed to + # enter any valid regular expression as argument + options = option_maker(options, 'strippables', Regexp.last_match(1)) + elsif arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]*)\"?/x + options = option_maker(options, Regexp.last_match(1), + Regexp.last_match(2)) + else + filelist << arg + end + end + + if options[:skeleton] + CMock.new(options).setup_skeletons(filelist) + else + CMock.new(options).setup_mocks(filelist) + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_config.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_config.rb new file mode 100644 index 0000000..716a0c5 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_config.rb @@ -0,0 +1,174 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockConfig + CMOCK_DEFAULT_OPTIONS = + { + :framework => :unity, + :mock_path => 'mocks', + :mock_prefix => 'Mock', + :mock_suffix => '', + :skeleton_path => '', + :weak => '', + :subdir => nil, + :plugins => [], + :strippables => ['(?:__attribute__\s*\(+.*?\)+)'], + :attributes => %w[__ramfunc __irq __fiq register extern], + :c_calling_conventions => %w[__stdcall __cdecl __fastcall], + :enforce_strict_ordering => false, + :fail_on_unexpected_calls => true, + :unity_helper_path => false, + :treat_as => {}, + :treat_as_array => {}, + :treat_as_void => [], + :memcmp_if_unknown => true, + :when_no_prototypes => :warn, # the options being :ignore, :warn, or :error + :when_ptr => :compare_data, # the options being :compare_ptr, :compare_data, or :smart + :verbosity => 2, # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose + :treat_externs => :exclude, # the options being :include or :exclude + :treat_inlines => :exclude, # the options being :include or :exclude + :callback_include_count => true, + :callback_after_arg_check => false, + :includes => nil, + :includes_h_pre_orig_header => nil, + :includes_h_post_orig_header => nil, + :includes_c_pre_header => nil, + :includes_c_post_header => nil, + :orig_header_include_fmt => '#include "%s"', + :array_size_type => [], + :array_size_name => 'size|len', + :skeleton => false, + :exclude_setjmp_h => false, + + # Format to look for inline functions. + # This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static") + # There are several possibilities: + # - sometimes they appear together, sometimes individually, + # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff), + # so we check for word boundaries when searching for them + # - We first remove "static inline" combinations and boil down to single inline or static statements + :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] # Last part (\s*) is just to remove whitespaces (only to prettify the output) + }.freeze + + def initialize(options = nil) + case options + when NilClass then options = CMOCK_DEFAULT_OPTIONS.dup + when String then options = CMOCK_DEFAULT_OPTIONS.dup.merge(load_config_file_from_yaml(options)) + when Hash then options = CMOCK_DEFAULT_OPTIONS.dup.merge(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + + # do some quick type verification + %i[plugins attributes treat_as_void].each do |opt| + unless options[opt].class == Array + options[opt] = [] + puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1 + end + end + %i[includes includes_h_pre_orig_header includes_h_post_orig_header includes_c_pre_header includes_c_post_header].each do |opt| + unless options[opt].nil? || (options[opt].class == Array) + options[opt] = [] + puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1 + end + end + options[:unity_helper_path] ||= options[:unity_helper] + options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String + + if options[:unity_helper_path] + require 'pathname' + includes1 = options[:includes_c_post_header] || [] + includes2 = options[:unity_helper_path].map do |path| + Pathname(path).relative_path_from(Pathname(options[:mock_path])).to_s + end + options[:includes_c_post_header] = (includes1 + includes2).uniq + end + + options[:plugins].compact! + options[:plugins].map!(&:to_sym) + @options = options + + treat_as_map = standard_treat_as_map # .clone + treat_as_map.merge!(@options[:treat_as]) + @options[:treat_as] = treat_as_map + + @options.each_key do |key| + unless methods.include?(key) + eval("def #{key}() return @options[:#{key}] end") + end + end + end + + def load_config_file_from_yaml(yaml_filename) + self.class.load_config_file_from_yaml yaml_filename + end + + def self.load_config_file_from_yaml(yaml_filename) + require 'yaml' + require 'fileutils' + YAML.load_file(yaml_filename)[:cmock] + end + + def path(new_path) + @src_path = new_path + end + + def load_unity_helper + return nil unless @options[:unity_helper_path] + + @options[:unity_helper_path].inject('') do |unity_helper, filename| + unity_helper + "\n" + File.new(filename).read + end + end + + def standard_treat_as_map + { + 'int' => 'INT', + 'char' => 'INT8', + 'short' => 'INT16', + 'long' => 'INT', + 'int8' => 'INT8', + 'int16' => 'INT16', + 'int32' => 'INT', + 'int8_t' => 'INT8', + 'int16_t' => 'INT16', + 'int32_t' => 'INT', + 'INT8_T' => 'INT8', + 'INT16_T' => 'INT16', + 'INT32_T' => 'INT', + 'bool' => 'INT', + 'bool_t' => 'INT', + 'BOOL' => 'INT', + 'BOOL_T' => 'INT', + 'unsigned int' => 'HEX32', + 'unsigned long' => 'HEX32', + 'uint32' => 'HEX32', + 'uint32_t' => 'HEX32', + 'UINT32' => 'HEX32', + 'UINT32_T' => 'HEX32', + 'void*' => 'HEX8_ARRAY', + 'void const*' => 'HEX8_ARRAY', + 'const void*' => 'HEX8_ARRAY', + 'unsigned short' => 'HEX16', + 'uint16' => 'HEX16', + 'uint16_t' => 'HEX16', + 'UINT16' => 'HEX16', + 'UINT16_T' => 'HEX16', + 'unsigned char' => 'HEX8', + 'uint8' => 'HEX8', + 'uint8_t' => 'HEX8', + 'UINT8' => 'HEX8', + 'UINT8_T' => 'HEX8', + 'char*' => 'STRING', + 'char const*' => 'STRING', + 'const char*' => 'STRING', + 'pCHAR' => 'STRING', + 'cstring' => 'STRING', + 'CSTRING' => 'STRING', + 'float' => 'FLOAT', + 'double' => 'FLOAT' + } + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb new file mode 100644 index 0000000..f30c44b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb @@ -0,0 +1,47 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockFileWriter + attr_reader :config + + def initialize(config) + @config = config + end + + def create_subdir(subdir) + require 'fileutils' + FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/") + FileUtils.mkdir_p "#{@config.mock_path}/#{subdir + '/' if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{subdir + '/' if subdir}") + end + + def create_file(filename, subdir) + raise "Where's the block of data to create?" unless block_given? + + full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" + full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" + File.open(full_file_name_temp, 'w') do |file| + yield(file, filename) + end + update_file(full_file_name_done, full_file_name_temp) + end + + def append_file(filename, subdir) + raise "Where's the block of data to create?" unless block_given? + + full_file_name = "#{@config.skeleton_path}/#{subdir + '/' if subdir}#{filename}" + File.open(full_file_name, 'a') do |file| + yield(file, filename) + end + end + + private ################################### + + def update_file(dest, src) + require 'fileutils' + FileUtils.rm(dest, :force => true) + FileUtils.mv(src, dest) + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb new file mode 100644 index 0000000..6ed5110 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb @@ -0,0 +1,368 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGenerator + attr_accessor :config, :file_writer, :module_name, :module_ext, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered + + def initialize(config, file_writer, utils, plugins) + @file_writer = file_writer + @utils = utils + @plugins = plugins + @config = config + @prefix = @config.mock_prefix + @suffix = @config.mock_suffix + @weak = @config.weak + @include_inline = @config.treat_inlines + @ordered = @config.enforce_strict_ordering + @framework = @config.framework.to_s + @fail_on_unexpected_calls = @config.fail_on_unexpected_calls + @exclude_setjmp_h = @config.exclude_setjmp_h + @subdir = @config.subdir + + @includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_c_pre_header = (@config.includes_c_pre_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_c_post_header = (@config.includes_c_post_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + + here = File.dirname __FILE__ + unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling + unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock + # path to Unity as specified by env var + unity_path_in_env = ENV.key?('UNITY_DIR') ? File.expand_path(ENV.fetch('UNITY_DIR')) : nil + + if unity_path_in_env && File.exist?(unity_path_in_env) + require "#{unity_path_in_env}/auto/type_sanitizer" + elsif File.exist? unity_path_in_ceedling + require "#{unity_path_in_ceedling}/auto/type_sanitizer" + elsif File.exist? unity_path_in_cmock + require "#{unity_path_in_cmock}/auto/type_sanitizer" + else + raise 'Failed to find an instance of Unity to pull in type_sanitizer module!' + end + end + + def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil) + # determine the name for our new mock + mock_name = @prefix + module_name + @suffix + + # determine the folder our mock will reside + mock_folder = if folder && @subdir + File.join(@subdir, folder) + elsif @subdir + @subdir + else + folder + end + + # adds a trailing slash to the folder output + mock_folder = File.join(mock_folder, '') if mock_folder + + # create out mock project from incoming data + mock_project = { + :module_name => module_name, + :module_ext => (module_ext || '.h'), + :mock_name => mock_name, + :clean_name => TypeSanitizer.sanitize_c_identifier(mock_name), + :folder => mock_folder, + :parsed_stuff => parsed_stuff, + :skeleton => false + } + + create_mock_subdir(mock_project) + create_mock_header_file(mock_project) + create_mock_source_file(mock_project) + end + + def create_skeleton(module_name, parsed_stuff) + mock_project = { + :module_name => module_name, + :module_ext => '.h', + :parsed_stuff => parsed_stuff, + :skeleton => true + } + + create_skeleton_source_file(mock_project) + end + + private if $ThisIsOnlyATest.nil? ############################## + + def create_mock_subdir(mock_project) + @file_writer.create_subdir(mock_project[:folder]) + end + + def create_using_statement(file, function) + file << "using namespace #{function[:namespace].join('::')};\n" unless function[:namespace].empty? + end + + def create_mock_header_file(mock_project) + if @include_inline == :include + @file_writer.create_file(mock_project[:module_name] + (mock_project[:module_ext]), mock_project[:folder]) do |file, _filename| + file << mock_project[:parsed_stuff][:normalized_source] + end + end + + @file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename| + create_mock_header_header(file, filename, mock_project) + create_mock_header_service_call_declarations(file, mock_project) + create_typedefs(file, mock_project) + mock_project[:parsed_stuff][:functions].each do |function| + create_using_statement(file, function) + file << @plugins.run(:mock_function_declarations, function) + end + create_mock_header_footer(file) + end + end + + def create_mock_source_file(mock_project) + @file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename| + create_source_header_section(file, filename, mock_project) + create_instance_structure(file, mock_project) + create_extern_declarations(file) + create_mock_verify_function(file, mock_project) + create_mock_init_function(file, mock_project) + create_mock_destroy_function(file, mock_project) + mock_project[:parsed_stuff][:functions].each do |function| + create_mock_implementation(file, function) + create_mock_interfaces(file, function) + end + end + end + + def create_skeleton_source_file(mock_project) + filename = "#{@config.mock_path}/#{@subdir + '/' if @subdir}#{mock_project[:module_name]}.c" + existing = File.exist?(filename) ? File.read(filename) : '' + @file_writer.append_file(mock_project[:module_name] + '.c', @subdir) do |file, fullname| + blank_project = mock_project.clone + blank_project[:parsed_stuff] = { :functions => [] } + create_source_header_section(file, fullname, blank_project) if existing.empty? + mock_project[:parsed_stuff][:functions].each do |function| + create_function_skeleton(file, function, existing) + end + end + end + + def create_mock_header_header(file, _filename, mock_project) + define_name = mock_project[:clean_name].upcase + orig_filename = (mock_project[:folder] || '') + mock_project[:module_name] + mock_project[:module_ext] + file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" + file << "#ifndef _#{define_name}_H\n" + file << "#define _#{define_name}_H\n\n" + file << "#include \"#{@framework}.h\"\n" + @includes_h_pre_orig_header.each { |inc| file << "#include #{inc}\n" } + file << @config.orig_header_include_fmt.gsub(/%s/, orig_filename.to_s) + "\n" + @includes_h_post_orig_header.each { |inc| file << "#include #{inc}\n" } + plugin_includes = @plugins.run(:include_files) + file << plugin_includes unless plugin_includes.empty? + file << "\n" + file << "/* Ignore the following warnings, since we are copying code */\n" + file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + file << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + file << "#pragma GCC diagnostic push\n" + file << "#endif\n" + file << "#if !defined(__clang__)\n" + file << "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + file << "#endif\n" + file << "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + file << "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + file << "#endif\n" + file << "\n" + end + + def create_typedefs(file, mock_project) + file << "\n" + mock_project[:parsed_stuff][:typedefs].each { |typedef| file << "#{typedef}\n" } + file << "\n\n" + end + + def create_mock_header_service_call_declarations(file, mock_project) + file << "void #{mock_project[:clean_name]}_Init(void);\n" + file << "void #{mock_project[:clean_name]}_Destroy(void);\n" + file << "void #{mock_project[:clean_name]}_Verify(void);\n\n" + end + + def create_mock_header_footer(header) + header << "\n" + header << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + header << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + header << "#pragma GCC diagnostic pop\n" + header << "#endif\n" + header << "#endif\n" + header << "\n" + header << "#endif\n" + end + + def create_source_header_section(file, filename, mock_project) + header_file = (mock_project[:folder] || '') + filename.gsub('.c', mock_project[:module_ext]) + file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty? + file << "#include <string.h>\n" + file << "#include <stdlib.h>\n" + unless @exclude_setjmp_h + file << "#include <setjmp.h>\n" + end + file << "#include \"cmock.h\"\n" + @includes_c_pre_header.each { |inc| file << "#include #{inc}\n" } + file << "#include \"#{header_file}\"\n" + @includes_c_post_header.each { |inc| file << "#include #{inc}\n" } + file << "\n" + strs = [] + mock_project[:parsed_stuff][:functions].each do |func| + strs << func[:name] + func[:args].each { |arg| strs << arg[:name] } + end + strs.uniq.sort.each do |str| + file << "static const char* CMockString_#{str} = \"#{str}\";\n" + end + file << "\n" + end + + def create_instance_structure(file, mock_project) + functions = mock_project[:parsed_stuff][:functions] + functions.each do |function| + file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n" + file << " UNITY_LINE_TYPE LineNumber;\n" + file << @plugins.run(:instance_typedefs, function) + file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n" + end + file << "static struct #{mock_project[:clean_name]}Instance\n{\n" + if functions.empty? + file << " unsigned char placeHolder;\n" + end + functions.each do |function| + file << @plugins.run(:instance_structure, function) + file << " CMOCK_MEM_INDEX_TYPE #{function[:name]}_CallInstance;\n" + end + file << "} Mock;\n\n" + end + + def create_extern_declarations(file) + unless @exclude_setjmp_h + file << "extern jmp_buf AbortFrame;\n" + end + if @ordered + file << "extern int GlobalExpectCount;\n" + file << "extern int GlobalVerifyOrder;\n" + end + file << "\n" + end + + def create_mock_verify_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Verify(void)\n{\n" + verifications = mock_project[:parsed_stuff][:functions].collect do |function| + v = @plugins.run(:mock_verify, function) + v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v] + end.join + unless verifications.empty? + file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" + file << " CMOCK_MEM_INDEX_TYPE call_instance;\n" + file << verifications + end + file << "}\n\n" + end + + def create_mock_init_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Init(void)\n{\n" + file << " #{mock_project[:clean_name]}_Destroy();\n" + file << "}\n\n" + end + + def create_mock_destroy_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Destroy(void)\n{\n" + file << " CMock_Guts_MemFreeAll();\n" + file << " memset(&Mock, 0, sizeof(Mock));\n" + file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_destroy, function) }.join + + unless @fail_on_unexpected_calls + file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_ignore, function) }.join + end + + if @ordered + file << " GlobalExpectCount = 0;\n" + file << " GlobalVerifyOrder = 0;\n" + end + file << "}\n\n" + end + + def create_mock_implementation(file, function) + # prepare return value and arguments + function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") + + (function[:return][:type]) + + (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') + args_string = function[:args_string] + args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil? + + # Encapsulate in namespace(s) if applicable + function[:namespace].each do |ns| + file << "namespace #{ns} {\n" + end + + # Determine class prefix (if any) + cls_pre = '' + unless function[:class].nil? + cls_pre = "#{function[:class]}::" + end + + # Create mock function + unless @weak.empty? + file << "#if defined (__IAR_SYSTEMS_ICC__)\n" + file << "#pragma weak #{function[:unscoped_name]}\n" + file << "#else\n" + file << "#{function_mod_and_rettype} #{function[:unscoped_name]}(#{args_string}) #{weak};\n" + file << "#endif\n\n" + end + file << "#{function_mod_and_rettype} #{cls_pre}#{function[:unscoped_name]}(#{args_string})\n" + file << "{\n" + file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" + file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n" + file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" + file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n" + file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" + file << @plugins.run(:mock_implementation_precheck, function) + file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n" + file << " cmock_line = cmock_call_instance->LineNumber;\n" + if @ordered + file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n" + file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n" + file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n" + file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate);\n" + end + file << @plugins.run(:mock_implementation, function) + file << " UNITY_CLR_DETAILS();\n" + file << " return cmock_call_instance->ReturnVal;\n" unless function[:return][:void?] + file << "}\n" + + # Close any namespace(s) opened above + function[:namespace].each do + file << "}\n" + end + + file << "\n" + end + + def create_mock_interfaces(file, function) + file << @utils.code_add_argument_loader(function) + file << @plugins.run(:mock_interfaces, function) + end + + def create_function_skeleton(file, function, existing) + # prepare return value and arguments + function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") + + (function[:return][:type]) + + (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') + args_string = function[:args_string] + args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil? + + decl = "#{function_mod_and_rettype} #{function[:name]}(#{args_string})" + + return if existing.include?(decl) + + file << "#{decl}\n" + file << "{\n" + file << " /*TODO: Implement Me!*/\n" + function[:args].each { |arg| file << " (void)#{arg[:name]};\n" } + file << " return (#{(function[:return][:type])})0;\n" unless function[:return][:void?] + file << "}\n\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb new file mode 100644 index 0000000..a9864ab --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb @@ -0,0 +1,63 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginArray + attr_reader :priority + attr_accessor :config, :utils, :unity_helper, :ordered + def initialize(config, utils) + @config = config + @ptr_handling = @config.when_ptr + @ordered = @config.enforce_strict_ordering + @utils = utils + @unity_helper = @utils.helpers[:unity_helper] + @priority = 8 + end + + def instance_typedefs(function) + function[:args].inject('') do |all, arg| + arg[:ptr?] ? all + " int Expected_#{arg[:name]}_Depth;\n" : all + end + end + + def mock_function_declarations(function) + return nil unless function[:contains_ptr?] + + args_call = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : (m[:name]).to_s }.join(', ') + args_string = function[:args].map do |m| + type = @utils.arg_type_with_const(m) + m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + end.join(', ') + if function[:return][:void?] + return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" \ + "void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n" + else + return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n" + end + end + + def mock_interfaces(function) + return nil unless function[:contains_ptr?] + + lines = [] + func_name = function[:name] + args_string = function[:args].map do |m| + type = @utils.arg_type_with_const(m) + m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + end.join(', ') + call_string = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name] }.join(', ') + lines << if function[:return][:void?] + "void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n" + else + "void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n" + end + lines << "{\n" + lines << @utils.code_add_base_expectation(func_name) + lines << " CMockExpectParameters_#{func_name}(cmock_call_instance, #{call_string});\n" + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless function[:return][:void?] + lines << "}\n\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb new file mode 100644 index 0000000..6ba8e9b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb @@ -0,0 +1,88 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginCallback + attr_accessor :include_count + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 6 + + @include_count = @config.callback_include_count + end + + def instance_structure(function) + func_name = function[:name] + " char #{func_name}_CallbackBool;\n" \ + " CMOCK_#{func_name}_CALLBACK #{func_name}_CallbackFunctionPointer;\n" \ + " int #{func_name}_CallbackCalls;\n" + end + + def mock_function_declarations(function) + func_name = function[:name] + return_type = function[:return][:type] + action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub' + style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2) + styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"] + "typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ + "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \ + "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \ + "#define #{func_name}_StubWithCallback #{func_name}_#{action}\n" + end + + def generate_call(function) + args = function[:args].map { |m| m[:name] } + args << "Mock.#{function[:name]}_CallbackCalls++" if @include_count + "Mock.#{function[:name]}_CallbackFunctionPointer(#{args.join(', ')})" + end + + def mock_implementation(function) + " if (Mock.#{function[:name]}_CallbackFunctionPointer != NULL)\n {\n" + + if function[:return][:void?] + " #{generate_call(function)};\n }\n" + else + " cmock_call_instance->ReturnVal = #{generate_call(function)};\n }\n" + end + end + + def mock_implementation_precheck(function) + " if (!Mock.#{function[:name]}_CallbackBool &&\n" \ + " Mock.#{function[:name]}_CallbackFunctionPointer != NULL)\n {\n" + + if function[:return][:void?] + " #{generate_call(function)};\n" \ + " UNITY_CLR_DETAILS();\n" \ + " return;\n }\n" + else + " #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \ + " UNITY_CLR_DETAILS();\n" \ + " return cmock_cb_ret;\n }\n" + end + end + + def mock_interfaces(function) + func_name = function[:name] + has_ignore = @config.plugins.include? :ignore + lines = '' + lines << "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore + lines << " Mock.#{func_name}_CallbackBool = (char)1;\n" + lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" + lines << "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore + lines << " Mock.#{func_name}_CallbackBool = (char)0;\n" + lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" + end + + def mock_verify(function) + func_name = function[:name] + " if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n {\n" \ + " call_instance = CMOCK_GUTS_NONE;\n" \ + " (void)call_instance;\n }\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb new file mode 100644 index 0000000..7e2d7b6 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginCexception + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 7 + raise 'Error: cexception is not supported without setjmp support' if @config.exclude_setjmp_h + end + + def include_files + "#include \"CException.h\"\n" + end + + def instance_typedefs(_function) + " CEXCEPTION_T ExceptionToThrow;\n" + end + + def mock_function_declarations(function) + if function[:args_string] == 'void' + "#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" \ + "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n" + else + "#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" \ + "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n" + end + end + + def mock_implementation(_function) + " if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" \ + " UNITY_CLR_DETAILS();\n" \ + " Throw(cmock_call_instance->ExceptionToThrow);\n }\n" + end + + def mock_interfaces(function) + arg_insert = function[:args_string] == 'void' ? '' : "#{function[:args_string]}, " + ["void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n", + @utils.code_add_base_expectation(function[:name]), + @utils.code_call_argument_loader(function), + " cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n", + "}\n\n"].join + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb new file mode 100644 index 0000000..3a79c1a --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb @@ -0,0 +1,100 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginExpect + attr_reader :priority + attr_accessor :config, :utils, :unity_helper, :ordered + + def initialize(config, utils) + @config = config + @ptr_handling = @config.when_ptr + @ordered = @config.enforce_strict_ordering + @utils = utils + @unity_helper = @utils.helpers[:unity_helper] + @priority = 5 + + if @config.plugins.include? :expect_any_args + alias :mock_implementation :mock_implementation_might_check_args + else + alias :mock_implementation :mock_implementation_always_check_args + end + end + + def instance_typedefs(function) + lines = '' + lines << " #{function[:return][:type]} ReturnVal;\n" unless function[:return][:void?] + lines << " int CallOrder;\n" if @ordered + function[:args].each do |arg| + lines << " #{arg[:type]} Expected_#{arg[:name]};\n" + end + lines + end + + def mock_function_declarations(function) + if function[:args].empty? + if function[:return][:void?] + "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" \ + "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n" + else + "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + elsif function[:return][:void?] + "#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" \ + "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n" + else + "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n" + end + end + + def mock_implementation_always_check_args(function) + lines = '' + function[:args].each do |arg| + lines << @utils.code_verify_an_arg_expectation(function, arg) + end + lines + end + + def mock_implementation_might_check_args(function) + return '' if function[:args].empty? + + lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n" + function[:args].each do |arg| + lines << @utils.code_verify_an_arg_expectation(function, arg) + end + lines << " }\n" + lines + end + + def mock_interfaces(function) + lines = '' + func_name = function[:name] + lines << if function[:return][:void?] + if function[:args_string] == 'void' + "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n" + else + "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n" + end + elsif function[:args_string] == 'void' + "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + else + "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n" + end + lines << @utils.code_add_base_expectation(func_name) + lines << @utils.code_call_argument_loader(function) + lines << @utils.code_assign_argument_quickly('cmock_call_instance->ReturnVal', function[:return]) unless function[:return][:void?] + lines << "}\n\n" + end + + def mock_verify(function) + " if (CMOCK_GUTS_NONE != call_instance)\n" \ + " {\n" \ + " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" \ + " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess);\n" \ + " }\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb new file mode 100644 index 0000000..0fc88e1 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginExpectAnyArgs + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 3 + end + + def instance_typedefs(_function) + " char ExpectAnyArgsBool;\n" + end + + def mock_function_declarations(function) + if function[:args].empty? + '' + elsif function[:return][:void?] + "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" \ + "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n" + else + "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + end + + def mock_interfaces(function) + lines = '' + unless function[:args].empty? + lines << if function[:return][:void?] + "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n" + else + "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + end + lines << @utils.code_add_base_expectation(function[:name], true) + unless function[:return][:void?] + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" + end + lines << " cmock_call_instance->ExpectAnyArgsBool = (char)1;\n" + lines << "}\n\n" + end + lines + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb new file mode 100644 index 0000000..b292f3d --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb @@ -0,0 +1,88 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginIgnore + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 2 + end + + def instance_structure(function) + if function[:return][:void?] + " char #{function[:name]}_IgnoreBool;\n" + else + " char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" + end + end + + def mock_function_declarations(function) + lines = if function[:return][:void?] + "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \ + "void #{function[:name]}_CMockIgnore(void);\n" + else + "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + + # Add stop ignore function. it does not matter if there are any args + lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \ + "void #{function[:name]}_CMockStopIgnore(void);\n" + lines + end + + def mock_implementation_precheck(function) + lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" + lines << " UNITY_CLR_DETAILS();\n" + if function[:return][:void?] + lines << " return;\n }\n" + else + retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal') + lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" + lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?] + lines << " return cmock_call_instance->ReturnVal;\n }\n" + end + lines + end + + def mock_interfaces(function) + lines = '' + lines << if function[:return][:void?] + "void #{function[:name]}_CMockIgnore(void)\n{\n" + else + "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + end + unless function[:return][:void?] + lines << @utils.code_add_base_expectation(function[:name], false) + end + unless function[:return][:void?] + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + lines << "}\n\n" + + # Add stop ignore function. it does not matter if there are any args + lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" + unless function[:return][:void?] + lines << " if(Mock.#{function[:name]}_IgnoreBool)\n" + lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n" + lines << "}\n\n" + end + + def mock_ignore(function) + " Mock.#{function[:name]}_IgnoreBool = (char) 1;\n" + end + + def mock_verify(function) + func_name = function[:name] + " if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb new file mode 100644 index 0000000..d55e84c --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb @@ -0,0 +1,42 @@ +class CMockGeneratorPluginIgnoreArg + attr_reader :priority + attr_accessor :utils + + def initialize(_config, utils) + @utils = utils + @priority = 10 + end + + def instance_typedefs(function) + lines = '' + function[:args].each do |arg| + lines << " char IgnoreArg_#{arg[:name]};\n" + end + lines + end + + def mock_function_declarations(function) + lines = '' + function[:args].each do |arg| + lines << "#define #{function[:name]}_IgnoreArg_#{arg[:name]}()" + lines << " #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(__LINE__)\n" + lines << "void #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line);\n" + end + lines + end + + def mock_interfaces(function) + lines = [] + func_name = function[:name] + function[:args].each do |arg| + lines << "void #{func_name}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line)\n" + lines << "{\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ + "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp);\n" + lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 1;\n" + lines << "}\n\n" + end + lines + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb new file mode 100644 index 0000000..9196ede --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb @@ -0,0 +1,85 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginIgnoreStateless + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 2 + end + + def instance_structure(function) + if function[:return][:void?] + " char #{function[:name]}_IgnoreBool;\n" + else + " char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" + end + end + + def mock_function_declarations(function) + lines = if function[:return][:void?] + "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \ + "void #{function[:name]}_CMockIgnore(void);\n" + else + "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(cmock_retval)\n" \ + "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]});\n" + end + + # Add stop ignore function. it does not matter if there are any args + lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \ + "void #{function[:name]}_CMockStopIgnore(void);\n" + lines + end + + def mock_implementation_precheck(function) + lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" + lines << " UNITY_CLR_DETAILS();\n" + if function[:return][:void?] + lines << " return;\n }\n" + else + retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal') + lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" + lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?] + lines << " return cmock_call_instance->ReturnVal;\n }\n" + end + lines + end + + # this function is adjusted + def mock_interfaces(function) + lines = '' + lines << if function[:return][:void?] + "void #{function[:name]}_CMockIgnore(void)\n{\n" + else + "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]})\n{\n" + end + unless function[:return][:void?] + lines << " Mock.#{function[:name]}_CallInstance = CMOCK_GUTS_NONE;\n" + lines << " Mock.#{function[:name]}_FinalReturn = cmock_to_return;\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + lines << "}\n\n" + + # Add stop ignore function. it does not matter if there are any args + lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" + lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n" + lines << "}\n\n" + + lines + end + + def mock_ignore(function) + " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + end + + def mock_verify(function) + func_name = function[:name] + " if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb new file mode 100644 index 0000000..96b2003 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -0,0 +1,79 @@ +class CMockGeneratorPluginReturnThruPtr + attr_reader :priority + attr_accessor :utils + + def initialize(_config, utils) + @utils = utils + @priority = 9 + end + + def instance_typedefs(function) + lines = '' + function[:args].each do |arg| + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << " char ReturnThruPtr_#{arg[:name]}_Used;\n" + lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n" + lines << " size_t ReturnThruPtr_#{arg[:name]}_Size;\n" + end + lines + end + + def mock_function_declarations(function) + lines = '' + function[:args].each do |arg| + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})" + # If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise + # we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size + lines << if arg[:type][-1] == '*' + " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n" + else + " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" + end + lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)" + lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_len * sizeof(*#{arg[:name]}))\n" + lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)" + lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n" + lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, size_t cmock_size);\n" + end + lines + end + + def mock_interfaces(function) + lines = [] + func_name = function[:name] + function[:args].each do |arg| + arg_name = arg[:name] + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, size_t cmock_size)\n" + lines << "{\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ + "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n" + lines << "}\n\n" + end + lines + end + + def mock_implementation(function) + lines = [] + function[:args].each do |arg| + arg_name = arg[:name] + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n" + lines << " {\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" + lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" + lines << " }\n" + end + lines + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb new file mode 100644 index 0000000..ecbc37e --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb @@ -0,0 +1,250 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorUtils + attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception + + def initialize(config, helpers = {}) + @config = config + @ptr_handling = @config.when_ptr + @ordered = @config.enforce_strict_ordering + @arrays = @config.plugins.include? :array + @cexception = @config.plugins.include? :cexception + @expect_any = @config.plugins.include? :expect_any_args + @return_thru_ptr = @config.plugins.include? :return_thru_ptr + @ignore_arg = @config.plugins.include? :ignore_arg + @ignore = @config.plugins.include? :ignore + @ignore_stateless = @config.plugins.include? :ignore_stateless + @treat_as = @config.treat_as + @helpers = helpers + end + + def self.arg_type_with_const(arg) + # Restore any "const" that was removed in header parsing + if arg[:type].include?('*') + arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type] + else + arg[:const?] ? "const #{arg[:type]}" : arg[:type] + end + end + + def arg_type_with_const(arg) + self.class.arg_type_with_const(arg) + end + + def code_verify_an_arg_expectation(function, arg) + if @arrays + case @ptr_handling + when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg) + when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg) + when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option." + end + else + code_verify_an_arg_expectation_with_no_arrays(function, arg) + end + end + + def code_add_base_expectation(func_name, global_ordering_supported = true) + lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" + lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if @ignore || @ignore_stateless + lines << " cmock_call_instance->LineNumber = cmock_line;\n" + lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if @ordered && global_ordering_supported + lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if @cexception + lines << " cmock_call_instance->ExpectAnyArgsBool = (char)0;\n" if @expect_any + lines + end + + def code_add_an_arg_expectation(arg, depth = 1) + lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg) + lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if @arrays && (depth.class == String) + lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if @ignore_arg + lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if @return_thru_ptr && ptr_or_str?(arg[:type]) && !(arg[:const?]) + lines + end + + def code_assign_argument_quickly(dest, arg) + if arg[:ptr?] || @treat_as.include?(arg[:type]) + " #{dest} = #{arg[:name]};\n" + else + assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1" + comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */" + " memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \ + " sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n" + end + end + + def code_add_argument_loader(function) + if function[:args_string] != 'void' + if @arrays + args_string = function[:args].map do |m| + type = arg_type_with_const(m) + m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + end.join(', ') + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" \ + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" + + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1)) } + + "}\n\n" + else + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" \ + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" + + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg) } + + "}\n\n" + end + else + '' + end + end + + def code_call_argument_loader(function) + if function[:args_string] != 'void' + args = function[:args].map do |m| + if @arrays && m[:ptr?] && !(m[:array_data?]) + "#{m[:name]}, 1" + elsif @arrays && m[:array_size?] + "#{m[:name]}, #{m[:name]}" + else + m[:name] + end + end + " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" + else + '' + end + end + + def ptr_or_str?(arg_type) + (arg_type.include?('*') || + @treat_as.fetch(arg_type, '').include?('*')) + end + + # private ###################### + + def lookup_expect_type(_function, arg) + c_type = arg[:type] + arg_name = arg[:name] + expected = "cmock_call_instance->Expected_#{arg_name}" + ignore = "cmock_call_instance->IgnoreArg_#{arg_name}" + unity_func = if (arg[:ptr?]) && ((c_type =~ /\*\*/) || (@ptr_handling == :compare_ptr)) + ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] + else + @helpers.nil? || @helpers[:unity_helper].nil? ? ['UNITY_TEST_ASSERT_EQUAL', ''] : @helpers[:unity_helper].get_helper(c_type) + end + [c_type, arg_name, expected, ignore, unity_func[0], unity_func[1]] + end + + def code_verify_an_arg_expectation_with_no_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end + + def code_verify_an_arg_expectation_with_normal_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end + + def code_verify_an_arg_expectation_with_smart_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '') + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '') + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb new file mode 100644 index 0000000..9730bf4 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb @@ -0,0 +1,623 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockHeaderParser + attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns + + def initialize(cfg) + @c_strippables = cfg.strippables + @c_attr_noconst = cfg.attributes.uniq - ['const'] + @c_attributes = ['const'] + c_attr_noconst + @c_calling_conventions = cfg.c_calling_conventions.uniq + @treat_as_array = cfg.treat_as_array + @treat_as_void = (['void'] + cfg.treat_as_void).uniq + @function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)' + @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m + @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq + @array_size_name = cfg.array_size_name + @array_size_type = (%w[int size_t] + cfg.array_size_type).uniq + @when_no_prototypes = cfg.when_no_prototypes + @local_as_void = @treat_as_void + @verbosity = cfg.verbosity + @treat_externs = cfg.treat_externs + @treat_inlines = cfg.treat_inlines + @inline_function_patterns = cfg.inline_function_patterns + @c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs + @c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines + end + + def parse(name, source) + parse_project = { + :module_name => name.gsub(/\W/, ''), + :typedefs => [], + :functions => [], + :normalized_source => nil + } + + function_names = [] + + all_funcs = parse_functions(import_source(source, parse_project)).map { |item| [item] } + all_funcs += parse_cpp_functions(import_source(source, parse_project, true)) + all_funcs.map do |decl| + func = parse_declaration(parse_project, *decl) + unless function_names.include? func[:name] + parse_project[:functions] << func + function_names << func[:name] + end + end + + parse_project[:normalized_source] = if @treat_inlines == :include + transform_inline_functions(source) + else + '' + end + + { :includes => nil, + :functions => parse_project[:functions], + :typedefs => parse_project[:typedefs], + :normalized_source => parse_project[:normalized_source] } + end + + private if $ThisIsOnlyATest.nil? ################ + + # Remove C/C++ comments from a string + # +source+:: String which will have the comments removed + def remove_comments_from_source(source) + # remove comments (block and line, in three steps to ensure correct precedence) + source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source.gsub!(/\/\*.*?\*\//m, '') # remove block comments + source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) + end + + def remove_nested_pairs_of_braces(source) + # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) + if RUBY_VERSION.split('.')[0].to_i > 1 + # we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. + r = '\\{([^\\{\\}]*|\\g<0>)*\\}' + source.gsub!(/#{r}/m, '{ }') + else + while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }') + end + end + + source + end + + # Return the number of pairs of braces/square brackets in the function provided by the user + # +source+:: String containing the function to be processed + def count_number_of_pairs_of_braces_in_function(source) + is_function_start_found = false + curr_level = 0 + total_pairs = 0 + + source.each_char do |c| + if c == '{' + curr_level += 1 + total_pairs += 1 + is_function_start_found = true + elsif c == '}' + curr_level -= 1 + end + + break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body + end + + if curr_level != 0 + total_pairs = 0 # Something is fishy about this source, not enough closing braces? + end + + total_pairs + end + + # Transform inline functions to regular functions in the source by the user + # +source+:: String containing the source to be processed + def transform_inline_functions(source) + inline_function_regex_formats = [] + square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets + + # Convert user provided string patterns to regex + # Use word bounderies before and after the user regex to limit matching to actual word iso part of a word + @inline_function_patterns.each do |user_format_string| + user_regex = Regexp.new(user_format_string) + word_boundary_before_user_regex = /\b/ + cleanup_spaces_after_user_regex = /[ ]*\b/ + inline_function_regex_formats << Regexp.new(word_boundary_before_user_regex.source + user_regex.source + cleanup_spaces_after_user_regex.source) + end + + # let's clean up the encoding in case they've done anything weird with the characters we might find + source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) + + # Comments can contain words that will trigger the parser (static|inline|<user_defined_static_keyword>) + remove_comments_from_source(source) + + # smush multiline macros into single line (checking for continuation character at end of line '\') + # If the user uses a macro to declare an inline function, + # smushing the macros makes it easier to recognize them as a macro and if required, + # remove them later on in this function + source.gsub!(/\s*\\\s*/m, ' ') + + # Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter + # Instead, look for an inline pattern (f.e. "static inline") and parse it. + # Below is a small explanation on how the general mechanism works: + # - Everything before the match should just be copied, we don't want + # to touch anything but the inline functions. + # - Remove the implementation of the inline function (this is enclosed + # in square brackets) and replace it with ";" to complete the + # transformation to normal/non-inline function. + # To ensure proper removal of the function body, we count the number of square-bracket pairs + # and remove the pairs one-by-one. + # - Copy everything after the inline function implementation and start the parsing of the next inline function + # There are ofcourse some special cases (inline macro declarations, inline function declarations, ...) which are handled and explained below + inline_function_regex_formats.each do |format| + inspected_source = '' + regex_matched = false + loop do + inline_function_match = source.match(/#{format}/) # Search for inline function declaration + + if inline_function_match.nil? # No inline functions so nothing to do + # Join pre and post match stripped parts for the next inline function detection regex + source = inspected_source + source if regex_matched == true + break + end + + regex_matched = true + # 1. Determine if we are dealing with a user defined macro to declare inline functions + # If the end of the pre-match string is a macro-declaration-like string, + # we are dealing with a user defined macro to declare inline functions + if /(#define\s*)\z/ =~ inline_function_match.pre_match + # Remove the macro from the source + stripped_pre_match = inline_function_match.pre_match.sub(/(#define\s*)\z/, '') + stripped_post_match = inline_function_match.post_match.sub(/\A(.*[\n]?)/, '') + inspected_source += stripped_pre_match + source = stripped_post_match + next + end + + # 2. Determine if we are dealing with an inline function declaration iso function definition + # If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments), + # we are dealing with a inline function declaration + if /\A#{@function_declaration_parse_base_match}\s*;/m =~ inline_function_match.post_match + # Only remove the inline part from the function declaration, leaving the function declaration won't do any harm + inspected_source += inline_function_match.pre_match + source = inline_function_match.post_match + next + end + + # 3. If we get here, we found an inline function declaration AND inline function body. + # Remove the function body to transform it into a 'normal' function declaration. + if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match + total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match) + + break if total_pairs_to_remove == 0 # Bad source? + + inline_function_stripped = inline_function_match.post_match + + total_pairs_to_remove.times do + inline_function_stripped.sub!(/\s*#{square_bracket_pair_regex_format}/, ';') # Remove inline implementation (+ some whitespace because it's prettier) + end + inspected_source += inline_function_match.pre_match + source = inline_function_stripped + next + end + + # 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header) + # Leave this code as it is. + inspected_source += inline_function_match.pre_match + inline_function_match[0] + source = inline_function_match.post_match + end + end + + source + end + + def import_source(source, parse_project, cpp = false) + # let's clean up the encoding in case they've done anything weird with the characters we might find + source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) + + # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void + # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void + @local_as_void = @treat_as_void + void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/) + if void_types + @local_as_void += void_types.flatten.uniq.compact + end + + # If user wants to mock inline functions, + # remove the (user specific) inline keywords before removing anything else to avoid missing an inline function + if @treat_inlines == :include + @inline_function_patterns.each do |user_format_string| + source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns + end + end + + # smush multiline macros into single line (checking for continuation character at end of line '\') + source.gsub!(/\s*\\\s*/m, ' ') + + remove_comments_from_source(source) + + # remove assembler pragma sections + source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') + + # remove gcc's __attribute__ tags + source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + + # remove preprocessor statements and extern "C" + source.gsub!(/^\s*#.*/, '') + source.gsub!(/extern\s+\"C\"\s*\{/, '') + + # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them + # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes + source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs + source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces + # remove problem keywords + source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2') + source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp + + source.gsub!(/\s*=\s*['"a-zA-Z0-9_\.]+\s*/, '') # remove default value statements from argument lists + source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements + source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric + source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped + + # scan standalone function pointers and remove them, because they can just be ignored + source.gsub!(/\w+\s*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/, ';') + + # scan for functions which return function pointers, because they are a pain + source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + unless cpp # only collect once + parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});" + "#{functype} #{Regexp.last_match(2).strip}(#{Regexp.last_match(3)});" + end + end + + source = remove_nested_pairs_of_braces(source) unless cpp + + if @treat_inlines == :include + # Functions having "{ }" at this point are/were inline functions, + # User wants them in so 'disguise' them as normal functions with the ";" + source.gsub!('{ }', ';') + end + + # remove function definitions by stripping off the arguments right now + source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ';') + + # drop extra white space to make the rest go faster + source.gsub!(/^\s+/, '') # remove extra white space from beginning of line + source.gsub!(/\s+$/, '') # remove extra white space from end of line + source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens + source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens + source.gsub!(/\s+/, ' ') # remove remaining extra white space + + # split lines on semicolons and remove things that are obviously not what we are looking for + src_lines = source.split(/\s*;\s*/) + src_lines = src_lines.uniq unless cpp # must retain closing braces for class/namespace + src_lines.delete_if { |line| line.strip.empty? } # remove blank lines + src_lines.delete_if { |line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil? } # remove function pointer arrays + + unless @treat_externs == :include + src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil? } # remove extern functions + end + + unless @treat_inlines == :include + src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil? } # remove inline functions + end + + src_lines.delete_if(&:empty?) # drop empty lines + end + + # Rudimentary C++ parser - does not handle all situations - e.g.: + # * A namespace function appears after a class with private members (should be parsed) + # * Anonymous namespace (shouldn't parse anything - no matter how nested - within it) + # * A class nested within another class + def parse_cpp_functions(source) + funcs = [] + + ns = [] + pub = false + source.each do |line| + # Search for namespace, class, opening and closing braces + line.scan(/(?:(?:\b(?:namespace|class)\s+(?:\S+)\s*)?{)|}/).each do |item| + if item == '}' + ns.pop + else + token = item.strip.sub(/\s+/, ' ') + ns << token + + pub = false if token.start_with? 'class' + pub = true if token.start_with? 'namespace' + end + end + + pub = true if line =~ /public:/ + pub = false if line =~ /private:/ || line =~ /protected:/ + + # ignore non-public and non-static + next unless pub + next unless line =~ /\bstatic\b/ + + line.sub!(/^.*static/, '') + next unless line =~ @declaration_parse_matcher + + tmp = ns.reject { |item| item == '{' } + + # Identify class name, if any + cls = nil + if tmp[-1].start_with? 'class ' + cls = tmp.pop.sub(/class (\S+) {/, '\1') + end + + # Assemble list of namespaces + tmp.each { |item| item.sub!(/(?:namespace|class) (\S+) {/, '\1') } + + funcs << [line.strip.gsub(/\s+/, ' '), tmp, cls] + end + funcs + end + + def parse_functions(source) + funcs = [] + source.each { |line| funcs << line.strip.gsub(/\s+/, ' ') if line =~ @declaration_parse_matcher } + if funcs.empty? + case @when_no_prototypes + when :error + raise 'ERROR: No function prototypes found!' + when :warn + puts 'WARNING: No function prototypes found!' unless @verbosity < 1 + end + end + funcs + end + + def parse_type_and_name(arg) + # Split up words and remove known attributes. For pointer types, make sure + # to remove 'const' only when it applies to the pointer itself, not when it + # applies to the type pointed to. For non-pointer types, remove any + # occurrence of 'const'. + arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word + arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word + arg_array = arg.split + arg_info = divine_ptr_and_const(arg) + arg_info[:name] = arg_array[-1] + + attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes + attr_array = [] + type_array = [] + + arg_array[0..-2].each do |word| + if attributes.include?(word) + attr_array << word + elsif @c_calling_conventions.include?(word) + arg_info[:c_calling_convention] = word + else + type_array << word + end + end + + if arg_info[:const_ptr?] + attr_array << 'const' + type_array.delete_at(type_array.rindex('const')) + end + + arg_info[:modifier] = attr_array.join(' ') + arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks + arg_info + end + + def parse_args(arg_list) + args = [] + arg_list.split(',').each do |arg| + arg.strip! + return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ... + + arg_info = parse_type_and_name(arg) + arg_info.delete(:modifier) # don't care about this + arg_info.delete(:c_calling_convention) # don't care about this + + # in C, array arguments implicitly degrade to pointers + # make the translation explicit here to simplify later logic + if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) + arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" + arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] + arg_info[:ptr?] = true + end + + args << arg_info + end + + # Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name> + args.each_with_index do |val, index| + next_index = index + 1 + next unless args.length > next_index + + if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) + val[:array_data?] = true + args[next_index][:array_size?] = true + end + end + + args + end + + def divine_ptr(arg) + return false unless arg.include? '*' + # treat "const char *" and similar as a string, not a pointer + return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg + + true + end + + def divine_const(arg) + # a non-pointer arg containing "const" is a constant + # an arg containing "const" before the last * is a pointer to a constant + if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg) + true + else + false + end + end + + def divine_ptr_and_const(arg) + divination = {} + + divination[:ptr?] = divine_ptr(arg) + divination[:const?] = divine_const(arg) + + # an arg containing "const" after the last * is a constant pointer + divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false + + divination + end + + def clean_args(arg_list, parse_project) + if @local_as_void.include?(arg_list.strip) || arg_list.empty? + 'void' + else + c = 0 + # magically turn brackets into asterisks, also match for parentheses that come from macros + arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1') + # remove space to place asterisks with type (where they belong) + arg_list.gsub!(/\s+\*/, '*') + # pull asterisks away from arg to place asterisks with type (where they belong) + arg_list.gsub!(/\*(\w)/, '* \1') + + # scan argument list for function pointers and replace them with custom types + arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + funcret = Regexp.last_match(1).strip + funcname = Regexp.last_match(2).strip + funcargs = Regexp.last_match(3).strip + funconst = '' + if funcname.include? 'const' + funcname.gsub!('const', '').strip! + funconst = 'const ' + end + parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" + funcname = "cmock_arg#{c += 1}" if funcname.empty? + "#{functype} #{funconst}#{funcname}" + end + + # scan argument list for function pointers with shorthand notation and replace them with custom types + arg_list.gsub!(/([\w\s\*]+)+\s+(\w+)\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + funcret = Regexp.last_match(1).strip + funcname = Regexp.last_match(2).strip + funcargs = Regexp.last_match(3).strip + funconst = '' + if funcname.include? 'const' + funcname.gsub!('const', '').strip! + funconst = 'const ' + end + parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" + funcname = "cmock_arg#{c += 1}" if funcname.empty? + "#{functype} #{funconst}#{funcname}" + end + + # automatically name unnamed arguments (those that only had a type) + arg_list.split(/\s*,\s*/).map do |arg| + parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) + if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1]) + "#{arg} cmock_arg#{c += 1}" + else + arg + end + end.join(', ') + end + end + + def parse_declaration(parse_project, declaration, namespace = [], classname = nil) + decl = {} + decl[:namespace] = namespace + decl[:class] = classname + + regex_match = @declaration_parse_matcher.match(declaration) + raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? + + # grab argument list + args = regex_match[2].strip + + # process function attributes, return type, and name + parsed = parse_type_and_name(regex_match[1]) + + # Record original name without scope prefix + decl[:unscoped_name] = parsed[:name] + + # Prefix name with namespace scope (if any) and then class + decl[:name] = namespace.join('_') + unless classname.nil? + decl[:name] << '_' unless decl[:name].empty? + decl[:name] << classname + end + # Add original name to complete fully scoped name + decl[:name] << '_' unless decl[:name].empty? + decl[:name] << decl[:unscoped_name] + + decl[:modifier] = parsed[:modifier] + unless parsed[:c_calling_convention].nil? + decl[:c_calling_convention] = parsed[:c_calling_convention] + end + + rettype = parsed[:type] + rettype = 'void' if @local_as_void.include?(rettype.strip) + decl[:return] = { :type => rettype, + :name => 'cmock_to_return', + :str => "#{rettype} cmock_to_return", + :void? => (rettype == 'void'), + :ptr? => parsed[:ptr?] || false, + :const? => parsed[:const?] || false, + :const_ptr? => parsed[:const_ptr?] || false } + + # remove default argument statements from mock definitions + args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') + + # check for var args + if args =~ /\.\.\./ + decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip + args = if args =~ /\,[\w\s]*\.\.\./ + args.gsub!(/\,[\w\s]*\.\.\./, '') + else + 'void' + end + else + decl[:var_arg] = nil + end + args = clean_args(args, parse_project) + decl[:args_string] = args + decl[:args] = parse_args(args) + decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') + decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } + + if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? || + decl[:return][:type].empty? || decl[:name].empty? + raise "Failed Parsing Declaration Prototype!\n" \ + " declaration: '#{declaration}'\n" \ + " modifier: '#{decl[:modifier]}'\n" \ + " return: #{prototype_inspect_hash(decl[:return])}\n" \ + " function: '#{decl[:name]}'\n" \ + " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" + end + + decl + end + + def prototype_inspect_hash(hash) + pairs = [] + hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if value.class == String}#{value}#{"'" if value.class == String}" } + "{#{pairs.join(', ')}}" + end + + def prototype_inspect_array_of_hashes(array) + hashes = [] + array.each { |hash| hashes << prototype_inspect_hash(hash) } + case array.size + when 0 + return '[]' + when 1 + return "[#{hashes[0]}]" + else + return "[\n #{hashes.join("\n ")}\n ]\n" + end + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb new file mode 100644 index 0000000..342014e --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockPluginManager + attr_accessor :plugins + + def initialize(config, utils) + @plugins = [] + plugins_to_load = [:expect, config.plugins].flatten.uniq.compact + plugins_to_load.each do |plugin| + plugin_name = plugin.to_s + object_name = 'CMockGeneratorPlugin' + camelize(plugin_name) + self.class.mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) } + end + @plugins.sort! { |a, b| a.priority <=> b.priority } + end + + def run(method, args = nil) + if args.nil? + @plugins.collect { |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join + else + @plugins.collect { |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join + end + end + + def camelize(lower_case_and_underscored_word) + lower_case_and_underscored_word.gsub(/\/(.?)/) { '::' + Regexp.last_match(1).upcase }.gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase } + end + + def self.mutex + @mutex ||= Mutex.new + end + + private + + def load_plugin(plugin_name, object_name, config, utils) + unless Object.const_defined? object_name + file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb" + require file_name + end + class_name = Object.const_get(object_name) + @plugins << class_name.new(config, utils) + rescue StandardError + file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb" + raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}" + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb new file mode 100644 index 0000000..9f4beb7 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb @@ -0,0 +1,77 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockUnityHelperParser + attr_accessor :c_types + + def initialize(config) + @config = config + @fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + @c_types = map_c_types.merge(import_source) + end + + def get_helper(ctype) + lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/, '\1\3\5\6').strip.gsub(/\s+/, '_') + return [@c_types[lookup], ''] if @c_types[lookup] + + if lookup =~ /\*$/ + lookup = lookup.gsub(/\*$/, '') + return [@c_types[lookup], '*'] if @c_types[lookup] + else + lookup += '*' + return [@c_types[lookup], '&'] if @c_types[lookup] + end + return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if ctype =~ /cmock_\w+_ptr\d+/ + raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown + + lookup =~ /\*$/ ? [@fallback, '&'] : [@fallback, ''] + end + + private ########################### + + def map_c_types + c_types = {} + @config.treat_as.each_pair do |ctype, expecttype| + c_type = ctype.gsub(/\s+/, '_') + if expecttype =~ /\*/ + c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.delete('*')}_ARRAY" + else + c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}" + c_types[c_type + '*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY" + end + end + c_types + end + + def import_source + source = @config.load_unity_helper + return {} if source.nil? + + c_types = {} + source = source.gsub(/\/\/.*$/, '') # remove line comments + source = source.gsub(/\/\*.*?\*\//m, '') # remove block comments + + # scan for comparison helpers + match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4, '\s*\w+\s*').join(',') + '\)') + pairs = source.scan(match_regex).flatten.compact + (pairs.size / 2).times do |i| + expect = pairs[i * 2] + ctype = pairs[(i * 2) + 1] + c_types[ctype] = expect unless expect.include?('_ARRAY') + end + + # scan for array variants of those helpers + match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5, '\s*\w+\s*').join(',') + '\)') + pairs = source.scan(match_regex).flatten.compact + (pairs.size / 2).times do |i| + expect = pairs[i * 2] + ctype = pairs[(i * 2) + 1] + c_types[ctype.gsub('_ARRAY', '*')] = expect + end + + c_types + end +end diff --git a/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.c b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.c new file mode 100644 index 0000000..88f2c2b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.c @@ -0,0 +1,216 @@ +/* ========================================== + CMock Project - Automatic Mock Generation for C + Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#include "cmock.h" + +/* public constants to be used by mocks */ +const char* CMockStringOutOfMemory = "CMock has run out of memory. Please allocate more."; +const char* CMockStringCalledMore = "Called more times than expected."; +const char* CMockStringCalledLess = "Called fewer times than expected."; +const char* CMockStringCalledEarly = "Called earlier than expected."; +const char* CMockStringCalledLate = "Called later than expected."; +const char* CMockStringCallOrder = "Called out of order."; +const char* CMockStringIgnPreExp = "IgnoreArg called before Expect."; +const char* CMockStringPtrPreExp = "ReturnThruPtr called before Expect."; +const char* CMockStringPtrIsNULL = "Pointer is NULL."; +const char* CMockStringExpNULL = "Expected NULL."; +const char* CMockStringMismatch = "Function called with unexpected argument value."; + +/* private variables */ +#ifdef CMOCK_MEM_DYNAMIC +static unsigned char* CMock_Guts_Buffer = NULL; +static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_ALIGN_SIZE; +static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; +#else +static unsigned char CMock_Guts_Buffer[CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE]; +static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE; +static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; +#endif + +/*------------------------------------------------------- + * CMock_Guts_MemNew + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size) +{ + CMOCK_MEM_INDEX_TYPE index; + + /* verify arguments valid (we must be allocating space for at least 1 byte, and the existing chain must be in memory somewhere) */ + if (size < 1) + return CMOCK_GUTS_NONE; + + /* verify we have enough room */ + size = size + CMOCK_MEM_INDEX_SIZE; + if (size & CMOCK_MEM_ALIGN_MASK) + size = (size + CMOCK_MEM_ALIGN_MASK) & ~CMOCK_MEM_ALIGN_MASK; + if ((CMock_Guts_BufferSize - CMock_Guts_FreePtr) < size) + { +#ifndef CMOCK_MEM_DYNAMIC + return CMOCK_GUTS_NONE; /* nothing we can do; our static buffer is out of memory */ +#else + /* our dynamic buffer does not have enough room; request more via realloc() */ + CMOCK_MEM_INDEX_TYPE new_buffersize = CMock_Guts_BufferSize + CMOCK_MEM_SIZE + size; + unsigned char* new_buffer = realloc(CMock_Guts_Buffer, (size_t)new_buffersize); + if (new_buffer == NULL) + return CMOCK_GUTS_NONE; /* realloc() failed; out of memory */ + CMock_Guts_Buffer = new_buffer; + CMock_Guts_BufferSize = new_buffersize; +#endif + } + + /* determine where we're putting this new block, and init its pointer to be the end of the line */ + index = CMock_Guts_FreePtr + CMOCK_MEM_INDEX_SIZE; + *(CMOCK_MEM_INDEX_TYPE*)(&CMock_Guts_Buffer[CMock_Guts_FreePtr]) = CMOCK_GUTS_NONE; + CMock_Guts_FreePtr += size; + + return index; +} + +/*------------------------------------------------------- + * CMock_Guts_MemChain + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index) +{ + CMOCK_MEM_INDEX_TYPE index; + void* root; + void* obj; + void* next; + + if (root_index == CMOCK_GUTS_NONE) + { + /* if there is no root currently, we return this object as the root of the chain */ + return obj_index; + } + else + { + /* reject illegal nodes */ + if ((root_index < CMOCK_MEM_ALIGN_SIZE) || (root_index >= CMock_Guts_FreePtr)) + { + return CMOCK_GUTS_NONE; + } + if ((obj_index < CMOCK_MEM_ALIGN_SIZE) || (obj_index >= CMock_Guts_FreePtr)) + { + return CMOCK_GUTS_NONE; + } + + root = (void*)(&CMock_Guts_Buffer[root_index]); + obj = (void*)(&CMock_Guts_Buffer[obj_index]); + + /* find the end of the existing chain and add us */ + next = root; + do { + index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE); + if (index >= CMock_Guts_FreePtr) + return CMOCK_GUTS_NONE; + if (index > 0) + next = (void*)(&CMock_Guts_Buffer[index]); + } while (index > 0); + *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE) = (CMOCK_MEM_INDEX_TYPE)((CMOCK_MEM_PTR_AS_INT)obj - (CMOCK_MEM_PTR_AS_INT)CMock_Guts_Buffer); + return root_index; + } +} + +/*------------------------------------------------------- + * CMock_Guts_MemNext + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) +{ + CMOCK_MEM_INDEX_TYPE index; + void* previous_item; + + /* There is nothing "next" if the pointer isn't from our buffer */ + if ((previous_item_index < CMOCK_MEM_ALIGN_SIZE) || (previous_item_index >= CMock_Guts_FreePtr)) + return CMOCK_GUTS_NONE; + previous_item = (void*)(&CMock_Guts_Buffer[previous_item_index]); + + /* if the pointer is good, then use it to look up the next index + * (we know the first element always goes in zero, so NEXT must always be > 1) */ + index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)previous_item - CMOCK_MEM_INDEX_SIZE); + if ((index > 1) && (index < CMock_Guts_FreePtr)) + return index; + else + return CMOCK_GUTS_NONE; +} + +/*------------------------------------------------------- + * CMock_Guts_MemEndOfChain + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) +{ + CMOCK_MEM_INDEX_TYPE index = root_index; + CMOCK_MEM_INDEX_TYPE next_index; + + for (next_index = root_index; + next_index != CMOCK_GUTS_NONE; + next_index = CMock_Guts_MemNext(index)) + { + index = next_index; + } + + return index; +} + +/*------------------------------------------------------- + * CMock_GetAddressFor + *-------------------------------------------------------*/ +void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) +{ + if ((index >= CMOCK_MEM_ALIGN_SIZE) && (index < CMock_Guts_FreePtr)) + { + return (void*)(&CMock_Guts_Buffer[index]); + } + else + { + return NULL; + } +} + +/*------------------------------------------------------- + * CMock_Guts_MemBytesCapacity + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) +{ + return (sizeof(CMock_Guts_Buffer) - CMOCK_MEM_ALIGN_SIZE); +} + +/*------------------------------------------------------- + * CMock_Guts_MemBytesFree + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) +{ + return CMock_Guts_BufferSize - CMock_Guts_FreePtr; +} + +/*------------------------------------------------------- + * CMock_Guts_MemBytesUsed + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) +{ + return CMock_Guts_FreePtr - CMOCK_MEM_ALIGN_SIZE; +} + +/*------------------------------------------------------- + * CMock_Guts_MemFreeAll + *-------------------------------------------------------*/ +void CMock_Guts_MemFreeAll(void) +{ + CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; /* skip the very beginning */ +} + +/*------------------------------------------------------- + * CMock_Guts_MemFreeFinal + *-------------------------------------------------------*/ +void CMock_Guts_MemFreeFinal(void) +{ + CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; +#ifdef CMOCK_MEM_DYNAMIC + if (CMock_Guts_Buffer) + { + free(CMock_Guts_Buffer); + CMock_Guts_Buffer = NULL; + } +#endif +} + diff --git a/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h new file mode 100644 index 0000000..45bab18 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock.h @@ -0,0 +1,47 @@ +/* ========================================== + CMock Project - Automatic Mock Generation for C + Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef CMOCK_FRAMEWORK_H +#define CMOCK_FRAMEWORK_H + +#include "cmock_internals.h" + +#define CMOCK_VERSION_MAJOR 2 +#define CMOCK_VERSION_MINOR 5 +#define CMOCK_VERSION_BUILD 4 +#define CMOCK_VERSION ((CMOCK_VERSION_MAJOR << 16) | (CMOCK_VERSION_MINOR << 8) | CMOCK_VERSION_BUILD) + +/* should be big enough to index full range of CMOCK_MEM_MAX */ +#ifndef CMOCK_MEM_INDEX_TYPE +#include <stddef.h> +#define CMOCK_MEM_INDEX_TYPE size_t +#endif + +#define CMOCK_GUTS_NONE (0) + +#if defined __GNUC__ +# define CMOCK_FUNCTION_ATTR(a) __attribute__((a)) +#else +# define CMOCK_FUNCTION_ATTR(a) /* ignore */ +#endif + +/*------------------------------------------------------- + * Memory API + *-------------------------------------------------------*/ +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) CMOCK_FUNCTION_ATTR(pure); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) CMOCK_FUNCTION_ATTR(pure); + +void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) CMOCK_FUNCTION_ATTR(pure); + +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) CMOCK_FUNCTION_ATTR(const); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) CMOCK_FUNCTION_ATTR(pure); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) CMOCK_FUNCTION_ATTR(pure); +void CMock_Guts_MemFreeAll(void); +void CMock_Guts_MemFreeFinal(void); + +#endif /* end of CMOCK_FRAMEWORK_H */ diff --git a/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h new file mode 100644 index 0000000..56fb33b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/src/cmock_internals.h @@ -0,0 +1,91 @@ +/* ========================================== + CMock Project - Automatic Mock Generation for C + Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef CMOCK_FRAMEWORK_INTERNALS_H +#define CMOCK_FRAMEWORK_INTERNALS_H + +#include "unity.h" + +/* These are constants that the generated mocks have access to */ +extern const char* CMockStringOutOfMemory; +extern const char* CMockStringCalledMore; +extern const char* CMockStringCalledLess; +extern const char* CMockStringCalledEarly; +extern const char* CMockStringCalledLate; +extern const char* CMockStringCallOrder; +extern const char* CMockStringIgnPreExp; +extern const char* CMockStringPtrPreExp; +extern const char* CMockStringPtrIsNULL; +extern const char* CMockStringExpNULL; +extern const char* CMockStringMismatch; + +/* define CMOCK_MEM_DYNAMIC to grab memory as needed with malloc + * when you do that, CMOCK_MEM_SIZE is used for incremental size instead of total */ +#ifdef CMOCK_MEM_STATIC +#undef CMOCK_MEM_DYNAMIC +#endif + +#ifdef CMOCK_MEM_DYNAMIC +#include <stdlib.h> +#endif + +/* this is used internally during pointer arithmetic. make sure this type is the same size as the target's pointer type */ +#ifndef CMOCK_MEM_PTR_AS_INT +#ifdef UNITY_POINTER_WIDTH +#ifdef UNITY_INT_WIDTH +#if UNITY_POINTER_WIDTH == UNITY_INT_WIDTH +#define CMOCK_MEM_PTR_AS_INT unsigned int +#endif +#endif +#endif +#endif + +#ifndef CMOCK_MEM_PTR_AS_INT +#ifdef UNITY_POINTER_WIDTH +#ifdef UNITY_LONG_WIDTH +#if UNITY_POINTER_WIDTH == UNITY_LONG_WIDTH +#define CMOCK_MEM_PTR_AS_INT unsigned long +#endif +#if UNITY_POINTER_WIDTH > UNITY_LONG_WIDTH +#define CMOCK_MEM_PTR_AS_INT unsigned long long +#endif +#endif +#endif +#endif + +#ifndef CMOCK_MEM_PTR_AS_INT +#define CMOCK_MEM_PTR_AS_INT unsigned long +#endif + +/* 0 for no alignment, 1 for 16-bit, 2 for 32-bit, 3 for 64-bit */ +#ifndef CMOCK_MEM_ALIGN + #ifdef UNITY_LONG_WIDTH + #if (UNITY_LONG_WIDTH == 16) + #define CMOCK_MEM_ALIGN (1) + #elif (UNITY_LONG_WIDTH == 32) + #define CMOCK_MEM_ALIGN (2) + #elif (UNITY_LONG_WIDTH == 64) + #define CMOCK_MEM_ALIGN (3) + #else + #define CMOCK_MEM_ALIGN (2) + #endif + #else + #define CMOCK_MEM_ALIGN (2) + #endif +#endif + +/* amount of memory to allow cmock to use in its internal heap */ +#ifndef CMOCK_MEM_SIZE +#define CMOCK_MEM_SIZE (32768) +#endif + +/* automatically calculated defs for easier reading */ +#define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(1u << CMOCK_MEM_ALIGN) +#define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1) +#define CMOCK_MEM_INDEX_SIZE (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_PTR_AS_INT)((sizeof(CMOCK_MEM_INDEX_TYPE) > CMOCK_MEM_ALIGN_SIZE) ? sizeof(CMOCK_MEM_INDEX_TYPE) : CMOCK_MEM_ALIGN_SIZE) + + +#endif /* end of CMOCK_FRAMEWORK_INTERNALS_H */ diff --git a/CAN_App/vendor/ceedling/vendor/cmock/src/meson.build b/CAN_App/vendor/ceedling/vendor/cmock/src/meson.build new file mode 100644 index 0000000..c03c4e5 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/cmock/src/meson.build @@ -0,0 +1,12 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams. +# +# license: MIT +# +cmock_dir = include_directories('.') + +cmock_lib = static_library(meson.project_name(), + files('cmock.c'), + dependencies: [unity_dep], + include_directories: cmock_dir) diff --git a/CAN_App/vendor/ceedling/vendor/diy/lib/diy.rb b/CAN_App/vendor/ceedling/vendor/diy/lib/diy.rb new file mode 100644 index 0000000..581afc7 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/diy/lib/diy.rb @@ -0,0 +1,403 @@ +require 'diy/factory.rb' +require 'yaml' +require 'set' + +module DIY #:nodoc:# + VERSION = '1.1.2' + class Context + + class << self + # Enable / disable automatic requiring of libraries. Default: true + attr_accessor :auto_require + end + @auto_require = true + + # Accepts a Hash defining the object context (usually loaded from objects.yml), and an additional + # Hash containing objects to inject into the context. + def initialize(context_hash, extra_inputs={}) + raise "Nil context hash" unless context_hash + raise "Need a hash" unless context_hash.kind_of?(Hash) + [ "[]", "keys" ].each do |mname| + unless extra_inputs.respond_to?(mname) + raise "Extra inputs must respond to hash-like [] operator and methods #keys and #each" + end + end + + # store extra inputs + if extra_inputs.kind_of?(Hash) + @extra_inputs= {} + extra_inputs.each { |k,v| @extra_inputs[k.to_s] = v } # smooth out the names + else + @extra_inputs = extra_inputs + end + + collect_object_and_subcontext_defs context_hash + + # init the cache + @cache = {} + @cache['this_context'] = self + end + + + # Convenience: create a new DIY::Context by loading from a String (or open file handle.) + def self.from_yaml(io_or_string, extra_inputs={}) + raise "nil input to YAML" unless io_or_string + Context.new(YAML.load(io_or_string), extra_inputs) + end + + # Convenience: create a new DIY::Context by loading from the named file. + def self.from_file(fname, extra_inputs={}) + raise "nil file name" unless fname + self.from_yaml(File.read(fname), extra_inputs) + end + + # Return a reference to the object named. If necessary, the object will + # be instantiated on first use. If the object is non-singleton, a new + # object will be produced each time. + def get_object(obj_name) + key = obj_name.to_s + obj = @cache[key] + unless obj + if extra_inputs_has(key) + obj = @extra_inputs[key] + else + case @defs[key] + when MethodDef + obj = construct_method(key) + when FactoryDef + obj = construct_factory(key) + @cache[key] = obj + else + obj = construct_object(key) + @cache[key] = obj if @defs[key].singleton? + end + end + end + obj + end + alias :[] :get_object + + # Inject a named object into the Context. This must be done before the Context has instantiated the + # object in question. + def set_object(obj_name,obj) + key = obj_name.to_s + raise "object '#{key}' already exists in context" if @cache.keys.include?(key) + @cache[key] = obj + end + alias :[]= :set_object + + # Provide a listing of object names + def keys + (@defs.keys.to_set + @extra_inputs.keys.to_set).to_a + end + + # Instantiate and yield the named subcontext + def within(sub_context_name) + # Find the subcontext definitaion: + context_def = @sub_context_defs[sub_context_name.to_s] + raise "No sub-context named #{sub_context_name}" unless context_def + # Instantiate a new context using self as parent: + context = Context.new( context_def, self ) + + yield context + end + + # Returns true if the context contains an object with the given name + def contains_object(obj_name) + key = obj_name.to_s + @defs.keys.member?(key) or extra_inputs_has(key) + end + + # Every top level object in the Context is instantiated. This is especially useful for + # systems that have "floating observers"... objects that are never directly accessed, who + # would thus never be instantiated by coincedence. This does not build any subcontexts + # that may exist. + def build_everything + @defs.keys.each { |k| self[k] } + end + alias :build_all :build_everything + alias :preinstantiate_singletons :build_everything + + private + + def collect_object_and_subcontext_defs(context_hash) + @defs = {} + @sub_context_defs = {} + get_defs_from context_hash + end + + def get_defs_from(hash, namespace=nil) + hash.each do |name,info| + # we modify the info hash below so it's important to have a new + # instance to play with + info = info.dup if info + + # see if we are building a factory + if info and info.has_key?('builds') + unless info.has_key?('auto_require') + info['auto_require'] = self.class.auto_require + end + + if namespace + info['builds'] = namespace.build_classname(info['builds']) + end + @defs[name] = FactoryDef.new({:name => name, + :target => info['builds'], + :library => info['library'], + :auto_require => info['auto_require']}) + next + end + + name = name.to_s + case name + when /^\+/ + # subcontext + @sub_context_defs[name.gsub(/^\+/,'')] = info + + when /^using_namespace/ + # namespace: use a module(s) prefix for the classname of contained object defs + # NOTE: namespacing is NOT scope... it's just a convenient way to setup class names for a group of objects. + get_defs_from info, parse_namespace(name) + when /^method\s/ + key_name = name.gsub(/^method\s/, "") + @defs[key_name] = MethodDef.new(:name => key_name, + :object => info['object'], + :method => info['method'], + :attach => info['attach']) + else + # Normal object def + info ||= {} + if extra_inputs_has(name) + raise ConstructionError.new(name, "Object definition conflicts with parent context") + end + unless info.has_key?('auto_require') + info['auto_require'] = self.class.auto_require + end + if namespace + if info['class'] + info['class'] = namespace.build_classname(info['class']) + else + info['class'] = namespace.build_classname(name) + end + end + + @defs[name] = ObjectDef.new(:name => name, :info => info) + + end + end + end + + def construct_method(key) + method_definition = @defs[key] + object = get_object(method_definition.object) + method = object.method(method_definition.method) + + unless method_definition.attach.nil? + instance_var_name = "@__diy_#{method_definition.object}" + + method_definition.attach.each do |object_key| + get_object(object_key).instance_eval do + instance_variable_set(instance_var_name, object) + eval %|def #{key}(*args) + #{instance_var_name}.#{method_definition.method}(*args) + end| + end + end + end + + return method + rescue Exception => oops + build_and_raise_construction_error(key, oops) + end + + def construct_object(key) + # Find the object definition + obj_def = @defs[key] + raise "No object definition for '#{key}'" unless obj_def + # If object def mentions a library, load it + require obj_def.library if obj_def.library + + # Resolve all components for the object + arg_hash = {} + obj_def.components.each do |name,value| + case value + when Lookup + arg_hash[name.to_sym] = get_object(value.name) + when StringValue + arg_hash[name.to_sym] = value.literal_value + else + raise "Cannot cope with component definition '#{value.inspect}'" + end + end + # Get a reference to the class for the object + big_c = get_class_for_name_with_module_delimeters(obj_def.class_name) + # Make and return the instance + if obj_def.use_class_directly? + return big_c + elsif arg_hash.keys.size > 0 + return big_c.new(arg_hash) + else + return big_c.new + end + rescue Exception => oops + build_and_raise_construction_error(key, oops) + end + + def build_and_raise_construction_error(key, oops) + cerr = ConstructionError.new(key,oops) + cerr.set_backtrace(oops.backtrace) + raise cerr + end + + def get_class_for_name_with_module_delimeters(class_name) + class_name.split(/::/).inject(Object) do |mod,const_name| mod.const_get(const_name) end + end + + def extra_inputs_has(key) + if key.nil? or key.strip == '' + raise ArgumentError.new("Cannot lookup objects with nil keys") + end + @extra_inputs.keys.member?(key) or @extra_inputs.keys.member?(key.to_sym) + end + + def parse_namespace(str) + Namespace.new(str) + end + end + + class Namespace #:nodoc:# + def initialize(str) + # 'using_namespace Animal Reptile' + parts = str.split(/\s+/) + raise "Namespace definitions must begin with 'using_namespace'" unless parts[0] == 'using_namespace' + parts.shift + + if parts.length > 0 and parts[0] =~ /::/ + parts = parts[0].split(/::/) + end + + raise NamespaceError, "Namespace needs to indicate a module" if parts.empty? + + @module_nest = parts + end + + def build_classname(name) + [ @module_nest, Infl.camelize(name) ].flatten.join("::") + end + end + + class Lookup #:nodoc: + attr_reader :name + def initialize(obj_name) + @name = obj_name + end + end + + class MethodDef #:nodoc: + attr_accessor :name, :object, :method, :attach + + def initialize(opts) + @name, @object, @method, @attach = opts[:name], opts[:object], opts[:method], opts[:attach] + end + end + + class ObjectDef #:nodoc: + attr_accessor :name, :class_name, :library, :components + def initialize(opts) + name = opts[:name] + raise "Can't make an ObjectDef without a name" if name.nil? + + info = opts[:info] || {} + info = info.clone + + @components = {} + + # Object name + @name = name + + # Class name + @class_name = info.delete 'class' + @class_name ||= info.delete 'type' + @class_name ||= Infl.camelize(@name) + + # Auto Require + @auto_require = info.delete 'auto_require' + + # Library + @library = info.delete 'library' + @library ||= info.delete 'lib' + @library ||= Infl.underscore(@class_name) if @auto_require + + # Use Class Directly + @use_class_directly = info.delete 'use_class_directly' + + # Auto-compose + compose = info.delete 'compose' + if compose + case compose + when Array + auto_names = compose.map { |x| x.to_s } + when String + auto_names = compose.split(',').map { |x| x.to_s.strip } + when Symbol + auto_names = [ compose.to_s ] + else + raise "Cannot auto compose object #{@name}, bad 'compose' format: #{compose.inspect}" + end + end + auto_names ||= [] + auto_names.each do |cname| + @components[cname] = Lookup.new(cname) + end + + # Singleton status + if info['singleton'].nil? + @singleton = true + else + @singleton = info['singleton'] + end + info.delete 'singleton' + + # Remaining keys + info.each do |key,val| + @components[key.to_s] = Lookup.new(val.to_s) + end + + end + + def singleton? + @singleton + end + + def use_class_directly? + @use_class_directly == true + end + + end + + class ConstructionError < RuntimeError #:nodoc:# + def initialize(object_name, cause=nil) + object_name = object_name + cause = cause + m = "Failed to construct '#{object_name}'" + if cause + m << "\n ...caused by:\n >>> #{cause}" + end + super m + end + end + + class NamespaceError < RuntimeError #:nodoc:# + end + + module Infl #:nodoc:# + # Ganked this from Inflector: + def self.camelize(lower_case_and_underscored_word) + lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } + end + # Ganked this from Inflector: + def self.underscore(camel_cased_word) + camel_cased_word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase + end + end +end diff --git a/CAN_App/vendor/ceedling/vendor/diy/lib/diy/factory.rb b/CAN_App/vendor/ceedling/vendor/diy/lib/diy/factory.rb new file mode 100644 index 0000000..d2566c5 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/diy/lib/diy/factory.rb @@ -0,0 +1,36 @@ +module DIY #:nodoc:# + class FactoryDef #:nodoc: + attr_accessor :name, :target, :class_name, :library + + def initialize(opts) + @name, @target, @library, @auto_require = + opts[:name], opts[:target], opts[:library], opts[:auto_require] + + @class_name = Infl.camelize(@target) + @library ||= Infl.underscore(@class_name) if @auto_require + end + end + + class Context + def construct_factory(key) + factory_def = @defs[key] +# puts "requiring #{factory_def.library}" + require factory_def.library if factory_def.library + + big_c = get_class_for_name_with_module_delimeters(factory_def.class_name) + + FactoryFactory.new(big_c) + end + end + + class FactoryFactory + def initialize(clazz) + @class_to_create = clazz + end + + def create(*args) + @class_to_create.new(*args) + end + end +end + diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/colour_prompt.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/colour_prompt.rb new file mode 100644 index 0000000..85cbfd8 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/colour_prompt.rb @@ -0,0 +1,119 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +if RUBY_PLATFORM =~ /(win|w)32$/ + begin + require 'Win32API' + rescue LoadError + puts 'ERROR! "Win32API" library not found' + puts '"Win32API" is required for colour on a windows machine' + puts ' try => "gem install Win32API" on the command line' + puts + end + # puts + # puts 'Windows Environment Detected...' + # puts 'Win32API Library Found.' + # puts +end + +class ColourCommandLine + def initialize + return unless RUBY_PLATFORM =~ /(win|w)32$/ + + get_std_handle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L') + @set_console_txt_attrb = + Win32API.new('kernel32', 'SetConsoleTextAttribute', %w[L N], 'I') + @hout = get_std_handle.call(-11) + end + + def change_to(new_colour) + if RUBY_PLATFORM =~ /(win|w)32$/ + @set_console_txt_attrb.call(@hout, win32_colour(new_colour)) + else + "\033[30;#{posix_colour(new_colour)};22m" + end + end + + def win32_colour(colour) + case colour + when :black then 0 + when :dark_blue then 1 + when :dark_green then 2 + when :dark_cyan then 3 + when :dark_red then 4 + when :dark_purple then 5 + when :dark_yellow, :narrative then 6 + when :default_white, :default, :dark_white then 7 + when :silver then 8 + when :blue then 9 + when :green, :success then 10 + when :cyan, :output then 11 + when :red, :failure then 12 + when :purple then 13 + when :yellow then 14 + when :white then 15 + else + 0 + end + end + + def posix_colour(colour) + # ANSI Escape Codes - Foreground colors + # | Code | Color | + # | 39 | Default foreground color | + # | 30 | Black | + # | 31 | Red | + # | 32 | Green | + # | 33 | Yellow | + # | 34 | Blue | + # | 35 | Magenta | + # | 36 | Cyan | + # | 37 | Light gray | + # | 90 | Dark gray | + # | 91 | Light red | + # | 92 | Light green | + # | 93 | Light yellow | + # | 94 | Light blue | + # | 95 | Light magenta | + # | 96 | Light cyan | + # | 97 | White | + + case colour + when :black then 30 + when :red, :failure then 31 + when :green, :success then 32 + when :yellow then 33 + when :blue, :narrative then 34 + when :purple, :magenta then 35 + when :cyan, :output then 36 + when :white, :default_white then 37 + when :default then 39 + else + 39 + end + end + + def out_c(mode, colour, str) + case RUBY_PLATFORM + when /(win|w)32$/ + change_to(colour) + $stdout.puts str if mode == :puts + $stdout.print str if mode == :print + change_to(:default_white) + else + $stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts + $stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print + end + end +end + +def colour_puts(role, str) + ColourCommandLine.new.out_c(:puts, role, str) +end + +def colour_print(role, str) + ColourCommandLine.new.out_c(:print, role, str) +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/colour_reporter.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/colour_reporter.rb new file mode 100644 index 0000000..1c3bc21 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/colour_reporter.rb @@ -0,0 +1,39 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +require_relative 'colour_prompt' + +$colour_output = true + +def report(message) + if !$colour_output + $stdout.puts(message) + else + message = message.join('\n') if message.class == Array + message.each_line do |line| + line.chomp! + colour = case line + when /(?:total\s+)?tests:?\s+(\d+)\s+(?:total\s+)?failures:?\s+\d+\s+Ignored:?/i + Regexp.last_match(1).to_i.zero? ? :green : :red + when /PASS/ + :green + when /^OK$/ + :green + when /(?:FAIL|ERROR)/ + :red + when /IGNORE/ + :yellow + when /^(?:Creating|Compiling|Linking)/ + :white + else + :silver + end + colour_puts(colour, line) + end + end + $stdout.flush + $stderr.flush +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/generate_config.yml b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_config.yml new file mode 100644 index 0000000..4a5e474 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_config.yml @@ -0,0 +1,36 @@ +#this is a sample configuration file for generate_module +#you would use it by calling generate_module with the -ygenerate_config.yml option +#files like this are useful for customizing generate_module to your environment +:generate_module: + :defaults: + #these defaults are used in place of any missing options at the command line + :path_src: ../src/ + :path_inc: ../src/ + :path_tst: ../test/ + :update_svn: true + :includes: + #use [] for no additional includes, otherwise list the includes on separate lines + :src: + - Defs.h + - Board.h + :inc: [] + :tst: + - Defs.h + - Board.h + - Exception.h + :boilerplates: + #these are inserted at the top of generated files. + #just comment out or remove if not desired. + #use %1$s where you would like the file name to appear (path/extension not included) + :src: | + //------------------------------------------- + // %1$s.c + //------------------------------------------- + :inc: | + //------------------------------------------- + // %1$s.h + //------------------------------------------- + :tst: | + //------------------------------------------- + // Test%1$s.c : Units tests for %1$s.c + //------------------------------------------- diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/generate_module.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_module.rb new file mode 100644 index 0000000..0a88bec --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_module.rb @@ -0,0 +1,313 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# This script creates all the files with start code necessary for a new module. +# A simple module only requires a source file, header file, and test file. +# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware). + +require 'rubygems' +require 'fileutils' +require 'pathname' + +# TEMPLATE_TST +TEMPLATE_TST ||= '#ifdef TEST + +#include "unity.h" + +%2$s#include "%1$s.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_%4$s_NeedToImplement(void) +{ + TEST_IGNORE_MESSAGE("Need to Implement %1$s"); +} + +#endif // TEST +'.freeze + +# TEMPLATE_SRC +TEMPLATE_SRC ||= '%2$s#include "%1$s.h" +'.freeze + +# TEMPLATE_INC +TEMPLATE_INC ||= '#ifndef %3$s_H +#define %3$s_H +%2$s + +#endif // %3$s_H +'.freeze + +class UnityModuleGenerator + ############################ + def initialize(options = nil) + @options = UnityModuleGenerator.default_options + case options + when NilClass then @options + when String then @options.merge!(UnityModuleGenerator.grab_config(options)) + when Hash then @options.merge!(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + + # Create default file paths if none were provided + @options[:path_src] = "#{__dir__}/../src/" if @options[:path_src].nil? + @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil? + @options[:path_tst] = "#{__dir__}/../test/" if @options[:path_tst].nil? + @options[:path_src] += '/' unless @options[:path_src][-1] == 47 + @options[:path_inc] += '/' unless @options[:path_inc][-1] == 47 + @options[:path_tst] += '/' unless @options[:path_tst][-1] == 47 + + # Built in patterns + @patterns = { + 'src' => { + '' => { inc: [] } + }, + 'test' => { + '' => { inc: [] } + }, + 'dh' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'dih' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] }, + 'Interrupt' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mch' => { + 'Model' => { inc: [] }, + 'Conductor' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mvp' => { + 'Model' => { inc: [] }, + 'Presenter' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] }, + 'View' => { inc: [] } + } + } + end + + ############################ + def self.default_options + { + pattern: 'src', + includes: { + src: [], + inc: [], + tst: [] + }, + update_svn: false, + boilerplates: {}, + test_prefix: 'Test', + mock_prefix: 'Mock' + } + end + + ############################ + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require 'yaml' + yaml_guts = YAML.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + ############################ + def files_to_operate_on(module_name, pattern = nil) + # strip any leading path information from the module name and save for later + subfolder = File.dirname(module_name) + module_name = File.basename(module_name) + + # create triad definition + prefix = @options[:test_prefix] || 'Test' + triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] }, + { ext: '.h', path: @options[:path_inc], prefix: '', template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] }, + { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst] }] + + # prepare the pattern for use + pattern = (pattern || @options[:pattern] || 'src').downcase + patterns = @patterns[pattern] + raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil? + + # single file patterns (currently just 'test') can reject the other parts of the triad + triad.select! { |v| v[:inc] == :tst } if pattern == 'test' + + # Assemble the path/names of the files we need to work with. + files = [] + triad.each do |cfg| + patterns.each_pair do |pattern_file, pattern_traits| + submodule_name = create_filename(module_name, pattern_file) + filename = cfg[:prefix] + submodule_name + cfg[:ext] + files << { + path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath, + name: submodule_name, + template: cfg[:template], + boilerplate: cfg[:boilerplate], + includes: case (cfg[:inc]) + when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) }) + when :inc then (@options[:includes][:inc] || []) + when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) }) + end + } + end + end + + files + end + + ############################ + def neutralize_filename(name, start_cap = true) + return name if name.empty? + name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map { |v| v.capitalize }.join('_') + name = name[0].downcase + name[1..-1] unless start_cap + return name + end + + ############################ + def create_filename(part1, part2 = '') + name = part2.empty? ? part1 : part1 + '_' + part2 + case (@options[:naming]) + when 'bumpy' then neutralize_filename(name,false).delete('_') + when 'camel' then neutralize_filename(name).delete('_') + when 'snake' then neutralize_filename(name).downcase + when 'caps' then neutralize_filename(name).upcase + else name + end + end + + ############################ + def generate(module_name, pattern = nil) + files = files_to_operate_on(module_name, pattern) + + # Abort if all of the module files already exist + all_files_exist = true + files.each do |file| + all_files_exist = false unless File.exist?(file[:path]) + end + raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist + + # Create Source Modules + files.each_with_index do |file, _i| + # If this file already exists, don't overwrite it. + if File.exist?(file[:path]) + puts "File #{file[:path]} already exists!" + next + end + # Create the path first if necessary. + FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false) + File.open(file[:path], 'w') do |f| + f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil? + f.write(file[:template] % [file[:name], + file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join, + file[:name].upcase.gsub(/-/, '_'), + file[:name].gsub(/-/, '_')]) + end + if @options[:update_svn] + `svn add \"#{file[:path]}\"` + if $!.exitstatus.zero? + puts "File #{file[:path]} created and added to source control" + else + puts "File #{file[:path]} created but FAILED adding to source control!" + end + else + puts "File #{file[:path]} created" + end + end + puts 'Generate Complete' + end + + ############################ + def destroy(module_name, pattern = nil) + files_to_operate_on(module_name, pattern).each do |filespec| + file = filespec[:path] + if File.exist?(file) + if @options[:update_svn] + `svn delete \"#{file}\" --force` + puts "File #{file} deleted and removed from source control" + else + FileUtils.remove(file) + puts "File #{file} deleted" + end + else + puts "File #{file} does not exist so cannot be removed." + end + end + puts 'Destroy Complete' + end +end + +############################ +# Handle As Command Line If Called That Way +if $0 == __FILE__ + destroy = false + options = {} + module_name = nil + + # Parse the command line parameters. + ARGV.each do |arg| + case arg + when /^-d/ then destroy = true + when /^-u/ then options[:update_svn] = true + when /^-p\"?(\w+)\"?/ then options[:pattern] = Regexp.last_match(1) + when /^-s\"?(.+)\"?/ then options[:path_src] = Regexp.last_match(1) + when /^-i\"?(.+)\"?/ then options[:path_inc] = Regexp.last_match(1) + when /^-t\"?(.+)\"?/ then options[:path_tst] = Regexp.last_match(1) + when /^-n\"?(.+)\"?/ then options[:naming] = Regexp.last_match(1) + when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(1)) + when /^(\w+)/ + raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil? + + module_name = arg + when /^-(h|-help)/ + ARGV = [].freeze + else + raise "ERROR: Unknown option specified '#{arg}'" + end + end + + unless ARGV[0] + puts ["\nGENERATE MODULE\n-------- ------", + "\nUsage: ruby generate_module [options] module_name", + " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)", + " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)", + " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)", + ' -p"MCH" sets the output pattern to MCH.', + ' dh - driver hardware.', + ' dih - driver interrupt hardware.', + ' mch - model conductor hardware.', + ' mvp - model view presenter.', + ' src - just a source module, header and test. (DEFAULT)', + ' test - just a test file.', + ' -d destroy module instead of creating it.', + ' -n"camel" sets the file naming convention.', + ' bumpy - BumpyCaseFilenames.', + ' camel - camelCaseFilenames.', + ' snake - snake_case_filenames.', + ' caps - CAPS_CASE_FILENAMES.', + ' -u update subversion too (requires subversion command line)', + ' -y"my.yml" selects a different yaml config file for module generation', + ''].join("\n") + exit + end + + raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil? + + if destroy + UnityModuleGenerator.new(options).destroy(module_name) + else + UnityModuleGenerator.new(options).generate(module_name) + end + +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb new file mode 100644 index 0000000..d1d8f91 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb @@ -0,0 +1,511 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class UnityTestRunnerGenerator + def initialize(options = nil) + @options = UnityTestRunnerGenerator.default_options + case options + when NilClass + @options + when String + @options.merge!(UnityTestRunnerGenerator.grab_config(options)) + when Hash + # Check if some of these have been specified + @options[:has_setup] = !options[:setup_name].nil? + @options[:has_teardown] = !options[:teardown_name].nil? + @options[:has_suite_setup] = !options[:suite_setup].nil? + @options[:has_suite_teardown] = !options[:suite_teardown].nil? + @options.merge!(options) + else + raise 'If you specify arguments, it should be a filename or a hash of options' + end + require_relative 'type_sanitizer' + end + + def self.default_options + { + includes: [], + defines: [], + plugins: [], + framework: :unity, + test_prefix: 'test|spec|should', + mock_prefix: 'Mock', + mock_suffix: '', + setup_name: 'setUp', + teardown_name: 'tearDown', + test_reset_name: 'resetTest', + test_verify_name: 'verifyTest', + main_name: 'main', # set to :auto to automatically generate each time + main_export_decl: '', + cmdline_args: false, + omit_begin_end: false, + use_param_tests: false, + include_extensions: '(?:hpp|hh|H|h)', + source_extensions: '(?:cpp|cc|ino|C|c)' + } + end + + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require 'yaml' + yaml_guts = YAML.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + def run(input_file, output_file, options = nil) + @options.merge!(options) unless options.nil? + + # pull required data from source file + source = File.read(input_file) + source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil) + tests = find_tests(source) + headers = find_includes(source) + testfile_includes = (headers[:local] + headers[:system]) + used_mocks = find_mocks(testfile_includes) + testfile_includes = (testfile_includes - used_mocks) + testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ } + find_setup_and_teardown(source) + + # build runner file + generate(input_file, output_file, tests, used_mocks, testfile_includes) + + # determine which files were used to return them + all_files_used = [input_file, output_file] + all_files_used += testfile_includes.map { |filename| filename + '.c' } unless testfile_includes.empty? + all_files_used += @options[:includes] unless @options[:includes].empty? + all_files_used += headers[:linkonly] unless headers[:linkonly].empty? + all_files_used.uniq + end + + def generate(input_file, output_file, tests, used_mocks, testfile_includes) + File.open(output_file, 'w') do |output| + create_header(output, used_mocks, testfile_includes) + create_externs(output, tests, used_mocks) + create_mock_management(output, used_mocks) + create_setup(output) + create_teardown(output) + create_suite_setup(output) + create_suite_teardown(output) + create_reset(output) + create_run_test(output) unless tests.empty? + create_args_wrappers(output, tests) + create_main(output, input_file, tests, used_mocks) + end + + return unless @options[:header_file] && !@options[:header_file].empty? + + File.open(@options[:header_file], 'w') do |output| + create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks) + end + end + + def find_tests(source) + tests_and_line_numbers = [] + + # contains characters which will be substituted from within strings, doing + # this prevents these characters from interfering with scrubbers + # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers + substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' } + substring_re = Regexp.union(substring_subs.keys) + substring_unsubs = substring_subs.invert # the inverse map will be used to fix the strings afterwords + substring_unsubs['@quote@'] = '\\"' + substring_unsubs['@apos@'] = '\\\'' + substring_unre = Regexp.union(substring_unsubs.keys) + source_scrubbed = source.clone + source_scrubbed = source_scrubbed.gsub(/\\"/, '@quote@') # hide escaped quotes to allow capture of the full string/char + source_scrubbed = source_scrubbed.gsub(/\\'/, '@apos@') # hide escaped apostrophes to allow capture of the full string/char + source_scrubbed = source_scrubbed.gsub(/("[^"\n]*")|('[^'\n]*')/) { |s| s.gsub(substring_re, substring_subs) } # temporarily hide problematic characters within strings + source_scrubbed = source_scrubbed.gsub(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments + source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments (all that remain) + lines = source_scrubbed.split(/(^\s*\#.*$) | (;|\{|\}) /x) # Treat preprocessor directives as a logical line. Match ;, {, and } as end of lines + .map { |line| line.gsub(substring_unre, substring_unsubs) } # unhide the problematic characters previously removed + + lines.each_with_index do |line, _index| + # find tests + next unless line =~ /^((?:\s*(?:TEST_CASE|TEST_RANGE)\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m + + arguments = Regexp.last_match(1) + name = Regexp.last_match(2) + call = Regexp.last_match(3) + params = Regexp.last_match(4) + args = nil + + if @options[:use_param_tests] && !arguments.empty? + args = [] + arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] } + + arguments.scan(/\s*TEST_RANGE\s*\((.*)\)\s*$/).flatten.each do |range_str| + args += range_str.scan(/\[\s*(-?\d+.?\d*),\s*(-?\d+.?\d*),\s*(-?\d+.?\d*)\s*\]/).map do |arg_values_str| + arg_values_str.map do |arg_value_str| + arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i + end + end.map do |arg_values| + (arg_values[0]..arg_values[1]).step(arg_values[2]).to_a + end.reduce do |result, arg_range_expanded| + result.product(arg_range_expanded) + end.map do |arg_combinations| + arg_combinations.flatten.join(', ') + end + end + end + + tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } + end + + tests_and_line_numbers.uniq! { |v| v[:test] } + + # determine line numbers and create tests to run + source_lines = source.split("\n") + source_index = 0 + tests_and_line_numbers.size.times do |i| + source_lines[source_index..-1].each_with_index do |line, index| + next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/ + + source_index += index + tests_and_line_numbers[i][:line_number] = source_index + 1 + break + end + end + + tests_and_line_numbers + end + + def find_includes(source) + # remove comments (block and line, in three steps to ensure correct precedence) + source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source.gsub!(/\/\*.*?\*\//m, '') # remove block comments + source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) + + # parse out includes + includes = { + local: source.scan(/^\s*#include\s+\"\s*(.+\.#{@options[:include_extensions]})\s*\"/).flatten, + system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, + linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+\.#{@options[:source_extensions]})\s*\"/).flatten + } + includes + end + + def find_mocks(includes) + mock_headers = [] + includes.each do |include_path| + include_file = File.basename(include_path) + mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i + end + mock_headers + end + + def find_setup_and_teardown(source) + @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/ + @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/ + @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/) + @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/) + end + + def create_header(output, mocks, testfile_includes = []) + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + output.puts("\n/*=======Automagically Detected Files To Include=====*/") + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless mocks.empty? + if @options[:defines] && !@options[:defines].empty? + @options[:defines].each { |d| output.puts("#ifndef #{d}\n#define #{d}\n#endif /* #{d} */") } + end + if @options[:header_file] && !@options[:header_file].empty? + output.puts("#include \"#{File.basename(@options[:header_file])}\"") + else + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + end + mocks.each do |mock| + output.puts("#include \"#{mock}\"") + end + output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) + + return unless @options[:enforce_strict_ordering] + + output.puts('') + output.puts('int GlobalExpectCount;') + output.puts('int GlobalVerifyOrder;') + output.puts('char* GlobalOrderError;') + end + + def create_externs(output, tests, _mocks) + output.puts("\n/*=======External Functions This Runner Calls=====*/") + output.puts("extern void #{@options[:setup_name]}(void);") + output.puts("extern void #{@options[:teardown_name]}(void);") + output.puts("\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif") if @options[:externc] + tests.each do |test| + output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});") + end + output.puts("#ifdef __cplusplus\n}\n#endif") if @options[:externc] + output.puts('') + end + + def create_mock_management(output, mock_headers) + output.puts("\n/*=======Mock Management=====*/") + output.puts('static void CMock_Init(void)') + output.puts('{') + + if @options[:enforce_strict_ordering] + output.puts(' GlobalExpectCount = 0;') + output.puts(' GlobalVerifyOrder = 0;') + output.puts(' GlobalOrderError = NULL;') + end + + mocks = mock_headers.map { |mock| File.basename(mock, '.*') } + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Init();") + end + output.puts("}\n") + + output.puts('static void CMock_Verify(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Verify();") + end + output.puts("}\n") + + output.puts('static void CMock_Destroy(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Destroy();") + end + output.puts("}\n") + end + + def create_setup(output) + return if @options[:has_setup] + + output.puts("\n/*=======Setup (stub)=====*/") + output.puts("void #{@options[:setup_name]}(void) {}") + end + + def create_teardown(output) + return if @options[:has_teardown] + + output.puts("\n/*=======Teardown (stub)=====*/") + output.puts("void #{@options[:teardown_name]}(void) {}") + end + + def create_suite_setup(output) + return if @options[:suite_setup].nil? + + output.puts("\n/*=======Suite Setup=====*/") + output.puts('void suiteSetUp(void)') + output.puts('{') + output.puts(@options[:suite_setup]) + output.puts('}') + end + + def create_suite_teardown(output) + return if @options[:suite_teardown].nil? + + output.puts("\n/*=======Suite Teardown=====*/") + output.puts('int suiteTearDown(int num_failures)') + output.puts('{') + output.puts(@options[:suite_teardown]) + output.puts('}') + end + + def create_reset(output) + output.puts("\n/*=======Test Reset Options=====*/") + output.puts("void #{@options[:test_reset_name]}(void);") + output.puts("void #{@options[:test_reset_name]}(void)") + output.puts('{') + output.puts(" #{@options[:teardown_name]}();") + output.puts(' CMock_Verify();') + output.puts(' CMock_Destroy();') + output.puts(' CMock_Init();') + output.puts(" #{@options[:setup_name]}();") + output.puts('}') + output.puts("void #{@options[:test_verify_name]}(void);") + output.puts("void #{@options[:test_verify_name]}(void)") + output.puts('{') + output.puts(' CMock_Verify();') + output.puts('}') + end + + def create_run_test(output) + require 'erb' + template = ERB.new(File.read(File.join(__dir__, 'run_test.erb')), nil, '<>') + output.puts("\n" + template.result(binding)) + end + + def create_args_wrappers(output, tests) + return unless @options[:use_param_tests] + + output.puts("\n/*=======Parameterized Test Wrappers=====*/") + tests.each do |test| + next if test[:args].nil? || test[:args].empty? + + test[:args].each.with_index(1) do |args, idx| + output.puts("static void runner_args#{idx}_#{test[:test]}(void)") + output.puts('{') + output.puts(" #{test[:test]}(#{args});") + output.puts("}\n") + end + end + end + + def create_main(output, filename, tests, used_mocks) + output.puts("\n/*=======MAIN=====*/") + main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s + if @options[:cmdline_args] + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);") + end + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)") + output.puts('{') + output.puts(' int parse_status = UnityParseOptions(argc, argv);') + output.puts(' if (parse_status != 0)') + output.puts(' {') + output.puts(' if (parse_status < 0)') + output.puts(' {') + output.puts(" UnityPrint(\"#{filename.gsub('.c', '')}.\");") + output.puts(' UNITY_PRINT_EOL();') + tests.each do |test| + if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? + output.puts(" UnityPrint(\" #{test[:test]}\");") + output.puts(' UNITY_PRINT_EOL();') + else + test[:args].each do |args| + output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");") + output.puts(' UNITY_PRINT_EOL();') + end + end + end + output.puts(' return 0;') + output.puts(' }') + output.puts(' return parse_status;') + output.puts(' }') + else + main_return = @options[:omit_begin_end] ? 'void' : 'int' + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} #{main_return} #{main_name}(void);") + end + output.puts("#{main_return} #{main_name}(void)") + output.puts('{') + end + output.puts(' suiteSetUp();') if @options[:has_suite_setup] + if @options[:omit_begin_end] + output.puts(" UnitySetTestFile(\"#{filename.gsub(/\\/, '\\\\\\')}\");") + else + output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");") + end + tests.each do |test| + if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? + output.puts(" run_test(#{test[:test]}, \"#{test[:test]}\", #{test[:line_number]});") + else + test[:args].each.with_index(1) do |args, idx| + wrapper = "runner_args#{idx}_#{test[:test]}" + testname = "#{test[:test]}(#{args})".dump + output.puts(" run_test(#{wrapper}, #{testname}, #{test[:line_number]});") + end + end + end + output.puts + output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty? + if @options[:has_suite_teardown] + if @options[:omit_begin_end] + output.puts(' (void) suite_teardown(0);') + else + output.puts(' return suiteTearDown(UnityEnd());') + end + else + output.puts(' return UnityEnd();') unless @options[:omit_begin_end] + end + output.puts('}') + end + + def create_h_file(output, filename, tests, testfile_includes, used_mocks) + filename = File.basename(filename).gsub(/[-\/\\\.\,\s]/, '_').upcase + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + output.puts("#ifndef _#{filename}") + output.puts("#define _#{filename}\n\n") + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless used_mocks.empty? + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") + end + output.puts "\n" + tests.each do |test| + if test[:params].nil? || test[:params].empty? + output.puts("void #{test[:test]}(void);") + else + output.puts("void #{test[:test]}(#{test[:params]});") + end + end + output.puts("#endif\n\n") + end +end + +if $0 == __FILE__ + options = { includes: [] } + + # parse out all the options first (these will all be removed as we go) + ARGV.reject! do |arg| + case arg + when '-cexception' + options[:plugins] = [:cexception] + true + when /\.*\.ya?ml$/ + options = UnityTestRunnerGenerator.grab_config(arg) + true + when /--(\w+)=\"?(.*)\"?/ + options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) + true + when /\.*\.(?:hpp|hh|H|h)$/ + options[:includes] << arg + true + else false + end + end + + # make sure there is at least one parameter left (the input file) + unless ARGV[0] + puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)", + "\n input_test_file - this is the C file you want to create a runner for", + ' output - this is the name of the runner file to generate', + ' defaults to (input_test_file)_Runner', + ' files:', + ' *.yml / *.yaml - loads configuration from here in :unity or :cmock', + ' *.h - header files are added as #includes in runner', + ' options:', + ' -cexception - include cexception support', + ' -externc - add extern "C" for cpp support', + ' --setup_name="" - redefine setUp func name to something else', + ' --teardown_name="" - redefine tearDown func name to something else', + ' --main_name="" - redefine main func name to something else', + ' --test_prefix="" - redefine test prefix from default test|spec|should', + ' --test_reset_name="" - redefine resetTest func name to something else', + ' --test_verify_name="" - redefine verifyTest func name to something else', + ' --suite_setup="" - code to execute for setup of entire suite', + ' --suite_teardown="" - code to execute for teardown of entire suite', + ' --use_param_tests=1 - enable parameterized tests (disabled by default)', + ' --omit_begin_end=1 - omit calls to UnityBegin and UnityEnd (disabled by default)', + ' --header_file="" - path/name of test header file to generate too'].join("\n") + exit 1 + end + + # create the default test runner name if not specified + ARGV[1] = ARGV[0].gsub('.c', '_Runner.c') unless ARGV[1] + + UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1]) +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/parse_output.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/parse_output.rb new file mode 100644 index 0000000..d72c6e8 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/parse_output.rb @@ -0,0 +1,322 @@ +#============================================================ +# Author: John Theofanopoulos +# A simple parser. Takes the output files generated during the +# build process and extracts information relating to the tests. +# +# Notes: +# To capture an output file under VS builds use the following: +# devenv [build instructions] > Output.txt & type Output.txt +# +# To capture an output file under Linux builds use the following: +# make | tee Output.txt +# +# This script can handle the following output formats: +# - normal output (raw unity) +# - fixture output (unity_fixture.h/.c) +# - fixture output with verbose flag set ("-v") +# +# To use this parser use the following command +# ruby parseOutput.rb [options] [file] +# options: -xml : produce a JUnit compatible XML file +# file: file to scan for results +#============================================================ + +# Parser class for handling the input file +class ParseOutput + def initialize + # internal data + @class_name_idx = 0 + @path_delim = nil + + # xml output related + @xml_out = false + @array_list = false + + # current suite name and statistics + @test_suite = nil + @total_tests = 0 + @test_passed = 0 + @test_failed = 0 + @test_ignored = 0 + end + + # Set the flag to indicate if there will be an XML output file or not + def set_xml_output + @xml_out = true + end + + # If write our output to XML + def write_xml_output + output = File.open('report.xml', 'w') + output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + @array_list.each do |item| + output << item << "\n" + end + end + + # Pushes the suite info as xml to the array list, which will be written later + def push_xml_output_suite_info + # Insert opening tag at front + heading = '<testsuite name="Unity" tests="' + @total_tests.to_s + '" failures="' + @test_failed.to_s + '"' + ' skips="' + @test_ignored.to_s + '">' + @array_list.insert(0, heading) + # Push back the closing tag + @array_list.push '</testsuite>' + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_passed(test_name) + @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '"/>' + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_failed(test_name, reason) + @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">' + @array_list.push ' <failure type="ASSERT FAILED">' + reason + '</failure>' + @array_list.push ' </testcase>' + end + + # Pushes xml output data to the array list, which will be written later + def push_xml_output_ignored(test_name, reason) + @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">' + @array_list.push ' <skipped type="TEST IGNORED">' + reason + '</skipped>' + @array_list.push ' </testcase>' + end + + # This function will try and determine when the suite is changed. This is + # is the name that gets added to the classname parameter. + def test_suite_verify(test_suite_name) + # Split the path name + test_name = test_suite_name.split(@path_delim) + + # Remove the extension and extract the base_name + base_name = test_name[test_name.size - 1].split('.')[0] + + # Return if the test suite hasn't changed + return unless base_name.to_s != @test_suite.to_s + + @test_suite = base_name + printf "New Test: %s\n", @test_suite + end + + # Prepares the line for verbose fixture output ("-v") + def prepare_fixture_line(line) + line = line.sub('IGNORE_TEST(', '') + line = line.sub('TEST(', '') + line = line.sub(')', ',') + line = line.chomp + array = line.split(',') + array.map { |x| x.to_s.lstrip.chomp } + end + + # Test was flagged as having passed so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_passed_unity_fixture(array) + class_name = array[0] + test_name = array[1] + test_suite_verify(class_name) + printf "%-40s PASS\n", test_name + + push_xml_output_passed(test_name) if @xml_out + end + + # Test was flagged as having failed so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_failed_unity_fixture(array) + class_name = array[0] + test_name = array[1] + test_suite_verify(class_name) + reason_array = array[2].split(':') + reason = reason_array[-1].lstrip.chomp + ' at line: ' + reason_array[-4] + + printf "%-40s FAILED\n", test_name + + push_xml_output_failed(test_name, reason) if @xml_out + end + + # Test was flagged as being ignored so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_ignored_unity_fixture(array) + class_name = array[0] + test_name = array[1] + reason = 'No reason given' + if array.size > 2 + reason_array = array[2].split(':') + tmp_reason = reason_array[-1].lstrip.chomp + reason = tmp_reason == 'IGNORE' ? 'No reason given' : tmp_reason + end + test_suite_verify(class_name) + printf "%-40s IGNORED\n", test_name + + push_xml_output_ignored(test_name, reason) if @xml_out + end + + # Test was flagged as having passed so format the output + def test_passed(array) + last_item = array.length - 1 + test_name = array[last_item - 1] + test_suite_verify(array[@class_name_idx]) + printf "%-40s PASS\n", test_name + + return unless @xml_out + + push_xml_output_passed(test_name) if @xml_out + end + + # Test was flagged as having failed so format the line + def test_failed(array) + last_item = array.length - 1 + test_name = array[last_item - 2] + reason = array[last_item].chomp.lstrip + ' at line: ' + array[last_item - 3] + class_name = array[@class_name_idx] + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + + test_suite = array2[0].sub('TEST(', '') + test_suite = test_suite.sub(',', '') + class_name = test_suite + + test_name = array2[1].sub(')', '') + end + + test_suite_verify(class_name) + printf "%-40s FAILED\n", test_name + + push_xml_output_failed(test_name, reason) if @xml_out + end + + # Test was flagged as being ignored so format the output + def test_ignored(array) + last_item = array.length - 1 + test_name = array[last_item - 2] + reason = array[last_item].chomp.lstrip + class_name = array[@class_name_idx] + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + + test_suite = array2[0].sub('TEST(', '') + test_suite = test_suite.sub(',', '') + class_name = test_suite + + test_name = array2[1].sub(')', '') + end + + test_suite_verify(class_name) + printf "%-40s IGNORED\n", test_name + + push_xml_output_ignored(test_name, reason) if @xml_out + end + + # Adjusts the os specific members according to the current path style + # (Windows or Unix based) + def detect_os_specifics(line) + if line.include? '\\' + # Windows X:\Y\Z + @class_name_idx = 1 + @path_delim = '\\' + else + # Unix Based /X/Y/Z + @class_name_idx = 0 + @path_delim = '/' + end + end + + # Main function used to parse the file that was captured. + def process(file_name) + @array_list = [] + + puts 'Parsing file: ' + file_name + + @test_passed = 0 + @test_failed = 0 + @test_ignored = 0 + puts '' + puts '=================== RESULTS =====================' + puts '' + File.open(file_name).each do |line| + # Typical test lines look like these: + # ---------------------------------------------------- + # 1. normal output: + # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0 + # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented + # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS + # + # 2. fixture output + # <path>/<test_file>.c:63:TEST(<test_group>, <test_function>):FAIL: Expected 0x00001234 Was 0x00005A5A + # <path>/<test_file>.c:36:TEST(<test_group>, <test_function>):IGNORE + # Note: "PASS" information won't be generated in this mode + # + # 3. fixture output with verbose information ("-v") + # TEST(<test_group, <test_file>)<path>/<test_file>:168::FAIL: Expected 0x8D Was 0x8C + # TEST(<test_group>, <test_file>)<path>/<test_file>:22::IGNORE: This Test Was Ignored On Purpose + # IGNORE_TEST(<test_group, <test_file>) + # TEST(<test_group, <test_file>) PASS + # + # Note: Where path is different on Unix vs Windows devices (Windows leads with a drive letter)! + detect_os_specifics(line) + line_array = line.split(':') + + # If we were able to split the line then we can look to see if any of our target words + # were found. Case is important. + next unless (line_array.size >= 4) || (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(') + + # check if the output is fixture output (with verbose flag "-v") + if (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(') + line_array = prepare_fixture_line(line) + if line.include? ' PASS' + test_passed_unity_fixture(line_array) + @test_passed += 1 + elsif line.include? 'FAIL' + test_failed_unity_fixture(line_array) + @test_failed += 1 + elsif line.include? 'IGNORE' + test_ignored_unity_fixture(line_array) + @test_ignored += 1 + end + # normal output / fixture output (without verbose "-v") + elsif line.include? ':PASS' + test_passed(line_array) + @test_passed += 1 + elsif line.include? ':FAIL' + test_failed(line_array) + @test_failed += 1 + elsif line.include? ':IGNORE:' + test_ignored(line_array) + @test_ignored += 1 + elsif line.include? ':IGNORE' + line_array.push('No reason given') + test_ignored(line_array) + @test_ignored += 1 + end + @total_tests = @test_passed + @test_failed + @test_ignored + end + puts '' + puts '=================== SUMMARY =====================' + puts '' + puts 'Tests Passed : ' + @test_passed.to_s + puts 'Tests Failed : ' + @test_failed.to_s + puts 'Tests Ignored : ' + @test_ignored.to_s + + return unless @xml_out + + # push information about the suite + push_xml_output_suite_info + # write xml output file + write_xml_output + end +end + +# If the command line has no values in, used a default value of Output.txt +parse_my_file = ParseOutput.new + +if ARGV.size >= 1 + ARGV.each do |arg| + if arg == '-xml' + parse_my_file.set_xml_output + else + parse_my_file.process(arg) + break + end + end +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/run_test.erb b/CAN_App/vendor/ceedling/vendor/unity/auto/run_test.erb new file mode 100644 index 0000000..f91b566 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/run_test.erb @@ -0,0 +1,37 @@ +/*=======Test Runner Used To Run Each Test=====*/ +static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num) +{ + Unity.CurrentTestName = name; + Unity.CurrentTestLineNumber = line_num; +#ifdef UNITY_USE_COMMAND_LINE_ARGS + if (!UnityTestMatches()) + return; +#endif + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + CMock_Init(); + if (TEST_PROTECT()) + { +<% if @options[:plugins].include?(:cexception) %> + CEXCEPTION_T e; + Try { + <%= @options[:setup_name] %>(); + func(); + } Catch(e) { + TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); + } +<% else %> + <%= @options[:setup_name] %>(); + func(); +<% end %> + } + if (TEST_PROTECT()) + { + <%= @options[:teardown_name] %>(); + CMock_Verify(); + } + CMock_Destroy(); + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb new file mode 100644 index 0000000..e01f791 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb @@ -0,0 +1,251 @@ +#!/usr/bin/ruby +# +# unity_to_junit.rb +# +require 'fileutils' +require 'optparse' +require 'ostruct' +require 'set' + +require 'pp' + +VERSION = 1.0 + +class ArgvParser + # + # Return a structure describing the options. + # + def self.parse(args) + # The options specified on the command line will be collected in *options*. + # We set default values here. + options = OpenStruct.new + options.results_dir = '.' + options.root_path = '.' + options.out_file = 'results.xml' + + opts = OptionParser.new do |o| + o.banner = 'Usage: unity_to_junit.rb [options]' + + o.separator '' + o.separator 'Specific options:' + + o.on('-r', '--results <dir>', 'Look for Unity Results files here.') do |results| + # puts "results #{results}" + options.results_dir = results + end + + o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path| + options.root_path = root_path + end + + o.on('-o', '--output <filename>', 'XML file to generate.') do |out_file| + # puts "out_file: #{out_file}" + options.out_file = out_file + end + + o.separator '' + o.separator 'Common options:' + + # No argument, shows at tail. This will print an options summary. + o.on_tail('-h', '--help', 'Show this message') do + puts o + exit + end + + # Another typical switch to print the version. + o.on_tail('--version', 'Show version') do + puts "unity_to_junit.rb version #{VERSION}" + exit + end + end + + opts.parse!(args) + options + end +end + +class UnityToJUnit + include FileUtils::Verbose + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root, :out_file + + def initialize + @report = '' + @unit_name = '' + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + # puts "Output File: #{@out_file}" + f = File.new(@out_file, 'w') + write_xml_header(f) + write_suites_header(f) + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + result_output = get_details(result_file, lines) + tests, failures, ignored = parse_test_summary(lines) + result_output[:counts][:total] = tests + result_output[:counts][:failed] = failures + result_output[:counts][:ignored] = ignored + result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored]) + + # use line[0] from the test output to get the test_file path and name + test_file_str = lines[0].tr('\\', '/') + test_file_str = test_file_str.split(':') + test_file = if test_file_str.length < 2 + result_file + else + test_file_str[0] + ':' + test_file_str[1] + end + result_output[:source][:path] = File.dirname(test_file) + result_output[:source][:file] = File.basename(test_file) + + # save result_output + @unit_name = File.basename(test_file, '.*') + + write_suite_header(result_output[:counts], f) + write_failures(result_output, f) + write_tests(result_output, f) + write_ignored(result_output, f) + write_suite_footer(f) + end + write_suites_footer(f) + f.close + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts 'Usage: unity_to_junit.rb [options]' + puts '' + puts 'Specific options:' + puts ' -r, --results <dir> Look for Unity Results files here.' + puts ' -p, --root_path <path> Prepend this path to files in results.' + puts ' -o, --output <filename> XML file to generate.' + puts '' + puts 'Common options:' + puts ' -h, --help Show this message' + puts ' --version Show version' + + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = results_structure + lines.each do |line| + line = line.tr('\\', '/') + _src_file, src_line, test_name, status, msg = line.split(/:/) + case status + when 'IGNORE' then results[:ignores] << { test: test_name, line: src_line, message: msg } + when 'FAIL' then results[:failures] << { test: test_name, line: src_line, message: msg } + when 'PASS' then results[:successes] << { test: test_name, line: src_line, message: msg } + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end + + private + + def results_structure + { + source: { path: '', file: '' }, + successes: [], + failures: [], + ignores: [], + counts: { total: 0, passed: 0, failed: 0, ignored: 0 }, + stdout: [] + } + end + + def write_xml_header(stream) + stream.puts "<?xml version='1.0' encoding='utf-8' ?>" + end + + def write_suites_header(stream) + stream.puts '<testsuites>' + end + + def write_suite_header(counts, stream) + stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">" + end + + def write_failures(results, stream) + result = results[:failures] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">" + stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>" + stream.puts "\t\t\t<system-err> [File] #{filename} [Line] #{item[:line]} </system-err>" + stream.puts "\t\t</testcase>" + end + end + + def write_tests(results, stream) + result = results[:successes] + result.each do |item| + stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />" + end + end + + def write_ignored(results, stream) + result = results[:ignores] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + puts "Writing ignored tests for test harness: #{filename}" + stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">" + stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>" + stream.puts "\t\t\t<system-err> [File] #{filename} [Line] #{item[:line]} </system-err>" + stream.puts "\t\t</testcase>" + end + end + + def write_suite_footer(stream) + stream.puts "\t</testsuite>" + end + + def write_suites_footer(stream) + stream.puts '</testsuites>' + end +end + +if $0 == __FILE__ + # parse out the command options + options = ArgvParser.parse(ARGV) + + # create an instance to work with + utj = UnityToJUnit.new + begin + # look in the specified or current directory for result files + targets = "#{options.results_dir.tr('\\', '/')}**/*.test*" + + results = Dir[targets] + + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + + utj.targets = results + + # set the root path + utj.root = options.root_path + + # set the output XML file name + # puts "Output File from options: #{options.out_file}" + utj.out_file = options.out_file + + # run the summarizer + puts utj.run + rescue StandardError => e + utj.usage e.message + end +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/test_file_filter.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/test_file_filter.rb new file mode 100644 index 0000000..5c3a79f --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/test_file_filter.rb @@ -0,0 +1,25 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +require'yaml' + +module RakefileHelpers + class TestFileFilter + def initialize(all_files = false) + @all_files = all_files + + return unless @all_files + return unless File.exist?('test_file_filter.yml') + + filters = YAML.load_file('test_file_filter.yml') + @all_files = filters[:all_files] + @only_files = filters[:only_files] + @exclude_files = filters[:exclude_files] + end + + attr_accessor :all_files, :only_files, :exclude_files + end +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb new file mode 100644 index 0000000..dafb882 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb @@ -0,0 +1,6 @@ +module TypeSanitizer + def self.sanitize_c_identifier(unsanitized) + # convert filename to valid C identifier by replacing invalid chars with '_' + unsanitized.gsub(/[-\/\\\.\,\s]/, '_') + end +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.py b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.py new file mode 100644 index 0000000..00c0da8 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.py @@ -0,0 +1,139 @@ +#! python3 +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de +# [Released under MIT License. Please refer to license.txt for details] +# Based on the ruby script by Mike Karlesky, Mark VanderVoord, Greg Williams +# ========================================== +import sys +import os +import re +from glob import glob + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + details = self.get_details(result_file, lines) + failures = details['failures'] + ignores = details['ignores'] + if len(failures) > 0: failure_output.append('\n'.join(failures)) + if len(ignores) > 0: ignore_output.append('n'.join(ignores)) + tests,failures,ignored = self.parse_test_summary('\n'.join(lines)) + self.total_tests += tests + self.failures += failures + self.ignored += ignored + + if self.ignored > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY IGNORED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "\n".join(ignore_output) + + if self.failures > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY FAILED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += '\n'.join(failure_output) + + self.report += "\n" + self.report += "--------------------------\n" + self.report += "OVERALL UNITY TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored) + self.report += "\n" + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + def usage(self, err_msg=None): + print("\nERROR: ") + if err_msg: + print(err_msg) + print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") + print(" result_file_directory - The location of your results files.") + print(" Defaults to current directory if not specified.") + print(" Should end in / if specified.") + print(" root_path - Helpful for producing more verbose output if using relative paths.") + sys.exit(1) + + def get_details(self, result_file, lines): + results = { 'failures': [], 'ignores': [], 'successes': [] } + for line in lines: + parts = line.split(':') + if len(parts) == 5: + src_file,src_line,test_name,status,msg = parts + elif len(parts) == 4: + src_file,src_line,test_name,status = parts + msg = '' + else: + continue + if len(self.root) > 0: + line_out = "%s%s" % (self.root, line) + else: + line_out = line + if status == 'IGNORE': + results['ignores'].append(line_out) + elif status == 'FAIL': + results['failures'].append(line_out) + elif status == 'PASS': + results['successes'].append(line_out) + return results + + def parse_test_summary(self, summary): + m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary) + if not m: + raise Exception("Couldn't parse test results: %s" % summary) + + return int(m.group(1)), int(m.group(2)), int(m.group(3)) + + +if __name__ == '__main__': + uts = UnityTestSummary() + try: + #look in the specified or current directory for result files + if len(sys.argv) > 1: + targets_dir = sys.argv[1] + else: + targets_dir = './' + targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '**/*.test*', recursive=True))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) + uts.set_targets(targets) + + #set the root path + if len(sys.argv) > 2: + root_path = sys.argv[2] + else: + root_path = os.path.split(__file__)[0] + uts.set_root_path(root_path) + + #run the summarizer + print(uts.run()) + except Exception as e: + uts.usage(e) diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb new file mode 100644 index 0000000..b3fe8a6 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb @@ -0,0 +1,135 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# !/usr/bin/ruby +# +# unity_test_summary.rb +# +require 'fileutils' +require 'set' + +class UnityTestSummary + include FileUtils::Verbose + + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root + + def initialize(_opts = {}) + @report = '' + @total_tests = 0 + @failures = 0 + @ignored = 0 + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + output = get_details(result_file, lines) + failure_output << output[:failures] unless output[:failures].empty? + ignore_output << output[:ignores] unless output[:ignores].empty? + tests, failures, ignored = parse_test_summary(lines) + @total_tests += tests + @failures += failures + @ignored += ignored + end + + if @ignored > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY IGNORED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += ignore_output.flatten.join("\n") + end + + if @failures > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY FAILED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += failure_output.flatten.join("\n") + end + + @report += "\n" + @report += "--------------------------\n" + @report += "OVERALL UNITY TEST SUMMARY\n" + @report += "--------------------------\n" + @report += "#{@total_tests} TOTAL TESTS #{@failures} TOTAL FAILURES #{@ignored} IGNORED\n" + @report += "\n" + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts "\nUsage: unity_test_summary.rb result_file_directory/ root_path/" + puts ' result_file_directory - The location of your results files.' + puts ' Defaults to current directory if not specified.' + puts ' Should end in / if specified.' + puts ' root_path - Helpful for producing more verbose output if using relative paths.' + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = { failures: [], ignores: [], successes: [] } + lines.each do |line| + _src_file, _src_line, _test_name, status, _msg = line.split(/:/) + line_out = (@root && (@root != 0) ? "#{@root}#{line}" : line).gsub(/\//, '\\') + case status + when 'IGNORE' then results[:ignores] << line_out + when 'FAIL' then results[:failures] << line_out + when 'PASS' then results[:successes] << line_out + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end +end + +if $0 == __FILE__ + + # parse out the command options + opts, args = ARGV.partition { |v| v =~ /^--\w+/ } + opts.map! { |v| v[2..-1].to_sym } + + # create an instance to work with + uts = UnityTestSummary.new(opts) + + begin + # look in the specified or current directory for result files + args[0] ||= './' + targets = "#{ARGV[0].tr('\\', '/')}**/*.test*" + results = Dir[targets] + + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + + uts.targets = results + + # set the root path + args[1] ||= Dir.pwd + '/' + uts.root = ARGV[1] + + # run the summarizer + puts uts.run + rescue StandardError => e + uts.usage e.message + end +end diff --git a/CAN_App/vendor/ceedling/vendor/unity/auto/unity_to_junit.py b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_to_junit.py new file mode 100644 index 0000000..71dd568 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/auto/unity_to_junit.py @@ -0,0 +1,146 @@ +import sys +import os +from glob import glob + +from pyparsing import * +from junit_xml import TestSuite, TestCase + + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + self.targets = 0 + self.root = None + self.test_suites = dict() + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + # define an expression for your file reference + entry_one = Combine( + oneOf(list(alphas)) + ':/' + + Word(alphanums + '_-./')) + + entry_two = Word(printables + ' ', excludeChars=':') + entry = entry_one | entry_two + + delimiter = Literal(':').suppress() + tc_result_line = Group(entry.setResultsName('tc_file_name') + delimiter + entry.setResultsName( + 'tc_line_nr') + delimiter + entry.setResultsName('tc_name') + delimiter + entry.setResultsName( + 'tc_status') + Optional( + delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") + + eol = LineEnd().suppress() + sol = LineStart().suppress() + blank_line = sol + eol + + tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName( + "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName( + "tc_summary") + tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result") + + # run it and see... + pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line) + pp1.ignore(blank_line | OneOrMore("-")) + + result = list() + for l in lines: + result.append((pp1.parseString(l)).asDict()) + # delete empty results + result = filter(None, result) + + tc_list = list() + for r in result: + if 'tc_line' in r: + tmp_tc_line = r['tc_line'] + + # get only the file name which will be used as the classname + file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] + tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name) + if 'tc_status' in tmp_tc_line: + if str(tmp_tc_line['tc_status']) == 'IGNORE': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_skipped_info(message=" ") + elif str(tmp_tc_line['tc_status']) == 'FAIL': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_failure_info(message=" ") + + tc_list.append((str(result_file), tmp_tc)) + + for k, v in tc_list: + try: + self.test_suites[k].append(v) + except KeyError: + self.test_suites[k] = [v] + ts = [] + for suite_name in self.test_suites: + ts.append(TestSuite(suite_name, self.test_suites[suite_name])) + + with open('result.xml', 'w') as f: + TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8') + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + @staticmethod + def usage(err_msg=None): + print("\nERROR: ") + if err_msg: + print(err_msg) + print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") + print(" result_file_directory - The location of your results files.") + print(" Defaults to current directory if not specified.") + print(" Should end in / if specified.") + print(" root_path - Helpful for producing more verbose output if using relative paths.") + sys.exit(1) + + +if __name__ == '__main__': + uts = UnityTestSummary() + try: + # look in the specified or current directory for result files + if len(sys.argv) > 1: + targets_dir = sys.argv[1] + else: + targets_dir = './' + targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) + uts.set_targets(targets) + + # set the root path + if len(sys.argv) > 2: + root_path = sys.argv[2] + else: + root_path = os.path.split(__file__)[0] + uts.set_root_path(root_path) + + # run the summarizer + print(uts.run()) + except Exception as e: + UnityTestSummary.usage(e) diff --git a/CAN_App/vendor/ceedling/vendor/unity/src/meson.build b/CAN_App/vendor/ceedling/vendor/unity/src/meson.build new file mode 100644 index 0000000..1c7b426 --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/src/meson.build @@ -0,0 +1,11 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams. +# +# license: MIT +# +unity_dir = include_directories('.') + +unity_lib = static_library(meson.project_name(), + files('unity.c'), + include_directories: unity_dir) diff --git a/CAN_App/vendor/ceedling/vendor/unity/src/unity.c b/CAN_App/vendor/ceedling/vendor/unity/src/unity.c new file mode 100644 index 0000000..764a42b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/src/unity.c @@ -0,0 +1,2110 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#include "unity.h" +#include <stddef.h> + +#ifdef AVR +#include <avr/pgmspace.h> +#else +#define PROGMEM +#endif + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } +#define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } +#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) TEST_ABORT() + +struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +const char PROGMEM UnityStrOk[] = "\033[42mOK\033[00m"; +const char PROGMEM UnityStrPass[] = "\033[42mPASS\033[00m"; +const char PROGMEM UnityStrFail[] = "\033[41mFAIL\033[00m"; +const char PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; +#else +const char PROGMEM UnityStrOk[] = "OK"; +const char PROGMEM UnityStrPass[] = "PASS"; +const char PROGMEM UnityStrFail[] = "FAIL"; +const char PROGMEM UnityStrIgnore[] = "IGNORE"; +#endif +static const char PROGMEM UnityStrNull[] = "NULL"; +static const char PROGMEM UnityStrSpacer[] = ". "; +static const char PROGMEM UnityStrExpected[] = " Expected "; +static const char PROGMEM UnityStrWas[] = " Was "; +static const char PROGMEM UnityStrGt[] = " to be greater than "; +static const char PROGMEM UnityStrLt[] = " to be less than "; +static const char PROGMEM UnityStrOrEqual[] = "or equal to "; +static const char PROGMEM UnityStrNotEqual[] = " to be not equal to "; +static const char PROGMEM UnityStrElement[] = " Element "; +static const char PROGMEM UnityStrByte[] = " Byte "; +static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; +static const char PROGMEM UnityStrDelta[] = " Values Not Within Delta "; +static const char PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char PROGMEM UnityStrNot[] = "Not "; +static const char PROGMEM UnityStrInf[] = "Infinity"; +static const char PROGMEM UnityStrNegInf[] = "Negative Infinity"; +static const char PROGMEM UnityStrNaN[] = "NaN"; +static const char PROGMEM UnityStrDet[] = "Determinate"; +static const char PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; +const char PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char PROGMEM UnityStrBreaker[] = "-----------------------"; +static const char PROGMEM UnityStrResultsTests[] = " Tests "; +static const char PROGMEM UnityStrResultsFailures[] = " Failures "; +static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; +#ifndef UNITY_EXCLUDE_DETAILS +static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; +#endif +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +/* Local helper function to print characters. */ +static void UnityPrintChar(const char* pch) +{ + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } +} + +/*-----------------------------------------------*/ +/* Local helper function to print ANSI escape strings e.g. "\033[42m". */ +#ifdef UNITY_OUTPUT_COLOR +static UNITY_UINT UnityPrintAnsiEscapeString(const char* string) +{ + const char* pch = string; + UNITY_UINT count = 0; + + while (*pch && (*pch != 'm')) + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + count++; + } + UNITY_OUTPUT_CHAR('m'); + count++; + + return count; +} +#endif + +/*-----------------------------------------------*/ +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + UnityPrintChar(pch); + pch++; + } + } +} +/*-----------------------------------------------*/ +void UnityPrintLen(const char* string, const UNITY_UINT32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && ((UNITY_UINT32)(pch - string) < length)) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (style == UNITY_DISPLAY_STYLE_CHAR) + { + /* printable characters plus CR & LF are printed */ + UNITY_OUTPUT_CHAR('\''); + if ((number <= 126) && (number >= 32)) + { + UNITY_OUTPUT_CHAR((int)number); + } + /* write escaped carriage returns */ + else if (number == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (number == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 2); + } + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrintNumber(number); + } + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned((UNITY_UINT)number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (~number) + 1; + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + + if ((unsigned)nibbles > UNITY_MAX_NIBBLES) + { + nibbles = UNITY_MAX_NIBBLES; + } + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* + * This function prints a floating-point value in a format similar to + * printf("%.7g") on a single-precision machine or printf("%.9g") on a + * double-precision machine. The 7th digit won't always be totally correct + * in single-precision operation (for that level of accuracy, a more + * complicated algorithm would be needed). + */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ +#ifdef UNITY_INCLUDE_DOUBLE + static const int sig_digits = 9; + static const UNITY_INT32 min_scaled = 100000000; + static const UNITY_INT32 max_scaled = 1000000000; +#else + static const int sig_digits = 7; + static const UNITY_INT32 min_scaled = 1000000; + static const UNITY_INT32 max_scaled = 10000000; +#endif + + UNITY_DOUBLE number = input_number; + + /* print minus sign (does not handle negative zero) */ + if (number < 0.0f) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == 0.0f) + { + UnityPrint("0"); + } + else if (isnan(number)) + { + UnityPrint("nan"); + } + else if (isinf(number)) + { + UnityPrint("inf"); + } + else + { + UNITY_INT32 n_int = 0, n; + int exponent = 0; + int decimals, digits; + char buf[16] = {0}; + + /* + * Scale up or down by powers of 10. To minimize rounding error, + * start with a factor/divisor of 10^10, which is the largest + * power of 10 that can be represented exactly. Finally, compute + * (exactly) the remaining power of 10 and perform one more + * multiplication or division. + */ + if (number < 1.0f) + { + UNITY_DOUBLE factor = 1.0f; + + while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } + while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } + + number *= factor; + } + else if (number > (UNITY_DOUBLE)max_scaled) + { + UNITY_DOUBLE divisor = 1.0f; + + while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } + while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } + + number /= divisor; + } + else + { + /* + * In this range, we can split off the integer part before + * doing any multiplications. This reduces rounding error by + * freeing up significant bits in the fractional part. + */ + UNITY_DOUBLE factor = 1.0f; + n_int = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n_int; + + while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } + + number *= factor; + } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + /* round to even if exactly between two integers */ + if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) + n--; +#endif + + n += n_int; + + if (n >= max_scaled) + { + n = min_scaled; + exponent++; + } + + /* determine where to place decimal point */ + decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while ((decimals > 0) && ((n % 10) == 0)) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while ((n != 0) || (digits <= decimals)) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + while (digits > 0) + { + if (digits == decimals) { UNITY_OUTPUT_CHAR('.'); } + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if (exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while ((exponent != 0) || (digits < 2)) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ +#ifdef UNITY_OUTPUT_FOR_ECLIPSE + UNITY_OUTPUT_CHAR('('); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(')'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else +#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH + UnityPrint("<SRCREF line="); + UnityPrintNumber((UNITY_INT)line); + UnityPrint(" file=\""); + UnityPrint(file); + UNITY_OUTPUT_CHAR('"'); + UNITY_OUTPUT_CHAR('>'); + UnityPrint(Unity.CurrentTestName); + UnityPrint("</SRCREF> "); +#else +#ifdef UNITY_OUTPUT_FOR_QT_CREATOR + UnityPrint("file://"); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#endif +#endif +#endif +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EXEC_TIME(); + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg) +{ + if (msg) + { + UnityPrint(UnityStrSpacer); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, + const char* actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char* msg) +{ + /* Both are NULL or same pointer */ + if (expected == actual) { return 0; } + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; } + if ((threshold == actual)) { failed = 1; } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + } + else /* UINT or HEX */ + { + if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +{ \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; } + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if (expect_val != actual_val) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \ + if (UNITY_NAN_CHECK) return 1; \ + (diff) = (actual) - (expected); \ + if ((diff) < 0) (diff) = -(diff); \ + if ((delta) < 0) (delta) = -(delta); \ + return !(isnan(diff) || isinf(diff) || ((diff) > (delta))) + /* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN + #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) +#else + #define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); } +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + else + { + if ((UNITY_UINT)actual > (UNITY_UINT)expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual_val > expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + else + { + if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char* expd = NULL; + const char* act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if ((const void*)expected == (const void*)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char*)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char* const*)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if ((elements == 0) || (length == 0)) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch(size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +/*----------------------------------------------- + * printf helper function + *-----------------------------------------------*/ +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +static void UnityPrintFVA(const char* format, va_list va) +{ + const char* pch = format; + if (pch != NULL) + { + while (*pch) + { + /* format identification character */ + if (*pch == '%') + { + pch++; + + if (pch != NULL) + { + switch (*pch) + { + case 'd': + case 'i': + { + const int number = va_arg(va, int); + UnityPrintNumber((UNITY_INT)number); + break; + } +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + case 'f': + case 'g': + { + const double number = va_arg(va, double); + UnityPrintFloat((UNITY_DOUBLE)number); + break; + } +#endif + case 'u': + { + const unsigned int number = va_arg(va, unsigned int); + UnityPrintNumberUnsigned((UNITY_UINT)number); + break; + } + case 'b': + { + const unsigned int number = va_arg(va, unsigned int); + const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('b'); + UnityPrintMask(mask, (UNITY_UINT)number); + break; + } + case 'x': + case 'X': + case 'p': + { + const unsigned int number = va_arg(va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 8); + break; + } + case 'c': + { + const int ch = va_arg(va, int); + UnityPrintChar((const char *)&ch); + break; + } + case 's': + { + const char * string = va_arg(va, const char *); + UnityPrint(string); + break; + } + case '%': + { + UnityPrintChar(pch); + break; + } + default: + { + /* print the unknown format character */ + UNITY_OUTPUT_CHAR('%'); + UnityPrintChar(pch); + break; + } + } + } + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + else if (*pch == '\n') + { + UNITY_PRINT_EOL(); + } + else + { + UnityPrintChar(pch); + } + + pch++; + } + } +} + +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if(format != NULL) + { + UnityPrint(": "); + va_list va; + va_start(va, format); + UnityPrintFVA(format, va); + va_end(va); + } + UNITY_PRINT_EOL(); +} +#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ + + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_PRINT_EOL(); +} + +/*-----------------------------------------------*/ +/* If we have not defined our own test runner, then include our default test runner to make life easier */ +#ifndef UNITY_SKIP_DEFAULT_RUNNER +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} +#endif + +/*-----------------------------------------------*/ +void UnitySetTestFile(const char* filename) +{ + Unity.TestFile = filename; +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char* UnityOptionIncludeNamed = NULL; +char* UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; + +/*-----------------------------------------------*/ +int UnityParseOptions(int argc, char** argv) +{ + int i; + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + if (argv[i][2] == '=') + { + UnityOptionIncludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionIncludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + { + UnityOptionExcludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionExcludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + return 1; + } + } + } + + return 0; +} + +/*-----------------------------------------------*/ +int IsStringInBiggerString(const char* longstring, const char* shortstring) +{ + const char* lptr = longstring; + const char* sptr = shortstring; + const char* lnext = lptr; + + if (*sptr == '*') + { + return 1; + } + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + /* We're done if we match the entire string or up to a wildcard */ + if (*sptr == '*') + return 1; + if (*sptr == ',') + return 1; + if (*sptr == '"') + return 1; + if (*sptr == '\'') + return 1; + if (*sptr == ':') + return 2; + if (*sptr == 0) + return 1; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + + return 0; +} + +/*-----------------------------------------------*/ +int UnityStringArgumentMatches(const char* str) +{ + int retval; + const char* ptr1; + const char* ptr2; + const char* ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + { + ptr1++; + } + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + { + ptrf = &ptr2[1]; + } + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + { + ptr2++; + } + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + { + return retval; + } + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + { + return 1; + } + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + { + return 1; + } + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +/*-----------------------------------------------*/ +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + { + retval = 1; + } + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + { + retval = 0; + } + } + + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/CAN_App/vendor/ceedling/vendor/unity/src/unity.h b/CAN_App/vendor/ceedling/vendor/unity/src/unity.h new file mode 100644 index 0000000..338df0b --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/src/unity.h @@ -0,0 +1,661 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#define UNITY_VERSION_MAJOR 2 +#define UNITY_VERSION_MINOR 5 +#define UNITY_VERSION_BUILD 4 +#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. + * If using unity directly, these will need to be provided for each test + * executable built. If you are using the test runner generator and/or + * Ceedling, these are optional. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). If using + * Unity directly, you're in charge of calling these if they are desired. + * If using Ceedling or the test runner generator, these will be called + * automatically if they exist. */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/*------------------------------------------------------- + * Test Reset and Verify + *-------------------------------------------------------*/ + +/* These functions are intended to be called before during tests in order + * to support complex test loops, etc. Both are NOT built into Unity. Instead + * the test runner generator will create them. resetTest will run teardown and + * setup again, verifying any end-of-test needs between. verifyTest will only + * run the verification. */ +void resetTest(void); +void verifyTest(void); + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in <stdint.h> + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in <limits.h> + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) +#define TEST_ONLY() +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__) +#endif + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() +#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while(0) + +/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out + * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ +#define TEST_FILE(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") +#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") +#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal") +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) +#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message) +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h b/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h new file mode 100644 index 0000000..2c91b6d --- /dev/null +++ b/CAN_App/vendor/ceedling/vendor/unity/src/unity_internals.h @@ -0,0 +1,1053 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include <setjmp.h> +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include <math.h> +#endif + +#ifndef UNITY_EXCLUDE_STDDEF_H +#include <stddef.h> +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#include <stdarg.h> +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in <limits.h>, or default to 32 bits + * Attempt 2: UINTPTR_MAX in <stdint.h>, or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include <stdint.h> +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include <limits.h> +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define UNITY_FUNCTION_ATTR(a) __attribute__((a)) +#else + #define UNITY_FUNCTION_ATTR(a) /* ignore */ +#endif + +#ifndef UNITY_NORETURN + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define UNITY_NORETURN [[ noreturn ]] + #endif + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #include <stdnoreturn.h> + #define UNITY_NORETURN noreturn + #endif +#endif +#ifndef UNITY_NORETURN + #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn) +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Also sizeof(int) does return the size in addressable units on all platforms, + * which may not necessarily be the size in bytes. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_INT_WIDTH (32) + #endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_LONG_WIDTH (32) + #endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH + #endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char UNITY_UINT8; + typedef unsigned short UNITY_UINT16; + typedef unsigned int UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed short UNITY_INT16; + typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char UNITY_UINT8; + typedef unsigned int UNITY_UINT16; + typedef unsigned long UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed int UNITY_INT16; + typedef signed long UNITY_INT32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +/* Auto-detect 64 Bit Support */ +#ifndef UNITY_SUPPORT_64 + #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 + #define UNITY_SUPPORT_64 + #endif +#endif + +/* 64-Bit Support Dependent Configuration */ +#ifndef UNITY_SUPPORT_64 + /* No 64-bit Support */ + typedef UNITY_UINT32 UNITY_UINT; + typedef UNITY_INT32 UNITY_INT; + #define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */ +#else + /* 64-bit Support */ + #if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long UNITY_UINT64; + typedef signed long long UNITY_INT64; + #elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long UNITY_UINT64; + typedef signed long UNITY_INT64; + #else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) + #endif + typedef UNITY_UINT64 UNITY_UINT; + typedef UNITY_INT64 UNITY_INT; + #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */ +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) + #define UNITY_PTR_TO_INT UNITY_INT32 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) + #define UNITY_PTR_TO_INT UNITY_INT64 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) + #define UNITY_PTR_TO_INT UNITY_INT16 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE + #define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR + #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +#endif + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isinf & isnan macros should be provided by math.h */ +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define isinf(n) (isnan((n) - (n)) && !isnan(n)) +#endif + +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + + /* No Floating Point Support */ + #ifndef UNITY_EXCLUDE_DOUBLE + #define UNITY_EXCLUDE_DOUBLE + #else + #undef UNITY_INCLUDE_DOUBLE + #endif + + #ifndef UNITY_EXCLUDE_FLOAT + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_FLOAT UNITY_DOUBLE; + /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ + #endif + +#else + + /* Double Floating Point Support */ + #ifndef UNITY_DOUBLE_PRECISION + #define UNITY_DOUBLE_PRECISION (1e-12) + #endif + + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR + /* Default to using putchar, which is defined in stdio.h */ + #include <stdio.h> + #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION + extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH + #ifdef UNITY_USE_FLUSH_STDOUT + /* We want to use the stdout flush utility */ + #include <stdio.h> + #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) + #else + /* We've specified nothing, therefore flush should just be ignored */ + #define UNITY_OUTPUT_FLUSH() + #endif +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION + extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +#ifdef UNITY_INCLUDE_EXEC_TIME + #if !defined(UNITY_EXEC_TIME_START) && \ + !defined(UNITY_EXEC_TIME_STOP) && \ + !defined(UNITY_PRINT_EXEC_TIME) && \ + !defined(UNITY_TIME_TYPE) + /* If none any of these macros are defined then try to provide a default implementation */ + + #if defined(UNITY_CLOCK_MS) + /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */ + #define UNITY_TIME_TYPE UNITY_UINT + #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS() + #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS() + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(_WIN32) + #include <time.h> + #define UNITY_TIME_TYPE clock_t + #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(__unix__) || defined(__APPLE__) + #include <time.h> + #define UNITY_TIME_TYPE struct timespec + #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \ + execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #endif + #endif +#endif + +#ifndef UNITY_EXEC_TIME_START +#define UNITY_EXEC_TIME_START() do{}while(0) +#endif + +#ifndef UNITY_EXEC_TIME_STOP +#define UNITY_EXEC_TIME_STOP() do{}while(0) +#endif + +#ifndef UNITY_TIME_TYPE +#define UNITY_TIME_TYPE UNITY_UINT +#endif + +#ifndef UNITY_PRINT_EXEC_TIME +#define UNITY_PRINT_EXEC_TIME() do{}while(0) +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) +#define UNITY_DISPLAY_RANGE_CHAR (0x80) + +typedef enum +{ + UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + + UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT, + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_WITHIN = 0x0, + UNITY_EQUAL_TO = 0x1, + UNITY_GREATER_THAN = 0x2, + UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 0x4, + UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, + UNITY_NOT_EQUAL = 0x0, + UNITY_UNKNOWN +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY, + UNITY_ARRAY_UNKNOWN +} UNITY_FLAGS_T; + +struct UNITY_STORAGE_T +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifdef UNITY_INCLUDE_EXEC_TIME + UNITY_TIME_TYPE CurrentTestStartTime; + UNITY_TIME_TYPE CurrentTestStopTime; +#endif +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnitySetTestFile(const char* filename); +void UnityConcludeTest(void); + +#ifndef RUN_TEST +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); +#else +#define UNITY_SKIP_DEFAULT_RUNNER +#endif + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#define UNITY_CLR_DETAILS() { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAIL(d1) { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAILS(d1,d2) { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = (d2); } + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +#ifdef UNITY_PRINT_TEST_CONTEXT +void UNITY_PRINT_TEST_CONTEXT(void); +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...); +#endif + +void UnityPrintLen(const char* string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number_to_print); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +#ifndef UNITY_EXCLUDE_SETJMP_H +UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line); +UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#else +void UnityFail(const char* message, const UNITY_LINE_TYPE line); +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#endif + +void UnityMessage(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrOk[]; +extern const char UnityStrPass[]; +extern const char UnityStrFail[]; +extern const char UnityStrIgnore[]; + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; +extern const char UnityStrErrShorthand[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_PROTECT() 1 +#define TEST_ABORT() return +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define UNITY_SUPPORT_VARIADIC_MACROS +#endif +#endif +#ifdef UNITY_SUPPORT_VARIADIC_MACROS +#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +#ifndef UNITY_SHORTHAND_AS_INT +#ifndef UNITY_SHORTHAND_AS_MEM +#ifndef UNITY_SHORTHAND_AS_NONE +#ifndef UNITY_SHORTHAND_AS_RAW +#define UNITY_SHORTHAND_AS_OLD +#endif +#endif +#endif +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char** argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) do {if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));}} while(0) +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16) (expected), (UNITY_INT)(UNITY_INT16) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32) (expected), (UNITY_INT)(UNITY_INT32) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) (expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/InstallingCeedlingUnitTestTools.pdf b/InstallingCeedlingUnitTestTools.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b19cf1016c5a7aac08407df64a62b2204d836a6b GIT binary patch literal 260555 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�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℘Wu%v%Sc!$f{?CxO7DBnp1&tUc=esZQ4eWiViEQ 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 O0GdQBEYd16ME?&IF|-2! literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 81349f7..b316b90 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,30 @@ +# Unit Test Skeleton Example +The unit tests are configured in the CAN_App to execute using the ceedling gem script. This docker container has all the tools needed to run ceedling. If you do not have Docker Desktop installed you can install the tools locally on your PC or in a VM. The root folder contains InstallingCeedlingUnitTestTools.pdf which has instructions that I've summarized for installing the tools on your PC. A restart is usually required to finish the GCC install. +To run the tests, open Command Promp, navigate to the CAN_App folder, and execute ceedling. If everything is installed correctly you will see the following messages. +Test 'test_CAN.c' +----------------- +Running test_CAN.out... + + +Test 'test_CAN_Driver.c' +------------------------ +Running test_CAN_Driver.out... + +-------------------- +IGNORED TEST SUMMARY +-------------------- +[test_CAN_Driver.c] + Test: test_CAN_Driver_NeedToImplement + At line (17): "Need to Implement CAN_Driver" + +-------------------- +OVERALL TEST SUMMARY +-------------------- +TESTED: 3 +PASSED: 2 +FAILED: 0 +IGNORED: 1 + # Mad Science Lab C Unit Testing Docker Image C unit testing environment with Unity, CMock, and Ceedling. The purpose of this docker image is to create an easy-to-install portable system for running unit tests with Ceedling. @@ -20,6 +47,8 @@ image is to create an easy-to-install portable system for running unit tests wit ## Usage `docker run -it --rm -v <local project path>:/project throwtheswitch/madsciencelab[:tag]` +Alternatively, if you do not have docker installed, follw the instructions in InstallingCeedlingUnitTestTools.pdf to install the tools locally. + ## Basic Articles Discussing Unit Testing * This docker image uses Ceedling to build "native" code as described [here](http://www.throwtheswitch.org/build/which) * For additional help and tips for building native tests, read [this](http://www.throwtheswitch.org/build/native) \ No newline at end of file diff --git a/source/src/main.c b/source/src/main.c new file mode 100644 index 0000000..727132a --- /dev/null +++ b/source/src/main.c @@ -0,0 +1,27 @@ +// Example file for CAN SPI project +#include main.h +#include CAN.h + +void main(void) +{ + char dataRxTx[ 8 ]; + uint8_t dataRxLen; + uint8_t msgRcvd; + uint8_t dataTx; + uint32_t idRx; + uint8_t canRcvFlags; + + dataTx = 0xAB; + + msgRcvd = CANSPIRead( &idRx , &dataRxTx , &dataRxLen, &canRcvFlags ); + + if ( msgRcvd ) + { + mikrobus_logWrite( &dataRxTx, _LOG_BYTE ); + Delay_1sec(); + } + +// CANSPIWrite( id2nd, dataTx, 1, canSendFlags ); +// mikrobus_logWrite( "MESSAGE SENT", _LOG_LINE ); +// Delay_1sec(); +} \ No newline at end of file From 2d783de2b2add89748637192fe42585db2ae33f0 Mon Sep 17 00:00:00 2001 From: JohnRenoII <jreno2@gmail.com> Date: Sun, 12 Dec 2021 18:02:51 -0500 Subject: [PATCH 2/2] Delete main.c Deleted dead files. --- source/src/main.c | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 source/src/main.c diff --git a/source/src/main.c b/source/src/main.c deleted file mode 100644 index 727132a..0000000 --- a/source/src/main.c +++ /dev/null @@ -1,27 +0,0 @@ -// Example file for CAN SPI project -#include main.h -#include CAN.h - -void main(void) -{ - char dataRxTx[ 8 ]; - uint8_t dataRxLen; - uint8_t msgRcvd; - uint8_t dataTx; - uint32_t idRx; - uint8_t canRcvFlags; - - dataTx = 0xAB; - - msgRcvd = CANSPIRead( &idRx , &dataRxTx , &dataRxLen, &canRcvFlags ); - - if ( msgRcvd ) - { - mikrobus_logWrite( &dataRxTx, _LOG_BYTE ); - Delay_1sec(); - } - -// CANSPIWrite( id2nd, dataTx, 1, canSendFlags ); -// mikrobus_logWrite( "MESSAGE SENT", _LOG_LINE ); -// Delay_1sec(); -} \ No newline at end of file