ceedling 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +2 -0
  5. data/README.md +4 -1
  6. data/assets/tests_with_conditional_includes/src/feature_config.h +13 -0
  7. data/assets/tests_with_conditional_includes/src/feature_extra.h +13 -0
  8. data/assets/tests_with_conditional_includes/src/local_flag_extra.h +13 -0
  9. data/assets/tests_with_conditional_includes/src/nested_extra.h +13 -0
  10. data/assets/tests_with_conditional_includes/src/nested_wrapper.h +16 -0
  11. data/assets/tests_with_conditional_includes/src/project_flag_extra.h +13 -0
  12. data/assets/tests_with_conditional_includes/src/widget.c +48 -0
  13. data/assets/tests_with_conditional_includes/src/widget.h +15 -0
  14. data/assets/tests_with_conditional_includes/src/widget_feature.c +29 -0
  15. data/assets/tests_with_conditional_includes/src/widget_feature.h +13 -0
  16. data/assets/tests_with_conditional_includes/test/test_widget.c +44 -0
  17. data/assets/tests_with_conditional_includes/test/test_widget_feature.c +31 -0
  18. data/docs/Changelog.md +31 -1
  19. data/docs/KnownIssues.md +1 -0
  20. data/docs/mkdocs/configuration/project-file.md +25 -7
  21. data/docs/mkdocs/configuration/reference/cexception.md +1 -1
  22. data/docs/mkdocs/configuration/reference/cmock.md +1 -1
  23. data/docs/mkdocs/configuration/reference/unity.md +1 -1
  24. data/docs/mkdocs/getting-started/quick-start.md +11 -8
  25. data/docs/mkdocs/plugins/gcov/gcovr.md +324 -189
  26. data/docs/mkdocs/plugins/gcov/reportgenerator.md +113 -31
  27. data/docs/mkdocs/reference/gcov-plugin.md +21 -3
  28. data/docs/mkdocs/snapshot/plugins/gcov/config/defaults.yml +0 -1
  29. data/examples/wondrous_forest/src/SensorHal.h +13 -0
  30. data/examples/wondrous_forest/src/TemperatureSensor.c +12 -5
  31. data/examples/wondrous_forest/src/TemperatureSensor.h +5 -4
  32. data/examples/wondrous_forest/test/TestTemperatureSensor.c +32 -3
  33. data/lib/ceedling/generators/generator_test_results_backtrace.rb +16 -8
  34. data/lib/ceedling/generators/generator_test_runner.rb +25 -1
  35. data/lib/ceedling/partials/partializer.rb +8 -1
  36. data/lib/ceedling/plugins/plugin_reportinator.rb +9 -0
  37. data/lib/ceedling/plugins/report_tests_stdout_plugin.rb +1 -1
  38. data/lib/ceedling/preprocess/preprocessinator.rb +22 -0
  39. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +33 -0
  40. data/lib/version.rb +1 -1
  41. data/plugins/gcov/config/defaults.yml +0 -1
  42. data/plugins/gcov/lib/gcov.rb +35 -16
  43. data/plugins/gcov/lib/gcovr_reportinator.rb +31 -1
  44. data/plugins/valgrind/lib/valgrind.rb +1 -1
  45. data/site-local/configuration/project-file.html +78 -8
  46. data/site-local/configuration/reference/cexception.html +5 -5
  47. data/site-local/configuration/reference/cmock.html +5 -5
  48. data/site-local/configuration/reference/unity.html +5 -5
  49. data/site-local/getting-started/quick-start.html +13 -9
  50. data/site-local/plugins/gcov/gcovr.html +1215 -255
  51. data/site-local/plugins/gcov/reportgenerator.html +404 -46
  52. data/site-local/reference/gcov-plugin.html +40 -7
  53. data/site-local/sitemap.xml.gz +0 -0
  54. data/site-local/snapshot/plugins/gcov/config/defaults.yml +0 -1
  55. data/spec/support/system/spec_system_helper.rb +11 -0
  56. data/spec/support/system/system_context.rb +25 -12
  57. data/spec/system/conditional_include_macro_visibility_spec.rb +152 -0
  58. data/spec/system/deployment_as_gem_spec.rb +2 -0
  59. data/spec/system/deployment_as_vendor_spec.rb +2 -0
  60. data/spec/system/encoding_stress_spec.rb +4 -4
  61. data/spec/system/example_wondrous_forest_spec.rb +2 -2
  62. data/spec/system/gcov_deployment_spec.rb +2 -0
  63. data/spec/system/support/common_test_cases.rb +47 -1
  64. data/spec/system/support/gcov_common_test_cases.rb +52 -0
  65. data/spec/system/test_runner_system_include_spec.rb +90 -0
  66. data/spec/units/generators/generator_test_results_backtrace_spec.rb +51 -0
  67. data/spec/units/generators/generator_test_runner_spec.rb +69 -0
  68. data/spec/units/partials/partializer_spec.rb +69 -0
  69. data/spec/units/plugin_reportinator_spec.rb +38 -0
  70. data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +61 -0
  71. data/spec/units/preprocess/preprocessinator_spec.rb +136 -0
  72. data/vendor/c_exception/lib/CException.h +1 -1
  73. data/vendor/c_exception/project.yml +2 -2
  74. data/vendor/c_exception/test/TestException.c +1 -1
  75. data/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  76. data/vendor/cmock/docs/CMockChangeLog.md +23 -22
  77. data/vendor/cmock/scripts/create_makefile.rb +4 -3
  78. data/vendor/cmock/src/cmock.h +1 -1
  79. data/vendor/cmock/vendor/c_exception/lib/CException.h +1 -1
  80. data/vendor/cmock/vendor/c_exception/project.yml +2 -2
  81. data/vendor/cmock/vendor/c_exception/test/TestException.c +1 -1
  82. data/vendor/cmock/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  83. data/vendor/cmock/vendor/unity/auto/generate_test_runner.rb +85 -29
  84. data/vendor/cmock/vendor/unity/docs/UnityChangeLog.md +1 -0
  85. data/vendor/cmock/vendor/unity/examples/unity_config.h +8 -0
  86. data/vendor/cmock/vendor/unity/src/unity.c +9 -1
  87. data/vendor/cmock/vendor/unity/src/unity.h +1 -1
  88. data/vendor/cmock/vendor/unity/test/rakefile +2 -1
  89. data/vendor/cmock/vendor/unity/test/testdata/testRunnerGeneratorEmpty.c +13 -0
  90. data/vendor/cmock/vendor/unity/test/tests/test_generate_test_runner.rb +31 -0
  91. data/vendor/unity/auto/generate_test_runner.rb +63 -57
  92. data/vendor/unity/docs/UnityChangeLog.md +1 -0
  93. data/vendor/unity/examples/unity_config.h +8 -0
  94. data/vendor/unity/src/unity.c +9 -1
  95. data/vendor/unity/src/unity.h +1 -1
  96. data/vendor/unity/test/rakefile +2 -1
  97. data/vendor/unity/test/tests/test_generate_test_runner.rb +20 -0
  98. metadata +23 -3
  99. data/vendor/cmock/vendor/unity/auto/run_test.erb +0 -37
@@ -4,55 +4,137 @@ The `ReportGenerator` utility may be configured with the following configuration
4
4
 
5
5
  All generated reports are found in `<build root>/artifacts/gcov/ReportGenerator/`.
6
6
 
7
+ ## Example configuration
8
+
7
9
  ```yaml
8
10
  :gcov:
9
11
  :report_generator:
10
- # Optional directory for storing persistent coverage information.
11
- # Can be used in future reports to show coverage evolution.
12
- :history_directory: <path>
12
+ :history_directory: build/artifacts/gcov/history
13
+ :file_filters: "-./vendor/*;-./build/*;-./test/*;+./src/*"
14
+ :verbosity: Warning
15
+ :tag: "release-1.4.0"
16
+ :num_parallel_threads: 4
17
+ :gcov_exclude:
18
+ - some_excluded_file
19
+ :custom_args:
20
+ - "-title:MyProject"
21
+ ```
22
+
23
+ ## Plugin configuration
24
+
25
+ ### `:history_directory`
26
+
27
+ Optional directory for storing persistent coverage information. Can be used
28
+ in future reports to show coverage evolution.
29
+
30
+ ---
31
+
32
+ ### `:plugins`
33
+
34
+ Optional plugin files for custom reports or custom history storage (separated
35
+ by semicolon).
36
+
37
+ **Example:** `plugin.dll;*.dll`
38
+
39
+ ---
40
+
41
+ ### `:assembly_filters`
42
+
43
+ Optional list of assemblies that should be included or excluded in the
44
+ report (separated by semicolon). Exclusion filters take precedence over
45
+ inclusion filters. Wildcards are allowed, but not regular expressions.
46
+
47
+ **Example:** `+<included>;-<excluded>`
48
+
49
+ ---
50
+
51
+ ### `:class_filters`
52
+
53
+ Optional list of classes that should be included or excluded in the report
54
+ (separated by semicolon). Exclusion filters take precedence over inclusion
55
+ filters. Wildcards are allowed, but not regular expressions.
56
+
57
+ **Example:** `+<included>;-<excluded>`
58
+
59
+ ---
60
+
61
+ ### `:file_filters`
62
+
63
+ Optional list of files that should be included or excluded in the report
64
+ (separated by semicolon). Exclusion filters take precedence over inclusion
65
+ filters. Wildcards are allowed, but not regular expressions. Ceedling places
66
+ your own patterns first, ahead of the exclusions it generates automatically
67
+ for test paths, the build root, and (when Partials are in use) Partial
68
+ source files, so your patterns take precedence.
13
69
 
14
- # Optional plugin files for custom reports or custom history storage (separated by semicolon).
15
- :plugins: <plugin.dll>;<*.dll>
70
+ **Example:** `"-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*"`
16
71
 
17
- # Optional list of assemblies that should be included or excluded in the report (separated by semicolon).
18
- # Exclusion filters take precedence over inclusion filters.
19
- # Wildcards are allowed, but not regular expressions.
20
- :assembly_filters: +<included>;-<excluded>
72
+ ---
21
73
 
22
- # Optional list of classes that should be included or excluded in the report (separated by semicolon).
23
- # Exclusion filters take precedence over inclusion filters.
24
- # Wildcards are allowed, but not regular expressions.
25
- :class_filters: +<included>;-<excluded>
74
+ ### `:verbosity`
26
75
 
27
- # Optional list of files that should be included or excluded in the report (separated by semicolon).
28
- # Exclusion filters take precedence over inclusion filters.
29
- # Wildcards are allowed, but not regular expressions.
30
- # Example: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*"
31
- :file_filters: +<included>;-<excluded>
76
+ The verbosity level of the log messages.
32
77
 
33
- # The verbosity level of the log messages.
34
- # Values: Verbose, Info, Warning, Error, Off (defaults to Warning)
35
- :verbosity: <level>
78
+ **Values:** `Verbose`, `Info`, `Warning`, `Error`, `Off`
36
79
 
37
- # Optional tag or build version.
38
- :tag: <tag>
80
+ **Default:** `Warning`
39
81
 
40
- # Optional list of one or more regular expressions to exclude gcov notes files that match these filters.
82
+ ---
83
+
84
+ ### `:tag`
85
+
86
+ Optional tag or build version.
87
+
88
+ ---
89
+
90
+ ### `:gcov_exclude`
91
+
92
+ Optional list of one or more regular expressions to exclude gcov notes
93
+ (`.gcno`) files that match these filters from coverage processing — these
94
+ files are never run through `gcov` at all. A trailing `.gcov` or `.gcno`
95
+ suffix on a pattern is stripped automatically, so either form works.
96
+
97
+ ```yaml
98
+ :gcov:
99
+ :report_generator:
41
100
  :gcov_exclude:
42
101
  - <regex>
43
102
  - ...
103
+ ```
104
+
105
+ **Default:** `[]`
44
106
 
45
- # Optionally set the number of threads to use in parallel. Defaults to 1.
46
- :threads: <count>
107
+ ---
47
108
 
48
- # Optional list of one or more command line arguments to pass to Report Generator.
49
- # Useful for configuring Risk Hotspots and Other Settings.
50
- # https://github.com/danielpalme/ReportGenerator/wiki/Settings
51
- # Note: This can be accomplished with Ceedling's tool configuration options outside of plugin
52
- # configuration but is supported here to collect configuration options in one place.
109
+ ### `:num_parallel_threads`
110
+
111
+ Optionally set the number of threads ReportGenerator uses in parallel. Drives
112
+ both of ReportGenerator's own `numberOfReportsParsedInParallel` and
113
+ `numberOfReportsMergedInParallel` settings together.
114
+
115
+ **Default:** unset (ReportGenerator's own default applies)
116
+
117
+ ---
118
+
119
+ ### `:custom_args`
120
+
121
+ Optional list of one or more command line arguments to pass to Report
122
+ Generator. Useful for configuring Risk Hotspots and other settings not
123
+ covered by the options above. See the
124
+ [ReportGenerator settings wiki](https://github.com/danielpalme/ReportGenerator/wiki/Settings).
125
+
126
+ Note: This can be accomplished with Ceedling's tool configuration options
127
+ outside of plugin configuration but is supported here to collect
128
+ configuration options in one place.
129
+
130
+ ```yaml
131
+ :gcov:
132
+ :report_generator:
53
133
  :custom_args:
54
134
  - <argument>
55
135
  - ...
56
136
  ```
57
137
 
138
+ **Default:** `[]`
139
+
58
140
  <br/><br/>
@@ -213,8 +213,24 @@ Exit with status 4 if total branch coverage is below this percentage.
213
213
  Exit with status 8 if total decision coverage is below this percentage.
214
214
  (`gcovr --fail-under-decision`)
215
215
 
216
+ `gcovr` treats `--fail-under-decision` as a no-op without `--decisions` also
217
+ present — setting this option implies `:decisions` (below) automatically.
218
+
216
219
  **Values:** `1`–`100`
217
220
 
221
+ **Requires:** `gcovr` 7.0 or higher
222
+
223
+ ---
224
+
225
+ ### `:decisions`
226
+
227
+ Report the decision coverage. For HTML, JSON, and the summary report.
228
+ (`gcovr --decisions`)
229
+
230
+ Implied automatically whenever `:fail_under_decision` is set.
231
+
232
+ **Requires:** `gcovr` 6.0 or higher
233
+
218
234
  ---
219
235
 
220
236
  ### `:fail_under_function`
@@ -552,11 +568,13 @@ are excluded from report generation.
552
568
 
553
569
  ---
554
570
 
555
- ### `:threads`
571
+ ### `:num_parallel_threads`
556
572
 
557
- Number of parallel threads to use during report generation.
573
+ Number of parallel threads to use during report generation. Drives both
574
+ ReportGenerator's own `numberOfReportsParsedInParallel` and
575
+ `numberOfReportsMergedInParallel` settings together.
558
576
 
559
- **Default:** `1`
577
+ **Default:** unset (ReportGenerator's own default applies)
560
578
 
561
579
  ---
562
580
 
@@ -27,7 +27,6 @@
27
27
 
28
28
  :report_generator:
29
29
  :verbosity: Warning # Default verbosity
30
- :collection_paths_source: [] # Explicitly defined as default empty array to simplify option validation code
31
30
  :custom_args: [] # Explicitly defined as default empty array to simplify option validation code
32
31
  :gcov_exclude: [] # Explicitly defined as default empty array to simplify option validation code
33
32
  ...
@@ -16,4 +16,17 @@ bool SensorHal_IsChannelReady(SensorChannel_t channel);
16
16
  void SensorHal_StartConversion(SensorChannel_t channel);
17
17
  uint32 SensorHal_GetTimestampMs(void);
18
18
 
19
+ /* Vendor-header-style static inline register accessor -- the same pattern that
20
+ * makes a real hardware header need Partial mocking instead of ordinary mocking,
21
+ * since there is no separate .c file to exclude from a test build and swap for a
22
+ * mock's .o at link time. The real body here stands in for what would otherwise
23
+ * be a direct, unsafe-off-target memory-mapped register read: it always returns
24
+ * the same obviously-wrong sentinel, so a test can tell whether this real body
25
+ * ran (Partial mocking failed to intercept the call) instead of its mock. */
26
+ static inline uint16 SensorHal_RawStatusRegister(SensorChannel_t channel)
27
+ {
28
+ (void)channel;
29
+ return 0xDEADu;
30
+ }
31
+
19
32
  #endif /* SENSOR_HAL_H */
@@ -9,9 +9,10 @@
9
9
  #include "TemperatureSensor.h"
10
10
  #include "SensorHal.h"
11
11
 
12
- static float s_calibration_offset;
13
- static int32 s_last_milli_celsius;
14
- static bool s_reading_valid;
12
+ static float s_calibration_offset;
13
+ static int32 s_last_milli_celsius;
14
+ static bool s_reading_valid;
15
+ static uint16 s_last_status_register;
15
16
 
16
17
  /* Linearized approximation: full-scale 4095 counts maps 0 C to 85 C. */
17
18
  static int32 TemperatureSensor__RawToMilliCelsius(uint16 raw_counts)
@@ -45,8 +46,9 @@ bool TemperatureSensor_Sample(void)
45
46
 
46
47
  if (!SensorHal_IsChannelReady(SENSOR_CHANNEL_TEMP)) { return false; }
47
48
 
48
- raw = SensorHal_ReadChannel(SENSOR_CHANNEL_TEMP);
49
- milli_c = TemperatureSensor__RawToMilliCelsius(raw);
49
+ raw = SensorHal_ReadChannel(SENSOR_CHANNEL_TEMP);
50
+ s_last_status_register = SensorHal_RawStatusRegister(SENSOR_CHANNEL_TEMP);
51
+ milli_c = TemperatureSensor__RawToMilliCelsius(raw);
50
52
  milli_c += (int32)(s_calibration_offset * 1000.0f);
51
53
 
52
54
  s_reading_valid = TemperatureSensor__IsInRange(milli_c);
@@ -65,3 +67,8 @@ bool TemperatureSensor_IsValid(void)
65
67
  {
66
68
  return s_reading_valid;
67
69
  }
70
+
71
+ uint16 TemperatureSensor_GetStatusRegister(void)
72
+ {
73
+ return s_last_status_register;
74
+ }
@@ -10,9 +10,10 @@
10
10
 
11
11
  #include "Types.h"
12
12
 
13
- void TemperatureSensor_Init(float calibration_offset_celsius);
14
- bool TemperatureSensor_Sample(void);
15
- int32 TemperatureSensor_GetMilliCelsius(void);
16
- bool TemperatureSensor_IsValid(void);
13
+ void TemperatureSensor_Init(float calibration_offset_celsius);
14
+ bool TemperatureSensor_Sample(void);
15
+ int32 TemperatureSensor_GetMilliCelsius(void);
16
+ bool TemperatureSensor_IsValid(void);
17
+ uint16 TemperatureSensor_GetStatusRegister(void);
17
18
 
18
19
  #endif /* TEMPERATURE_SENSOR_H */
@@ -5,17 +5,25 @@
5
5
  SPDX-License-Identifier: MIT
6
6
  ========================================================================= */
7
7
 
8
- /* Partials pattern: TEST_PARTIAL_ALL_MODULE
8
+ /* Partials pattern: TEST_PARTIAL_ALL_MODULE + MOCK_PARTIAL_ALL_MODULE
9
9
  * Tests both public functions AND the private static helpers
10
10
  * TemperatureSensor__RawToMilliCelsius() and TemperatureSensor__IsInRange().
11
11
  * Also accesses the file-scoped static s_calibration_offset directly.
12
- * SensorHal is mocked traditionally since it has no private statics. */
12
+ * SensorHal is mocked via Partials (MOCK_PARTIAL_ALL_MODULE) rather than
13
+ * traditionally: SensorHal_RawStatusRegister() is a static inline function,
14
+ * which only Partial mocking (not ordinary CMock header mocking) can
15
+ * intercept -- there is no separate SensorHal.c definition for a static
16
+ * inline function to exclude from this test build and replace with a mock
17
+ * .o at link time, so the call must be redirected to the mock before that,
18
+ * at the #include level. Every existing SensorHal_*_Expect* call below
19
+ * keeps working unchanged either way, since Partial-mocked functions use
20
+ * the identical CMock expectation API as a traditionally mocked module. */
13
21
 
14
22
  #include "unity.h"
15
23
  #include "ceedling.h"
16
- #include "MockSensorHal.h"
17
24
 
18
25
  #include TEST_PARTIAL_ALL_MODULE(TemperatureSensor)
26
+ #include MOCK_PARTIAL_ALL_MODULE(SensorHal)
19
27
 
20
28
  #include "Types.h"
21
29
 
@@ -78,6 +86,7 @@ void test_Sample_ReturnsTrueAndStoresReadingWhenReady(void)
78
86
  /* 1638 counts: (1638 * 85000) / 4095 ~= 34000 milli-C (34 C), in range */
79
87
  SensorHal_IsChannelReady_ExpectAndReturn(SENSOR_CHANNEL_TEMP, true);
80
88
  SensorHal_ReadChannel_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 1638u);
89
+ SensorHal_RawStatusRegister_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 0x1234u);
81
90
  SensorHal_StartConversion_Expect(SENSOR_CHANNEL_TEMP);
82
91
 
83
92
  TEST_ASSERT_TRUE(TemperatureSensor_Sample());
@@ -93,8 +102,28 @@ void test_CalibrationOffsetShiftsReading(void)
93
102
  /* 2048 counts ~= 42502 milli-C; with +1.0 C offset -> ~= 43502 */
94
103
  SensorHal_IsChannelReady_ExpectAndReturn(SENSOR_CHANNEL_TEMP, true);
95
104
  SensorHal_ReadChannel_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 2048u);
105
+ SensorHal_RawStatusRegister_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 0x1234u);
96
106
  SensorHal_StartConversion_Expect(SENSOR_CHANNEL_TEMP);
97
107
 
98
108
  TemperatureSensor_Sample();
99
109
  TEST_ASSERT_INT32_WITHIN(200, 43502, TemperatureSensor_GetMilliCelsius());
100
110
  }
111
+
112
+ /* Regression test for #1215: a module partial-mocked via MOCK_PARTIAL_ALL_MODULE
113
+ * (as SensorHal is above) must have its calling module's real #include swapped for
114
+ * the generated mock interface header. Without that swap, SensorHal_RawStatusRegister()'s
115
+ * real static inline body -- unreachable through ordinary link-time mock substitution,
116
+ * since a static inline function has no separate object file to exclude -- compiles
117
+ * straight into this test build and always returns its 0xDEAD sentinel regardless of
118
+ * what's expected here, no matter what the mock is told to return. */
119
+ void test_StatusRegisterReflectsMockedValueNotRealSentinel(void)
120
+ {
121
+ SensorHal_IsChannelReady_ExpectAndReturn(SENSOR_CHANNEL_TEMP, true);
122
+ SensorHal_ReadChannel_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 1638u);
123
+ SensorHal_RawStatusRegister_ExpectAndReturn(SENSOR_CHANNEL_TEMP, 0x1234u);
124
+ SensorHal_StartConversion_Expect(SENSOR_CHANNEL_TEMP);
125
+
126
+ TemperatureSensor_Sample();
127
+
128
+ TEST_ASSERT_EQUAL_HEX16(0x1234u, TemperatureSensor_GetStatusRegister());
129
+ }
@@ -66,13 +66,17 @@ class GeneratorTestResultsBacktrace
66
66
  # Attribute each group member its own real result line, if Unity printed one
67
67
  group.each do |test_case|
68
68
  case crash_result[:output]
69
- # Success test case
70
- when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:PASS\s*$)/
69
+ # Success test case -- tolerates a trailing duration suffix (e.g. `PASS (12 ms)`,
70
+ # UNITY_INCLUDE_EXEC_TIME), matching the real format generator_test_results.rb's
71
+ # own parser already recognizes for it.
72
+ when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:PASS(\s\(.*ms\))?\s*$)/
71
73
  group_results[:passed] += 1
72
74
  group_results[:output] << $1
73
75
 
74
- # Ignored test case
75
- when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:IGNORE\s*$)/
76
+ # Ignored test case -- tolerates a trailing message (TEST_IGNORE_MESSAGE), same as
77
+ # FAIL below. Before this fix, a message-carrying ignore fell through to "unresolved"
78
+ # and was misreported as crash evidence -- IGNORE was only recognized bare.
79
+ when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:IGNORE(:.+)?\s*$)/
76
80
  group_results[:ignored] += 1
77
81
  group_results[:output] << $1
78
82
 
@@ -260,13 +264,17 @@ class GeneratorTestResultsBacktrace
260
264
  # Attribute each group member its own real result line, if Unity printed one
261
265
  group.each do |test_case|
262
266
  case crash_result[:output]
263
- # Success test case
264
- when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:PASS\s*$)/
267
+ # Success test case -- tolerates a trailing duration suffix (e.g. `PASS (12 ms)`,
268
+ # UNITY_INCLUDE_EXEC_TIME), matching the real format generator_test_results.rb's
269
+ # own parser already recognizes for it.
270
+ when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:PASS(\s\(.*ms\))?\s*$)/
265
271
  group_results[:passed] += 1
266
272
  group_results[:output] << $1
267
273
 
268
- # Ignored test case
269
- when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:IGNORE\s*$)/
274
+ # Ignored test case -- tolerates a trailing message (TEST_IGNORE_MESSAGE), same as
275
+ # FAIL below. Before this fix, a message-carrying ignore fell through to "unresolved"
276
+ # and was misreported as crash evidence -- IGNORE was only recognized bare.
277
+ when /(^#{Regexp.escape(filename)}:\d+:#{Regexp.escape(test_case[:test])}:IGNORE(:.+)?\s*$)/
270
278
  group_results[:ignored] += 1
271
279
  group_results[:output] << $1
272
280
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  require 'generate_test_runner' # Unity's test runner generator
9
9
  require 'ceedling/parsing_parcels'
10
+ require 'ceedling/includes/includes'
10
11
 
11
12
  class GeneratorTestRunner
12
13
 
@@ -38,7 +39,30 @@ class GeneratorTestRunner
38
39
  @test_cases_internal,
39
40
  # Small hack for mock subdirectory support until include paths fully supported
40
41
  mocks.map{ |include| include.filepath },
41
- includes.map{ |include| include.filename }
42
+ # Unity's own generator emits a string verbatim if it contains '<' and otherwise
43
+ # wraps it in double quotes -- each entry here must therefore already be in its
44
+ # final, exact form.
45
+ #
46
+ # System includes only: use the full, bracketed spelling (include_path, a
47
+ # reconciled SystemInclude's original as-written directive text -- e.g.
48
+ # "sys/stat.h" -- when set, else the directory-preserving filepath). A bare
49
+ # filename here drops both the directory component and the '<>' delimiters a
50
+ # system header's own search path depends on (issue #1236).
51
+ #
52
+ # User includes: bare filename, same as always. Unlike mocks (whose subdirectory
53
+ # is a real, dedicated build/test/mocks/<test>/ path -- hence their own
54
+ # directory-preserving filepath usage above) or a system header (whose directory
55
+ # is meaningful to the system include search path), a project's own directories
56
+ # are exposed to the compiler as a flat list of -I search paths, not nested to
57
+ # match whatever directory component a captured UserInclude's filepath happens to
58
+ # carry -- rendering the full path here breaks resolution instead of fixing it.
59
+ includes.map do |include|
60
+ if include.is_a?(SystemInclude)
61
+ "<#{include.include_path || include.filepath}>"
62
+ else
63
+ include.filename
64
+ end
65
+ end
42
66
  )
43
67
  end
44
68
 
@@ -125,8 +125,15 @@ class Partializer
125
125
 
126
126
  partials.each do |_module, config|
127
127
  # Remap mockable interface headers that will be injected into generated partial implementation
128
+ #
129
+ # Any real (non-nil) mock mode means this module has a generated mock interface header
130
+ # to redirect to -- PUBLIC/PRIVATE alone once covered every mode that existed, but DEDUCT
131
+ # (MOCK_PARTIAL_ALL_MODULE) and ACCUMULATE (MOCK_PARTIAL_MODULE) were added later without
132
+ # updating this check, so a module mocked via either of those was silently never
133
+ # redirected: its real header (and any static inline/static function bodies in it) stayed
134
+ # #include'd verbatim, compiling straight past the mock instead of being replaced by it.
128
135
  if includes.any? { |include| include.filename.ext().downcase() == _module.downcase() }
129
- if [PUBLIC, PRIVATE].include?( config.mocks.type )
136
+ if !config.mocks.type.nil?
130
137
  # Insert mockable interface header from remapping of module name
131
138
  _includes << UserInclude.new(
132
139
  @file_path_utils.form_partial_interface_header_filename(_module)
@@ -95,6 +95,15 @@ class PluginReportinator
95
95
  return aggregated_results
96
96
  end
97
97
 
98
+ # A build's own test results -- pass or fail -- are core information a user
99
+ # silencing routine build chatter still wants to see; ERRORS is the floor
100
+ # for both, not just for a failing run. Centralized here so every plugin
101
+ # printing post-build test results (report_tests_stdout_plugin, gcov,
102
+ # valgrind) agrees on the same answer instead of each hardcoding its own.
103
+ def test_results_floor_verbosity
104
+ Verbosity::ERRORS
105
+ end
106
+
98
107
  def run_test_results_report(hash, verbosity=Verbosity::NORMAL)
99
108
  if @test_results_template.nil?
100
109
  raise CeedlingException.new( "No test results report template has been set." )
@@ -50,7 +50,7 @@ class ReportTestsStdoutPlugin < Plugin
50
50
 
51
51
  results = @plugin_reportinator.assemble_test_results( @result_list )
52
52
  hash = { :context => TEST_SYM, :results => results }
53
- verbosity = (results[:counts][:failed] > 0) ? Verbosity::ERRORS : Verbosity::NORMAL
53
+ verbosity = @plugin_reportinator.test_results_floor_verbosity
54
54
 
55
55
  # Print unit test suite results
56
56
  @plugin_reportinator.run_test_results_report( hash, verbosity )
@@ -567,6 +567,28 @@ class Preprocessinator
567
567
  defines: defines
568
568
  )
569
569
 
570
+ # Supplement with a literal text scan of the original file's own #include
571
+ # lines -- the gcc-based bare pass above runs against an isolated copy that
572
+ # can never open another header, so a conditional #include whose guard
573
+ # depends on a macro defined by an *earlier #include in this same file*
574
+ # evaluates false there and silently drops out. Unioning in this text-based
575
+ # pass's result only ever adds candidates for Includes.reconcile below to
576
+ # match against the accurate directives-only pass -- it can't introduce a
577
+ # spurious entry on its own, since reconcile still requires the accurate
578
+ # pass to also report it.
579
+ #
580
+ # This supplements the gcc-based pass rather than replacing it: gcc can
581
+ # resolve an #include whose target is itself a macro (e.g. the
582
+ # MOCK_PARTIAL_ALL_MODULE()-style directives this project's own generated
583
+ # test files use), since a command-line -D define is visible even to the
584
+ # isolated copy -- a literal text scan has no filename there to find at
585
+ # all, since none exists until a real preprocessor expands the macro. The
586
+ # two passes catch different, non-overlapping failure modes; keeping both
587
+ # covers both.
588
+ bare_includes = (
589
+ bare_includes + @includes_handler.extract_bare_includes_from_text( filepath: filepath )
590
+ ).uniq( &:filename )
591
+
570
592
  # Extract user includes
571
593
  user_includes = preprocess_user_includes(
572
594
  name: test,
@@ -115,6 +115,39 @@ class PreprocessinatorIncludesHandler
115
115
  return includes
116
116
  end
117
117
 
118
+ # Scan the original file's own text for every #include line, unconditionally --
119
+ # no #if/#ifdef tracking at all, just literal presence. Supplements the gcc-based
120
+ # bare pass (`extract_bare_includes`), which runs against an isolated, sibling-free
121
+ # copy of the file and so can never open another header to resolve a conditional
122
+ # that depends on a macro that header defines (e.g. `#if SOME_MACRO_FROM_ANOTHER_HEADER`)
123
+ # -- GCC treats the macro as undefined there and silently drops the #include line
124
+ # from that pass's output even though it's a perfectly ordinary, real, top-level
125
+ # #include once actually evaluated with the real file in place. This method's result
126
+ # is meant to be unioned with the gcc-based bare pass's own result (see
127
+ # Preprocessinator#preprocess_file_includes_common), not used on its own -- an
128
+ # `Includes.reconcile` call downstream still only keeps an entry here if the
129
+ # accurate directives-only pass also reports it, so unconditionally capturing every
130
+ # literal #include line (regardless of whether its own guard is really true) is
131
+ # safe: it can only ever let back in an entry the accurate pass already confirmed,
132
+ # never introduce a spurious one on its own. A supplement, not a replacement --
133
+ # the gcc pass can still do something this literal scan structurally can't: resolve
134
+ # an #include whose own target is a macro rather than a literal filename.
135
+ def extract_bare_includes_from_text(filepath:)
136
+ includes = []
137
+
138
+ # Open in binary mode: code_lines applies clean_encoding per-line, but each_line
139
+ # itself can raise on invalid byte sequences before clean_encoding is reached.
140
+ @file_wrapper.open(filepath, 'rb') do |input|
141
+ @parsing_parcels.code_lines( input ) do |line|
142
+ _include = @include_factory.user_include_from_directive( line ) ||
143
+ @include_factory.system_include_from_directive( line )
144
+ includes << Include.new( _include.filepath ) if !_include.nil?
145
+ end
146
+ end
147
+
148
+ return clean_self_reference( filepath, includes )
149
+ end
150
+
118
151
  def extract_user_includes_preprocess(name:, filepath:, preprocessed_filepath:)
119
152
  includes = []
120
153
 
data/lib/version.rb CHANGED
@@ -15,7 +15,7 @@
15
15
  module Ceedling
16
16
  module Version
17
17
  # Convenience constants for gem building, etc.
18
- GEM = '1.1.5'
18
+ GEM = '1.1.7'
19
19
  TAG = GEM
20
20
 
21
21
  # If run as a script print Ceedling's version to $stdout
@@ -27,7 +27,6 @@
27
27
 
28
28
  :report_generator:
29
29
  :verbosity: Warning # Default verbosity
30
- :collection_paths_source: [] # Explicitly defined as default empty array to simplify option validation code
31
30
  :custom_args: [] # Explicitly defined as default empty array to simplify option validation code
32
31
  :gcov_exclude: [] # Explicitly defined as default empty array to simplify option validation code
33
32
  ...
@@ -40,11 +40,19 @@ class Gcov < Plugin
40
40
  )
41
41
  end
42
42
 
43
- # Validate configuration and tools while building Reportinators
44
- @reportinators = build_reportinators(
45
- @project_config[:gcov_utilities],
46
- @reports_enabled
47
- )
43
+ # #1252 -- Validate `:gcov :utilities` config (cheap, no external tool needed) and
44
+ # build the console summary Reportinator (needs no external tool of its own) here,
45
+ # eagerly, since `setup()` runs for every build this plugin is merely *enabled* for,
46
+ # not only real `gcov:` builds. Deliberately NOT building the gcovr/ReportGenerator
47
+ # Reportinators here -- doing so validates gcovr/ReportGenerator are installed
48
+ # (see their own constructors), which would make either a hard dependency of any
49
+ # build at all (e.g. plain `test:all`) rather than only a real `gcov:` build. See
50
+ # generate_coverage_reports, which builds (and so validates) them lazily, only once
51
+ # post_build already knows a gcov: task actually ran.
52
+ validate_utilities_config( @project_config[:gcov_utilities] )
53
+ @console_reportinator = summaries_enabled?( @project_config ) ?
54
+ ConsoleReportinator.new( @ceedling, @project_config ) : nil
55
+ @reportinators = nil
48
56
 
49
57
  # Convenient instance variable references
50
58
  @configurator = @ceedling[:configurator]
@@ -214,7 +222,7 @@ class Gcov < Plugin
214
222
  results: results
215
223
  }
216
224
 
217
- verbosity = (results[:counts][:failed] > 0) ? Verbosity::ERRORS : Verbosity::NORMAL
225
+ verbosity = @plugin_reportinator.test_results_floor_verbosity
218
226
 
219
227
  # Print unit test suite results
220
228
  @plugin_reportinator.run_test_results_report( hash, verbosity )
@@ -257,6 +265,15 @@ class Gcov < Plugin
257
265
  def generate_coverage_reports()
258
266
  return if not @reports_enabled
259
267
 
268
+ # #1252 -- Lazily build (and so validate the external tools of) the gcovr/
269
+ # ReportGenerator Reportinators only now: this method only runs from post_build
270
+ # (automatic reporting) or the standalone `gcov:report` task (manual reporting,
271
+ # mutually exclusive with automatic per :gcov_report_task) -- either way, only
272
+ # once a real gcov: task is confirmed to have already run. See setup()'s own
273
+ # comment for why this can't happen any earlier. `||=` just guards against ever
274
+ # rebuilding (and so re-validating) on a hypothetical repeat call.
275
+ @reportinators ||= build_reportinators( @project_config[:gcov_utilities] )
276
+
260
277
  @reportinators.each do |reportinator|
261
278
  # Create the artifacts output directory.
262
279
  @file_wrapper.mkdir( reportinator.artifacts_path ) if reportinator.artifacts_path
@@ -312,16 +329,10 @@ class Gcov < Plugin
312
329
  return sources - tested_sources
313
330
  end
314
331
 
315
- def build_reportinators(config, enabled)
316
- reportinators = []
317
-
318
- # Instantiate console summary reportinator if summaries enabled
319
- @console_reportinator = summaries_enabled?(@project_config) ?
320
- ConsoleReportinator.new(@ceedling, @project_config) : nil
321
-
322
- # Do not instantiate file reportinators (and tool validation) unless reports enabled
323
- return reportinators if (!enabled)
324
-
332
+ # Fail fast at plugin setup if :utilities holds an unrecognized name (e.g. a typo) --
333
+ # cheap, config-shape-only validation, so this stays eager even though the actual
334
+ # Reportinators it names are built lazily (see build_reportinators, generate_coverage_reports).
335
+ def validate_utilities_config(config)
325
336
  config.each do |reportinator|
326
337
  if not GCOV_UTILITY_NAMES.map(&:upcase).include?( reportinator.upcase )
327
338
  options = GCOV_UTILITY_NAMES.map{ |utility| "'#{utility}'" }.join(', ')
@@ -329,6 +340,14 @@ class Gcov < Plugin
329
340
  raise CeedlingException.new(msg)
330
341
  end
331
342
  end
343
+ end
344
+
345
+ # #1252 -- Deliberately not called from setup(); see its own comment and
346
+ # generate_coverage_reports, the only caller. Constructing GcovrReportinator/
347
+ # ReportGeneratorReportinator validates gcovr/ReportGenerator are installed, so this
348
+ # can only run once a real gcov: task is confirmed to have run.
349
+ def build_reportinators(config)
350
+ reportinators = []
332
351
 
333
352
  # Run reports using gcovr
334
353
  if utility_enabled?( config, GCOV_UTILITY_NAME_GCOVR )