ceedling 1.1.2 → 1.1.4
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.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +1 -1
- data/assets/fake_crash_test_fixture.rb +14 -0
- data/assets/partial_all_module.h +11 -0
- data/assets/run_with_ubsan.rb +15 -0
- data/assets/same_dir_pairing_module.c +4 -0
- data/assets/same_dir_pairing_module.h +8 -0
- data/assets/same_dir_pairing_module_func.h +6 -0
- data/assets/same_dir_pairing_other.c +4 -0
- data/assets/same_dir_pairing_other.h +8 -0
- data/assets/test_example_file_crash_sigsegv_with_param.c +32 -0
- data/assets/test_example_file_ub_shift_overflow.c +22 -0
- data/assets/test_partial_all_module.c +16 -0
- data/assets/test_same_dir_pairing.c +23 -0
- data/bin/actions_wrapper.rb +7 -6
- data/docs/Changelog.md +29 -1
- data/docs/mkdocs/configuration/reference/index.md +8 -0
- data/docs/mkdocs/configuration/reference/partials.md +38 -0
- data/examples/wondrous_forest/src/LightSensor.c +21 -4
- data/examples/wondrous_forest/src/LightSensor.h +12 -4
- data/examples/wondrous_forest/test/TestLightSensor.c +34 -4
- data/lib/ceedling/c_extractor/c_extractor.rb +37 -11
- data/lib/ceedling/c_extractor/c_extractor_constants.rb +0 -3
- data/lib/ceedling/c_extractor/c_extractor_declarations.rb +26 -25
- data/lib/ceedling/c_extractor/c_extractor_functions.rb +5 -12
- data/lib/ceedling/config/configurator.rb +8 -1
- data/lib/ceedling/config/configurator_setup.rb +26 -0
- data/lib/ceedling/defaults.rb +16 -2
- data/lib/ceedling/file_path_utils.rb +7 -0
- data/lib/ceedling/file_wrapper.rb +8 -0
- data/lib/ceedling/generators/generator.rb +15 -0
- data/lib/ceedling/generators/generator_helper.rb +25 -6
- data/lib/ceedling/generators/generator_partials.rb +84 -3
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +268 -100
- data/lib/ceedling/generators/generator_test_runner.rb +20 -2
- data/lib/ceedling/includes/includes.rb +66 -11
- data/lib/ceedling/objects.yml +5 -0
- data/lib/ceedling/partials/partializer.rb +18 -3
- data/lib/ceedling/partials/partializer_utils.rb +6 -1
- data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +43 -16
- data/lib/ceedling/test_invoker/test_build_executor.rb +19 -8
- data/lib/version.rb +1 -1
- data/mkdocs.yml +1 -0
- data/plugins/gcov/lib/gcov.rb +2 -2
- data/site-local/404.html +30 -0
- data/site-local/configuration/environment-vars.html +30 -0
- data/site-local/configuration/global-collections.html +30 -0
- data/site-local/configuration/index.html +30 -0
- data/site-local/configuration/loading.html +30 -0
- data/site-local/configuration/mixins.html +30 -0
- data/site-local/configuration/parallel-builds.html +30 -0
- data/site-local/configuration/project-file.html +30 -0
- data/site-local/configuration/reference/cexception.html +30 -0
- data/site-local/configuration/reference/cmock.html +30 -0
- data/site-local/configuration/reference/defines.html +30 -0
- data/site-local/configuration/reference/environment.html +30 -0
- data/site-local/configuration/reference/extension.html +30 -0
- data/site-local/configuration/reference/files.html +30 -0
- data/site-local/configuration/reference/flags.html +30 -0
- data/site-local/configuration/reference/index.html +36 -0
- data/site-local/configuration/reference/libraries.html +30 -0
- data/site-local/configuration/reference/mixins.html +30 -0
- data/site-local/configuration/reference/partials.html +4257 -0
- data/site-local/configuration/reference/paths.html +30 -0
- data/site-local/configuration/reference/plugins.html +30 -0
- data/site-local/configuration/reference/project.html +30 -0
- data/site-local/configuration/reference/release-build.html +31 -1
- data/site-local/configuration/reference/test-build.html +31 -1
- data/site-local/configuration/reference/test-runner.html +30 -0
- data/site-local/configuration/reference/tools.html +30 -0
- data/site-local/configuration/reference/unity.html +30 -0
- data/site-local/configuration/which-ceedling.html +30 -0
- data/site-local/development/index.html +30 -0
- data/site-local/development/plugins/configuration.html +30 -0
- data/site-local/development/plugins/index.html +30 -0
- data/site-local/development/plugins/plugin-subclass.html +30 -0
- data/site-local/development/plugins/rake-tasks.html +30 -0
- data/site-local/development/workflow.html +30 -0
- data/site-local/getting-started/command-line.html +30 -0
- data/site-local/getting-started/example-projects/cipher-quest.html +30 -0
- data/site-local/getting-started/example-projects/index.html +30 -0
- data/site-local/getting-started/example-projects/temp-sensor.html +30 -0
- data/site-local/getting-started/example-projects/wondrous-forest.html +30 -0
- data/site-local/getting-started/index.html +30 -0
- data/site-local/getting-started/installation.html +30 -0
- data/site-local/getting-started/quick-start.html +30 -0
- data/site-local/help.html +30 -0
- data/site-local/index.html +30 -0
- data/site-local/overview/build-system.html +30 -0
- data/site-local/overview/index.html +30 -0
- data/site-local/overview/test-environments.html +30 -0
- data/site-local/overview/tools-and-frameworks.html +30 -0
- data/site-local/plugins/beep.html +30 -0
- data/site-local/plugins/bullseye.html +30 -0
- data/site-local/plugins/command-hooks.html +30 -0
- data/site-local/plugins/compile-commands-json-db.html +30 -0
- data/site-local/plugins/cppcheck.html +30 -0
- data/site-local/plugins/dependencies.html +30 -0
- data/site-local/plugins/fff.html +30 -0
- data/site-local/plugins/gcov/examples.html +30 -0
- data/site-local/plugins/gcov/gcovr.html +30 -0
- data/site-local/plugins/gcov/index.html +30 -0
- data/site-local/plugins/gcov/overview.html +30 -0
- data/site-local/plugins/gcov/reportgenerator.html +30 -0
- data/site-local/plugins/gcov/reporting.html +30 -0
- data/site-local/plugins/gcov/setup.html +30 -0
- data/site-local/plugins/gcov/tool-versions.html +30 -0
- data/site-local/plugins/gcov/troubleshooting.html +30 -0
- data/site-local/plugins/index.html +30 -0
- data/site-local/plugins/module-generator.html +30 -0
- data/site-local/plugins/report-build-warnings-log.html +30 -0
- data/site-local/plugins/report-tests-gtestlike-stdout.html +30 -0
- data/site-local/plugins/report-tests-ide-stdout.html +30 -0
- data/site-local/plugins/report-tests-log-factory.html +30 -0
- data/site-local/plugins/report-tests-pretty-stdout.html +30 -0
- data/site-local/plugins/report-tests-raw-output-log.html +30 -0
- data/site-local/plugins/report-tests-teamcity-stdout.html +30 -0
- data/site-local/plugins/valgrind.html +30 -0
- data/site-local/project/release-history.html +30 -0
- data/site-local/project/upgrade.html +30 -0
- data/site-local/reference/build-directives.html +30 -0
- data/site-local/reference/command-line.html +30 -0
- data/site-local/reference/environment-vars.html +30 -0
- data/site-local/reference/gcov-plugin.html +30 -0
- data/site-local/reference/global-collections.html +30 -0
- data/site-local/reference/index.html +30 -0
- data/site-local/reference/partials-macros.html +30 -0
- data/site-local/reference/project-configuration.html +30 -0
- data/site-local/sitemap.xml.gz +0 -0
- data/site-local/snapshot/examples/cipher_quest/index.html +30 -0
- data/site-local/snapshot/examples/temp_sensor/index.html +30 -0
- data/site-local/snapshot/examples/wondrous_forest/index.html +30 -0
- data/site-local/testing-guide/build-directives.html +30 -0
- data/site-local/testing-guide/conventions.html +30 -0
- data/site-local/testing-guide/frameworks.html +30 -0
- data/site-local/testing-guide/index.html +30 -0
- data/site-local/testing-guide/partials/configuration.html +30 -0
- data/site-local/testing-guide/partials/conventions.html +30 -0
- data/site-local/testing-guide/partials/directives.html +30 -0
- data/site-local/testing-guide/partials/example.html +30 -0
- data/site-local/testing-guide/partials/index.html +30 -0
- data/site-local/testing-guide/partials/overview.html +30 -0
- data/site-local/testing-guide/partials/variables.html +30 -0
- data/site-local/testing-guide/test-cases.html +30 -0
- data/site-local/testing-guide/test-sample.html +30 -0
- data/site-local/testing-guide/test-suite-anatomy.html +30 -0
- data/spec/support/system/spec_system_helper.rb +25 -0
- data/spec/support/system/system_context.rb +15 -0
- data/spec/system/deployment_as_gem_spec.rb +14 -0
- data/spec/system/deployment_as_vendor_spec.rb +19 -0
- data/spec/system/encoding_stress_spec.rb +4 -4
- data/spec/system/example_wondrous_forest_spec.rb +2 -2
- data/spec/system/support/common_test_cases.rb +227 -0
- data/spec/units/c_extractor/c_extractor_declarations_spec.rb +27 -4
- data/spec/units/c_extractor/c_extractor_functions_spec.rb +1 -11
- data/spec/units/c_extractor/c_extractor_integration_spec.rb +18 -11
- data/spec/units/c_extractor/c_extractor_spec.rb +57 -1
- data/spec/units/config/configurator_setup_spec.rb +61 -0
- data/spec/units/configurator_spec.rb +45 -0
- data/spec/units/file_path_utils_spec.rb +17 -0
- data/spec/units/generators/generator_helper_spec.rb +34 -7
- data/spec/units/generators/generator_partials_spec.rb +120 -12
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +248 -41
- data/spec/units/generators/generator_test_runner_spec.rb +26 -2
- data/spec/units/includes/include_spec.rb +30 -18
- data/spec/units/includes/includes_spec.rb +100 -10
- data/spec/units/partials/partializer_spec.rb +113 -1
- data/spec/units/partials/partializer_utils_spec.rb +11 -0
- data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +96 -7
- data/spec/units/system_wrapper_spec.rb +72 -0
- data/spec/units/test_context_extractor_spec.rb +3 -3
- metadata +20 -2
|
@@ -16,12 +16,9 @@ class CExtractorDeclarations
|
|
|
16
16
|
|
|
17
17
|
constructor :c_extractor_code_text
|
|
18
18
|
|
|
19
|
-
attr_writer :max_line_length
|
|
20
|
-
|
|
21
19
|
def setup()
|
|
22
20
|
# Aliases
|
|
23
|
-
@code_text
|
|
24
|
-
@max_line_length = DEFAULT_MAX_LINE_LENGTH
|
|
21
|
+
@code_text = @c_extractor_code_text
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
# Attempts to extract a complete variable declaration from the scanner
|
|
@@ -58,11 +55,14 @@ class CExtractorDeclarations
|
|
|
58
55
|
# On failure: Resets scanner position to starting position
|
|
59
56
|
#
|
|
60
57
|
# Safety:
|
|
61
|
-
#
|
|
58
|
+
# A declaration with no terminating semicolon runs the scan to the end of
|
|
59
|
+
# whatever buffer the caller (CExtractor#extract_next_feature) has grown so
|
|
60
|
+
# far; that caller is the one place enforcing an overall length ceiling,
|
|
61
|
+
# since only it knows how much more of the file remains to try growing into.
|
|
62
62
|
def try_extract_variable(scanner)
|
|
63
63
|
start_pos = scanner.pos
|
|
64
64
|
|
|
65
|
-
# Tracks paren, bracket, and brace depth simultaneously with inline string state machine
|
|
65
|
+
# Tracks paren, bracket, and brace depth simultaneously with an inline string state machine — not suitable for collect_balanced()
|
|
66
66
|
paren_depth = 0
|
|
67
67
|
bracket_depth = 0
|
|
68
68
|
brace_depth = 0
|
|
@@ -74,12 +74,6 @@ class CExtractorDeclarations
|
|
|
74
74
|
until scanner.eos?
|
|
75
75
|
char = scanner.peek(1)
|
|
76
76
|
|
|
77
|
-
# Safety check -- prevent infinite loops on malformed input
|
|
78
|
-
if (scanner.pos - start_pos) > @max_line_length
|
|
79
|
-
scanner.pos = start_pos
|
|
80
|
-
return [false, nil]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
77
|
# Handle string literals
|
|
84
78
|
if in_string
|
|
85
79
|
if char == '\\'
|
|
@@ -302,8 +296,8 @@ class CExtractorDeclarations
|
|
|
302
296
|
#
|
|
303
297
|
# Returns: CVariableDeclaration
|
|
304
298
|
def parse_declaration(individual_text, original_text)
|
|
305
|
-
decorators
|
|
306
|
-
clean_text =
|
|
299
|
+
decorators, remaining = extract_decorators(individual_text)
|
|
300
|
+
clean_text = normalize_declaration_whitespace(remaining)
|
|
307
301
|
# Strip compiler extensions for name/type/array_suffix extraction only;
|
|
308
302
|
# clean_text (stored as .text) retains attributes for correct compilation.
|
|
309
303
|
extraction_text = @code_text.strip_compiler_extensions(clean_text)
|
|
@@ -321,9 +315,17 @@ class CExtractorDeclarations
|
|
|
321
315
|
)
|
|
322
316
|
end
|
|
323
317
|
|
|
324
|
-
#
|
|
318
|
+
# Storage-class specifiers and base-type qualifiers precede a C declaration's type as a
|
|
319
|
+
# contiguous run of keywords -- `static`, `extern`, and a `const`/`volatile` that qualifies
|
|
320
|
+
# the type itself all appear here (e.g. `static const int MAX;`). A qualifier bound to a
|
|
321
|
+
# pointer level instead, such as the second `const` in `char * const p;`, sits after the
|
|
322
|
+
# type and its `*` tokens rather than in this leading run, so it is grammatically part of
|
|
323
|
+
# the declarator, not a decorator. Peeling keywords off one at a time from the front, and
|
|
324
|
+
# stopping at the first non-keyword token, naturally separates the two: the same keyword
|
|
325
|
+
# (e.g. `const`) can appear once here and again later in the declarator without the two
|
|
326
|
+
# occurrences being confused with each other, since only the leading run is ever consumed.
|
|
325
327
|
#
|
|
326
|
-
# Returns: Array<String> ordered decorator keywords found at start of text
|
|
328
|
+
# Returns: [Array<String> ordered decorator keywords found at start of text, String remaining text after that run]
|
|
327
329
|
def extract_decorators(text)
|
|
328
330
|
decorators = []
|
|
329
331
|
remaining = text.strip
|
|
@@ -339,18 +341,17 @@ class CExtractorDeclarations
|
|
|
339
341
|
end
|
|
340
342
|
break unless matched
|
|
341
343
|
end
|
|
342
|
-
decorators
|
|
344
|
+
[decorators, remaining]
|
|
343
345
|
end
|
|
344
346
|
|
|
345
|
-
#
|
|
347
|
+
# A declaration with its leading decorator run already removed (see extract_decorators)
|
|
348
|
+
# still carries whatever whitespace and line breaks appeared in the source between its
|
|
349
|
+
# remaining tokens. This collapses that whitespace to single spaces and guarantees a
|
|
350
|
+
# trailing semicolon, so `.text` is stable, single-line C ready to compile.
|
|
346
351
|
#
|
|
347
|
-
# Returns: String with
|
|
348
|
-
def
|
|
349
|
-
result =
|
|
350
|
-
decorators.each do |kw|
|
|
351
|
-
result.gsub!(/\b#{Regexp.escape(kw)}\b\s*/, '')
|
|
352
|
-
end
|
|
353
|
-
# Normalize whitespace but preserve semicolon
|
|
352
|
+
# Returns: String with whitespace normalized, semicolon retained
|
|
353
|
+
def normalize_declaration_whitespace(remaining)
|
|
354
|
+
result = remaining.dup
|
|
354
355
|
result.gsub!(/\r\n|\r|\n|\t/, ' ')
|
|
355
356
|
result.gsub!(/\s+/, ' ')
|
|
356
357
|
result.strip!
|
|
@@ -15,13 +15,9 @@ class CExtractorFunctions
|
|
|
15
15
|
|
|
16
16
|
constructor :c_extractor_code_text
|
|
17
17
|
|
|
18
|
-
attr_writer :max_line_length
|
|
19
|
-
|
|
20
18
|
def setup()
|
|
21
19
|
# Aliases
|
|
22
20
|
@code_text = @c_extractor_code_text
|
|
23
|
-
|
|
24
|
-
@max_line_length = DEFAULT_MAX_LINE_LENGTH
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
def try_extract_function_declaration(scanner)
|
|
@@ -124,7 +120,10 @@ class CExtractorFunctions
|
|
|
124
120
|
# - Before '{' for definitions)
|
|
125
121
|
#
|
|
126
122
|
# Safety:
|
|
127
|
-
#
|
|
123
|
+
# A signature with no terminating ')'/'{' runs the scan to the end of
|
|
124
|
+
# whatever buffer the caller (CExtractor#extract_next_feature) has grown so
|
|
125
|
+
# far; that caller is the one place enforcing an overall length ceiling,
|
|
126
|
+
# since only it knows how much more of the file remains to try growing into.
|
|
128
127
|
def extract_function_signature(scanner, type)
|
|
129
128
|
start_pos = scanner.pos
|
|
130
129
|
# Tracks paren depth while accumulating candidate end-positions — not suitable for collect_balanced()
|
|
@@ -133,16 +132,10 @@ class CExtractorFunctions
|
|
|
133
132
|
string_char = nil
|
|
134
133
|
signature_candidates = [] # Track positions where paren_depth returns to 0
|
|
135
134
|
found_opening_paren = false # Track if we've seen an opening paren
|
|
136
|
-
|
|
135
|
+
|
|
137
136
|
until scanner.eos?
|
|
138
137
|
char = scanner.peek(1)
|
|
139
138
|
|
|
140
|
-
# Safety check
|
|
141
|
-
if (scanner.pos - start_pos) > @max_line_length
|
|
142
|
-
scanner.pos = start_pos
|
|
143
|
-
return nil
|
|
144
|
-
end
|
|
145
|
-
|
|
146
139
|
# Handle string literals
|
|
147
140
|
if in_string
|
|
148
141
|
if char == '\\'
|
|
@@ -88,10 +88,16 @@ class Configurator
|
|
|
88
88
|
@loginator.log( "Reverted :cmock ↳ :treat_inlines to :exclude because this CMock feature is superseded by Partials.", Verbosity::COMPLAIN, LogLabels::NOTICE )
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
# If partials enabled, inject partials name prefix symbol to all test compilation
|
|
91
|
+
# If partials enabled, inject partials name prefix symbol to all test compilation
|
|
92
|
+
# and, if a project defines its own preprocess-scoped defines matcher, to
|
|
93
|
+
# preprocessing as well -- `:preprocess:` defines are looked up independently
|
|
94
|
+
# of `:test:` defines and only fall back to the latter when `:preprocess:`
|
|
95
|
+
# is absent entirely, so a project-defined `:preprocess:` matcher needs this
|
|
96
|
+
# symbol appended directly or Partials' own macro-based #includes break.
|
|
92
97
|
# Handle both the simple list and matcher hash config formats.
|
|
93
98
|
_partials_prefix_symbol = "CEEDLING_PARTIALS_PREFIX=#{PARTIAL_FILENAME_PREFIX}"
|
|
94
99
|
ConfigMatchinator.append_matcher_entries( config[:defines][:test], _partials_prefix_symbol )
|
|
100
|
+
ConfigMatchinator.append_matcher_entries( config[:defines][:preprocess], _partials_prefix_symbol )
|
|
95
101
|
end
|
|
96
102
|
|
|
97
103
|
|
|
@@ -750,6 +756,7 @@ class Configurator
|
|
|
750
756
|
blotter &= @configurator_setup.validate_test_preprocessor( config )
|
|
751
757
|
blotter &= @configurator_setup.validate_backtrace( config )
|
|
752
758
|
blotter &= @configurator_setup.validate_threads( config )
|
|
759
|
+
blotter &= @configurator_setup.validate_partials( config )
|
|
753
760
|
blotter &= @configurator_setup.validate_plugins( config )
|
|
754
761
|
|
|
755
762
|
# Informational notices
|
|
@@ -744,6 +744,32 @@ class ConfiguratorSetup
|
|
|
744
744
|
return valid
|
|
745
745
|
end
|
|
746
746
|
|
|
747
|
+
# `:max_extraction_length` is a multiplier of 1000 characters, not a raw character count
|
|
748
|
+
# (project-file ergonomics -- `5000` reads more clearly than `5000000`). The minimum here
|
|
749
|
+
# keeps that ceiling comfortably above any ordinary single C declaration or function
|
|
750
|
+
# signature, catching a value so small it would defeat the setting's own purpose (e.g.
|
|
751
|
+
# someone setting `1000` expecting "1000 characters" rather than "1000x characters").
|
|
752
|
+
def validate_partials(config)
|
|
753
|
+
valid = true
|
|
754
|
+
|
|
755
|
+
max_extraction_length = config[:partials][:max_extraction_length]
|
|
756
|
+
|
|
757
|
+
walk = @reportinator.generate_config_walk( [:partials, :max_extraction_length] )
|
|
758
|
+
|
|
759
|
+
case max_extraction_length
|
|
760
|
+
when Integer
|
|
761
|
+
if max_extraction_length < 10
|
|
762
|
+
@loginator.log( "#{walk} must be at least 10 (10,000 characters)", Verbosity::ERRORS )
|
|
763
|
+
valid = false
|
|
764
|
+
end
|
|
765
|
+
else
|
|
766
|
+
@loginator.log( "#{walk} is not an integer", Verbosity::ERRORS )
|
|
767
|
+
valid = false
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
return valid
|
|
771
|
+
end
|
|
772
|
+
|
|
747
773
|
def validate_plugins(config)
|
|
748
774
|
missing_plugins =
|
|
749
775
|
Set.new( config[:plugins][:enabled] ) -
|
data/lib/ceedling/defaults.rb
CHANGED
|
@@ -69,7 +69,10 @@ DEFAULT_TEST_FIXTURE_SIMPLE_BACKTRACE_TOOL = {
|
|
|
69
69
|
:name => 'default_test_fixture_simple_backtrace'.freeze,
|
|
70
70
|
:optional => false.freeze,
|
|
71
71
|
:arguments => [
|
|
72
|
-
|
|
72
|
+
# Test case matching flag and (quoted) value, fully composed by the caller as
|
|
73
|
+
# either `-n "<exact name>"` or `-f "<base name>"` -- see
|
|
74
|
+
# GeneratorTestResultsBacktrace#unity_filter_arg.
|
|
75
|
+
'${2}'.freeze
|
|
73
76
|
].freeze
|
|
74
77
|
}
|
|
75
78
|
|
|
@@ -202,7 +205,10 @@ DEFAULT_TEST_BACKTRACE_GDB_TOOL = {
|
|
|
202
205
|
"--command \"${1}\"".freeze, # Debug script file to run
|
|
203
206
|
'--args'.freeze,
|
|
204
207
|
'${2}'.freeze, # Test executable
|
|
205
|
-
|
|
208
|
+
# Test case matching flag and (quoted) value, fully composed by the caller as
|
|
209
|
+
# either `-n "<exact name>"` or `-f "<base name>"` -- see
|
|
210
|
+
# GeneratorTestResultsBacktrace#unity_filter_arg.
|
|
211
|
+
'${3}'.freeze
|
|
206
212
|
].freeze
|
|
207
213
|
}
|
|
208
214
|
|
|
@@ -289,6 +295,14 @@ DEFAULT_CEEDLING_PROJECT_CONFIG = {
|
|
|
289
295
|
:preprocess_force_fallback => false,
|
|
290
296
|
},
|
|
291
297
|
|
|
298
|
+
:partials => {
|
|
299
|
+
# A single C construct (declaration, definition, directive, etc.) is extracted into a
|
|
300
|
+
# growing buffer; this bounds how large that buffer may grow before extraction fails
|
|
301
|
+
# outright rather than silently dropping an oversized construct and everything after
|
|
302
|
+
# it in the file. Value is a multiplier of 1000 characters -- 5000 means 5,000,000.
|
|
303
|
+
:max_extraction_length => 5000,
|
|
304
|
+
},
|
|
305
|
+
|
|
292
306
|
# Unlike other top-level entries, :environment is an array (of hashes) to preserve order
|
|
293
307
|
:environment => [],
|
|
294
308
|
|
|
@@ -303,6 +303,13 @@ class FilePathUtils
|
|
|
303
303
|
return PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
|
|
304
304
|
end
|
|
305
305
|
|
|
306
|
+
# A module's typedefs and aggregate (struct/enum/union) definitions are generated into this
|
|
307
|
+
# standalone header, shared by both the implementation and interface headers below, so a
|
|
308
|
+
# module tested and mocked in the same file never has its types defined more than once.
|
|
309
|
+
def form_partial_types_header_filename(_module)
|
|
310
|
+
return PARTIAL_FILENAME_PREFIX + _module + '_types' + EXTENSION_CORE_HEADER
|
|
311
|
+
end
|
|
312
|
+
|
|
306
313
|
def form_mock_partial_interface_header_filename(_module)
|
|
307
314
|
return @configurator.cmock_mock_prefix + PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
|
|
308
315
|
end
|
|
@@ -9,6 +9,7 @@ require 'rubygems'
|
|
|
9
9
|
require 'rake' # for FileList
|
|
10
10
|
require 'fileutils'
|
|
11
11
|
require 'pathname'
|
|
12
|
+
require 'tmpdir'
|
|
12
13
|
require 'ceedling/constants'
|
|
13
14
|
|
|
14
15
|
|
|
@@ -132,4 +133,11 @@ class FileWrapper
|
|
|
132
133
|
return FileUtils.mkdir_p(folder)
|
|
133
134
|
end
|
|
134
135
|
|
|
136
|
+
# Creates a uniquely-named, empty directory nested inside `parent` and returns its path.
|
|
137
|
+
# `parent` must already exist. Collision-free even across concurrent callers targeting
|
|
138
|
+
# the same `parent` -- Dir.mktmpdir retries internally on name clash.
|
|
139
|
+
def mkdir_tmp(prefix, parent)
|
|
140
|
+
return Dir.mktmpdir(prefix, parent)
|
|
141
|
+
end
|
|
142
|
+
|
|
135
143
|
end
|
|
@@ -35,6 +35,21 @@ class Generator
|
|
|
35
35
|
@backtrace = @generator_test_results_backtrace
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# A module's typedefs and aggregate definitions are identical regardless of whether a test
|
|
39
|
+
# file tests it, mocks it, or both, so they're generated once into their own header here
|
|
40
|
+
# rather than duplicated by generate_partial_implementation and generate_partial_interface.
|
|
41
|
+
# Returns the bare generated filename (for the caller to add to each header's own includes
|
|
42
|
+
# list) or nil when the module has no typedefs or aggregate definitions to share.
|
|
43
|
+
def generate_partial_types(name:, c_module:, output_path:)
|
|
44
|
+
arg_hash = {
|
|
45
|
+
:name => name,
|
|
46
|
+
:c_module => c_module,
|
|
47
|
+
:output_path => output_path
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return @generator_partials.generate_types( **arg_hash )
|
|
51
|
+
end
|
|
52
|
+
|
|
38
53
|
def generate_partial_interface(test:, partial:, function_declarations:, includes:, c_module:, input_filepath:, output_path:)
|
|
39
54
|
msg = @reportinator.generate_module_progress(
|
|
40
55
|
operation: "Generating Partial mockable interface for",
|
|
@@ -18,21 +18,40 @@ class GeneratorHelper
|
|
|
18
18
|
|
|
19
19
|
crash = false
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
# Any signal death is unconditionally a crash, whatever text made it out beforehand.
|
|
22
|
+
# Broadened from a SIGSEGV(11)-only check: a sanitizer's halt_on_error (UBSan, ASan)
|
|
23
|
+
# raises SIGABRT, not SIGSEGV, and there's no reason to trust a process that a
|
|
24
|
+
# signal actually killed regardless of which one. Applies to Unix-like systems
|
|
25
|
+
# including MSYS on Windows -- native (non-MSYS) Windows Ruby never reports
|
|
26
|
+
# `signaled?` true at all, so this check is inert there rather than wrong; the
|
|
27
|
+
# stats-footer and stderr checks below already carry Windows crash detection.
|
|
28
|
+
if shell_result[:status] && shell_result[:status].signaled?
|
|
29
|
+
@loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
|
|
30
|
+
"#{runner} process terminated by signal #{shell_result[:status].termsig}"
|
|
26
31
|
end
|
|
27
32
|
crash = true
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
# No test results found in test executable output
|
|
31
|
-
|
|
36
|
+
stats_match = shell_result[:output].match( PATTERNS::TEST_STDOUT_STATISTICS )
|
|
37
|
+
if stats_match.nil?
|
|
32
38
|
# No debug logging here because we log this condition in the error log handling below
|
|
33
39
|
crash = true
|
|
34
40
|
end
|
|
35
41
|
|
|
42
|
+
# A clean summary (0 failures) is not trustworthy if the real process still exited
|
|
43
|
+
# nonzero afterward -- e.g. LeakSanitizer detecting a leak during teardown, after
|
|
44
|
+
# every test case already printed and Unity's own summary already looked clean.
|
|
45
|
+
# A nonzero exit consistent with Unity's own reported failures (its exit code
|
|
46
|
+
# convention is one per failed test case) is an ordinary failing run, not a crash.
|
|
47
|
+
if stats_match && shell_result[:status] && !shell_result[:status].success? &&
|
|
48
|
+
!shell_result[:status].signaled? && stats_match[2].to_i == 0
|
|
49
|
+
@loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
|
|
50
|
+
"#{runner} reported a clean summary but exited with real status #{shell_result[:status].exitstatus}"
|
|
51
|
+
end
|
|
52
|
+
crash = true
|
|
53
|
+
end
|
|
54
|
+
|
|
36
55
|
# Scan STDERR line by line for a segfault variant that is not attributed to the test file.
|
|
37
56
|
# A line starting with test_filename is a Unity-reported test-case result, not an OS crash.
|
|
38
57
|
# Checking each line individually avoids false negatives when attributed and bare segfault
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
require 'ceedling/file_wrapper'
|
|
9
9
|
require 'ceedling/partials/partials'
|
|
10
10
|
require 'ceedling/c_extractor/c_extractor_types'
|
|
11
|
+
require 'ceedling/c_extractor/c_extractor_constants'
|
|
11
12
|
|
|
12
13
|
class GeneratorPartials
|
|
13
14
|
|
|
@@ -55,8 +56,75 @@ class GeneratorPartials
|
|
|
55
56
|
return filepath
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
# A module's typedefs and aggregate (struct/enum/union) definitions are the same
|
|
60
|
+
# regardless of which functions a given test file chooses to test versus mock, so they're
|
|
61
|
+
# generated once here into their own header rather than by generate_header itself. Both
|
|
62
|
+
# the implementation and interface headers then simply #include this file, which means a
|
|
63
|
+
# module tested and mocked in the same test file -- each side producing its own generated
|
|
64
|
+
# header -- never ends up with two separate C definitions of the same type in one
|
|
65
|
+
# translation unit. Nothing is written and nil is returned when a module has no such
|
|
66
|
+
# content, since there's then nothing to share.
|
|
67
|
+
#
|
|
68
|
+
# @param name [String] Partial module name (used to form the filename and include guard)
|
|
69
|
+
# @param c_module [CExtractorTypes::CModule] Merged module with type_definitions/aggregate_definitions
|
|
70
|
+
# @param output_path [String] Directory shared with the implementation and interface headers
|
|
71
|
+
# @return [String, nil] The bare filename (for use as a sibling #include), or nil if nothing was generated
|
|
72
|
+
def generate_types(name:, c_module:, output_path:)
|
|
73
|
+
return nil if c_module.type_definitions.empty? && c_module.aggregate_definitions.empty?
|
|
74
|
+
|
|
75
|
+
header = @file_path_utils.form_partial_types_header_filename(name)
|
|
76
|
+
filepath = File.join(output_path, header)
|
|
77
|
+
|
|
78
|
+
# Binary mode: see generate_implementation above.
|
|
79
|
+
@file_wrapper.open(filepath, 'wb') do |file|
|
|
80
|
+
guard = FileWrapper.generate_include_guard(header)
|
|
81
|
+
file << "#ifndef #{guard}\n"
|
|
82
|
+
file << "#define #{guard}\n\n"
|
|
83
|
+
|
|
84
|
+
anything_emitted = false
|
|
85
|
+
c_module.element_sequence.each do |item|
|
|
86
|
+
next unless item.is_a?(CExtractorTypes::CStatement) && type_defining?(item, c_module)
|
|
87
|
+
file << item.text << "\n"
|
|
88
|
+
anything_emitted = true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
file << "\n" if anything_emitted
|
|
92
|
+
file << "#endif // #{guard}\n\n"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
return header
|
|
96
|
+
end
|
|
97
|
+
|
|
58
98
|
private
|
|
59
99
|
|
|
100
|
+
# A typedef or a non-typedef struct/enum/union tag definition establishes a type; C treats
|
|
101
|
+
# a second definition of the same type in one translation unit as a redefinition error even
|
|
102
|
+
# when the two definitions are textually identical. A macro statement carries no such
|
|
103
|
+
# restriction -- an identical #define may legally repeat -- so only membership in
|
|
104
|
+
# type_definitions or aggregate_definitions marks a CStatement as type-defining content
|
|
105
|
+
# that belongs in the shared types header instead of being emitted inline.
|
|
106
|
+
def type_defining?(item, c_module)
|
|
107
|
+
c_module.type_definitions.include?(item) || c_module.aggregate_definitions.include?(item)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# A `CVariableDeclaration`'s `decorators` mixes storage-class specifiers (`static`,
|
|
111
|
+
# `extern`) with base-type qualifiers (`const`, `volatile`, `restrict`) in their
|
|
112
|
+
# original source order, since both kinds of keyword lead a declaration the same
|
|
113
|
+
# way. An `extern` declaration and the variable's own definition both need the
|
|
114
|
+
# qualifiers reasserted -- omitting them produces a type that disagrees with any
|
|
115
|
+
# other declaration of the same variable elsewhere in the same translation unit --
|
|
116
|
+
# but neither may carry a storage-class specifier: `static` would contradict the
|
|
117
|
+
# external linkage the Partial is establishing, and `extern` is supplied literally
|
|
118
|
+
# by the caller. Filtering to TYPE_QUALIFIER_KEYWORDS keeps only what belongs on
|
|
119
|
+
# both forms.
|
|
120
|
+
#
|
|
121
|
+
# @param decl [CExtractorTypes::CVariableDeclaration]
|
|
122
|
+
# @return [String] e.g. "volatile " / "const volatile " / "" (trailing space when non-empty)
|
|
123
|
+
def qualifier_prefix(decl)
|
|
124
|
+
quals = decl.decorators.select { |kw| CExtractorConstants::TYPE_QUALIFIER_KEYWORDS.include?(kw) }
|
|
125
|
+
quals.empty? ? '' : "#{quals.join(' ')} "
|
|
126
|
+
end
|
|
127
|
+
|
|
60
128
|
# Emit a partial header file.
|
|
61
129
|
#
|
|
62
130
|
# Iterates c_module.element_sequence to emit non-function items (macros, typedefs,
|
|
@@ -101,13 +169,21 @@ class GeneratorPartials
|
|
|
101
169
|
c_module.element_sequence.each do |item|
|
|
102
170
|
case item
|
|
103
171
|
when CExtractorTypes::CStatement
|
|
172
|
+
# Typedefs and aggregate definitions live in the shared types header generated by
|
|
173
|
+
# generate_types instead of here, so that content defines a type exactly once no
|
|
174
|
+
# matter how many of a module's generated headers end up in the same test file.
|
|
175
|
+
next if type_defining?(item, c_module)
|
|
104
176
|
io << item.text << "\n"
|
|
105
177
|
last_was_func = false
|
|
106
178
|
anything_emitted = true
|
|
107
179
|
when CExtractorTypes::CVariableDeclaration
|
|
108
180
|
next unless include_variables
|
|
109
|
-
# If there is no array involved, array_suffix collapses to an empty string
|
|
110
|
-
|
|
181
|
+
# If there is no array involved, array_suffix collapses to an empty string.
|
|
182
|
+
# A leading const/volatile/restrict on the original declaration (see
|
|
183
|
+
# qualifier_prefix) is part of the variable's type and belongs here too,
|
|
184
|
+
# so this extern agrees with the definition and any other declaration of
|
|
185
|
+
# the same variable elsewhere in the translation unit.
|
|
186
|
+
io << "extern #{qualifier_prefix(item)}#{item.type} #{item.name}#{item.array_suffix};\n"
|
|
111
187
|
last_was_func = false
|
|
112
188
|
anything_emitted = true
|
|
113
189
|
when CExtractorTypes::CFunctionDefinition, CExtractorTypes::CFunctionDeclaration
|
|
@@ -170,7 +246,12 @@ class GeneratorPartials
|
|
|
170
246
|
c_module.element_sequence.each do |item|
|
|
171
247
|
case item
|
|
172
248
|
when CExtractorTypes::CVariableDeclaration
|
|
173
|
-
|
|
249
|
+
# `item.text` is the declaration with its leading decorator run removed (see
|
|
250
|
+
# CExtractorDeclarations#extract_decorators), so a const/volatile/restrict
|
|
251
|
+
# qualifier on the base type is reapplied here via qualifier_prefix -- this
|
|
252
|
+
# definition's type must match the extern declaration emitted for the same
|
|
253
|
+
# variable in generate_header.
|
|
254
|
+
io << "#{qualifier_prefix(item)}#{item.text}\n"
|
|
174
255
|
last_was_func = false
|
|
175
256
|
anything_emitted = true
|
|
176
257
|
when CExtractorTypes::CFunctionDefinition
|