ceedling 1.1.3 → 1.1.5
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_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/docs/Changelog.md +32 -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 +1 -0
- data/lib/ceedling/config/configurator_setup.rb +26 -0
- data/lib/ceedling/defaults.rb +8 -0
- 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 +103 -3
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +183 -83
- 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 +21 -8
- data/lib/version.rb +1 -1
- data/mkdocs.yml +1 -0
- 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 +4258 -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/system/deployment_as_gem_spec.rb +12 -0
- data/spec/system/deployment_as_vendor_spec.rb +17 -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 +137 -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/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 +223 -12
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +200 -37
- 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
- metadata +19 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# =========================================================================
|
|
2
|
+
# Ceedling - Test-Centered Build System for C
|
|
3
|
+
# ThrowTheSwitch.org
|
|
4
|
+
# Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
|
5
|
+
# SPDX-License-Identifier: MIT
|
|
6
|
+
# =========================================================================
|
|
7
|
+
|
|
8
|
+
require 'spec_helper'
|
|
9
|
+
require 'ceedling/config/configurator_setup'
|
|
10
|
+
require 'ceedling/reportinator'
|
|
11
|
+
|
|
12
|
+
# Only #validate_partials is covered here. The rest of ConfiguratorSetup has no unit spec
|
|
13
|
+
# at all today (its closest sibling, #validate_threads, is untested too) -- this file scopes
|
|
14
|
+
# itself to the new method rather than backfilling that existing gap.
|
|
15
|
+
describe ConfiguratorSetup do
|
|
16
|
+
before(:each) do
|
|
17
|
+
@configurator_builder = double('ConfiguratorBuilder')
|
|
18
|
+
@configurator_validator = double('ConfiguratorValidator')
|
|
19
|
+
@configurator_plugins = double('ConfiguratorPlugins')
|
|
20
|
+
@loginator = double('Loginator')
|
|
21
|
+
@reportinator = Reportinator.new
|
|
22
|
+
@file_wrapper = double('FileWrapper')
|
|
23
|
+
|
|
24
|
+
@setup = described_class.new(
|
|
25
|
+
{
|
|
26
|
+
configurator_builder: @configurator_builder,
|
|
27
|
+
configurator_validator: @configurator_validator,
|
|
28
|
+
configurator_plugins: @configurator_plugins,
|
|
29
|
+
loginator: @loginator,
|
|
30
|
+
reportinator: @reportinator,
|
|
31
|
+
file_wrapper: @file_wrapper
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "#validate_partials" do
|
|
37
|
+
it "accepts an integer at the minimum" do
|
|
38
|
+
config = { partials: { max_extraction_length: 10 } }
|
|
39
|
+
expect(@setup.validate_partials(config)).to be true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "accepts an integer above the minimum" do
|
|
43
|
+
config = { partials: { max_extraction_length: 5000 } }
|
|
44
|
+
expect(@setup.validate_partials(config)).to be true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "rejects a non-integer value" do
|
|
48
|
+
config = { partials: { max_extraction_length: '5000' } }
|
|
49
|
+
expect(@loginator).to receive(:log)
|
|
50
|
+
.with(/:partials ↳ :max_extraction_length is not an integer/, Verbosity::ERRORS)
|
|
51
|
+
expect(@setup.validate_partials(config)).to be false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "rejects an integer below the minimum" do
|
|
55
|
+
config = { partials: { max_extraction_length: 9 } }
|
|
56
|
+
expect(@loginator).to receive(:log)
|
|
57
|
+
.with(/:partials ↳ :max_extraction_length must be at least 10/, Verbosity::ERRORS)
|
|
58
|
+
expect(@setup.validate_partials(config)).to be false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -271,4 +271,21 @@ describe FilePathUtils do
|
|
|
271
271
|
end
|
|
272
272
|
end
|
|
273
273
|
|
|
274
|
+
describe '#form_partial_types_header_filename' do
|
|
275
|
+
before(:each) do
|
|
276
|
+
@fpu = described_class.new({
|
|
277
|
+
:configurator => double('configurator'),
|
|
278
|
+
:file_wrapper => double('file_wrapper')
|
|
279
|
+
})
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'forms a filename from the module name, the partial prefix, and a _types suffix' do
|
|
283
|
+
# Mirrors the sibling _interface/_impl header filename conventions -- a shared
|
|
284
|
+
# location for a module's typedefs and aggregate definitions needs a name
|
|
285
|
+
# distinct from those two generated headers so all three can coexist.
|
|
286
|
+
expect( @fpu.form_partial_types_header_filename('LightSensor') )
|
|
287
|
+
.to eq('ceedling_partial_LightSensor_types.h')
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
274
291
|
end
|
|
@@ -21,30 +21,57 @@ describe GeneratorHelper do
|
|
|
21
21
|
@helper = described_class.new({ :loginator => @loginator })
|
|
22
22
|
|
|
23
23
|
# A minimal status double for shell_result[:status].
|
|
24
|
-
@ok_status = double('status', termsig: nil)
|
|
24
|
+
@ok_status = double('status', termsig: nil, signaled?: false, exitstatus: 0, success?: true)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
# ---------------------------------------------------------------------------
|
|
29
29
|
# Helper: build a shell_result hash
|
|
30
30
|
# ---------------------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
# `success` left unspecified derives from `termsig`/`exitstatus` the way a real
|
|
32
|
+
# Process::Status would -- signaled or nonzero-exit is never success. Callers that
|
|
33
|
+
# need an inconsistent combination (a status that looks successful despite a nonzero
|
|
34
|
+
# exit, exercised nowhere here) can still pass `success:` explicitly.
|
|
35
|
+
def shell_result(output: VALID_STATS_OUTPUT, stderr: '', termsig: nil, exitstatus: 0, success: nil)
|
|
36
|
+
success = (termsig.nil? && exitstatus == 0) if success.nil?
|
|
37
|
+
status = double(
|
|
38
|
+
'status', termsig: termsig, signaled?: !termsig.nil?, exitstatus: exitstatus, success?: success
|
|
39
|
+
)
|
|
33
40
|
{ :output => output, :stderr => stderr, :status => status }
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
describe '#test_crash?' do
|
|
38
45
|
|
|
39
|
-
context '
|
|
40
|
-
it 'returns true when the process was terminated by signal 11' do
|
|
46
|
+
context 'signal death (any signal, not just SIGSEGV)' do
|
|
47
|
+
it 'returns true when the process was terminated by signal 11 (SIGSEGV)' do
|
|
41
48
|
result = shell_result(termsig: 11)
|
|
42
49
|
expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
|
|
43
50
|
end
|
|
44
51
|
|
|
45
|
-
it '
|
|
46
|
-
result = shell_result(termsig: 6)
|
|
52
|
+
it 'returns true when the process was terminated by signal 6 (SIGABRT) -- e.g. UBSan/ASan halt_on_error' do
|
|
53
|
+
result = shell_result(termsig: 6)
|
|
47
54
|
# output still has stats, stderr is empty → only the signal check matters here
|
|
55
|
+
expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context 'a clean summary contradicted by the real exit status' do
|
|
60
|
+
it 'returns true when 0 failures are reported but the real process exited nonzero and unsignaled' do
|
|
61
|
+
# e.g. LeakSanitizer detecting a leak during teardown, after every test case
|
|
62
|
+
# already printed and Unity's own summary already looked clean.
|
|
63
|
+
result = shell_result(output: VALID_STATS_OUTPUT, exitstatus: 1, success: false)
|
|
64
|
+
expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'returns false when a nonzero real exit is consistent with Unity reporting real failures' do
|
|
68
|
+
failing_output = "\n---------\n2 Tests 1 Failures 0 Ignored\nFAIL\n"
|
|
69
|
+
result = shell_result(output: failing_output, exitstatus: 1, success: false)
|
|
70
|
+
expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'returns false when 0 failures are reported and the real process exited successfully' do
|
|
74
|
+
result = shell_result(output: VALID_STATS_OUTPUT, exitstatus: 0, success: true)
|
|
48
75
|
expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be false
|
|
49
76
|
end
|
|
50
77
|
end
|
|
@@ -222,6 +222,183 @@ describe GeneratorPartials do
|
|
|
222
222
|
end
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
+
context "#generate_types" do
|
|
226
|
+
it "writes nothing and returns nil when the module has no typedefs or aggregate definitions" do
|
|
227
|
+
allow(@file_wrapper).to receive(:open)
|
|
228
|
+
|
|
229
|
+
c_module = make_module(
|
|
230
|
+
make_stmt(text: "#define FOO 1", line_num: 1),
|
|
231
|
+
make_var(name: 'counter', type: 'int', text: 'int counter;', line_num: 2)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
result = @generator.generate_types(
|
|
235
|
+
name: 'my_module',
|
|
236
|
+
c_module: c_module,
|
|
237
|
+
output_path: '/path/to/output'
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
expect(@file_wrapper).not_to have_received(:open)
|
|
241
|
+
expect(result).to be_nil
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "writes the typedefs and aggregate definitions, guarded and in element_sequence order, carrying forward a macro that precedes one of them, and returns the bare filename" do
|
|
245
|
+
file_contents = <<~CONTENTS
|
|
246
|
+
#ifndef __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
247
|
+
#define __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
248
|
+
|
|
249
|
+
typedef uint8_t Byte;
|
|
250
|
+
#define FOO 1
|
|
251
|
+
struct Config { int id; };
|
|
252
|
+
|
|
253
|
+
#endif // __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
254
|
+
|
|
255
|
+
CONTENTS
|
|
256
|
+
|
|
257
|
+
output_path = '/path/to/output'
|
|
258
|
+
name = 'my_module'
|
|
259
|
+
header_filename = 'my_module_types.h'
|
|
260
|
+
expected_filepath = File.join(output_path, header_filename)
|
|
261
|
+
|
|
262
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename)
|
|
263
|
+
.with(name)
|
|
264
|
+
.and_return(header_filename)
|
|
265
|
+
|
|
266
|
+
buf = StringIO.new()
|
|
267
|
+
allow(@file_wrapper).to receive(:open)
|
|
268
|
+
.with(expected_filepath, 'wb')
|
|
269
|
+
.and_yield(buf)
|
|
270
|
+
|
|
271
|
+
# A variable and a macro sit between the two type-defining statements in
|
|
272
|
+
# element_sequence. The variable (which stays behind for generate_header) is
|
|
273
|
+
# entirely absent here, but the macro precedes the aggregate and is carried
|
|
274
|
+
# forward immediately before it -- the aggregate's definition may depend on it
|
|
275
|
+
# (e.g. an array-size constant), and this header is #included before
|
|
276
|
+
# generate_header's own later, inline copy of that same macro would exist.
|
|
277
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef uint8_t Byte;", line_num: 1)
|
|
278
|
+
var_decl = make_var(name: 'counter', type: 'int', text: 'int counter;', line_num: 2)
|
|
279
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 3)
|
|
280
|
+
aggregate_stmt = CExtractorTypes::CStatement.new(text: "struct Config { int id; };", line_num: 4)
|
|
281
|
+
|
|
282
|
+
c_module = CExtractorTypes::CModule.new(
|
|
283
|
+
type_definitions: [typedef_stmt],
|
|
284
|
+
macro_definitions: [macro_stmt],
|
|
285
|
+
variable_declarations: [var_decl],
|
|
286
|
+
aggregate_definitions: [aggregate_stmt],
|
|
287
|
+
element_sequence: [typedef_stmt, var_decl, macro_stmt, aggregate_stmt]
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
result = @generator.generate_types(
|
|
291
|
+
name: name,
|
|
292
|
+
c_module: c_module,
|
|
293
|
+
output_path: output_path
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
expect( buf.string.strip() ).to eq file_contents.strip()
|
|
297
|
+
expect(@file_wrapper).to have_received(:open).with(expected_filepath, 'wb')
|
|
298
|
+
expect(result).to eq(header_filename)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
it "carries a macro forward directly before the single typedef it immediately precedes" do
|
|
302
|
+
output_path = '/path/to/output'
|
|
303
|
+
name = 'my_module'
|
|
304
|
+
header_filename = 'my_module_types.h'
|
|
305
|
+
expected_filepath = File.join(output_path, header_filename)
|
|
306
|
+
|
|
307
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
308
|
+
|
|
309
|
+
buf = StringIO.new()
|
|
310
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
311
|
+
|
|
312
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define LOCAL_CAPACITY 4", line_num: 1)
|
|
313
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef struct { int values[LOCAL_CAPACITY]; } Context;", line_num: 3)
|
|
314
|
+
|
|
315
|
+
c_module = CExtractorTypes::CModule.new(
|
|
316
|
+
type_definitions: [typedef_stmt],
|
|
317
|
+
macro_definitions: [macro_stmt],
|
|
318
|
+
element_sequence: [macro_stmt, typedef_stmt]
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
322
|
+
|
|
323
|
+
expect( buf.string ).to include( "#define LOCAL_CAPACITY 4\ntypedef struct { int values[LOCAL_CAPACITY]; } Context;\n" )
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it "carries forward multiple macros preceding one type-defining item, in their own original relative order" do
|
|
327
|
+
output_path = '/path/to/output'
|
|
328
|
+
name = 'my_module'
|
|
329
|
+
header_filename = 'my_module_types.h'
|
|
330
|
+
|
|
331
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
332
|
+
|
|
333
|
+
buf = StringIO.new()
|
|
334
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
335
|
+
|
|
336
|
+
macro_a = CExtractorTypes::CStatement.new(text: "#define WIDTH 4", line_num: 1)
|
|
337
|
+
macro_b = CExtractorTypes::CStatement.new(text: "#define HEIGHT 8", line_num: 2)
|
|
338
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Grid[WIDTH][HEIGHT];", line_num: 3)
|
|
339
|
+
|
|
340
|
+
c_module = CExtractorTypes::CModule.new(
|
|
341
|
+
type_definitions: [typedef_stmt],
|
|
342
|
+
macro_definitions: [macro_a, macro_b],
|
|
343
|
+
element_sequence: [macro_a, macro_b, typedef_stmt]
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
347
|
+
|
|
348
|
+
expect( buf.string ).to include( "#define WIDTH 4\n#define HEIGHT 8\ntypedef int Grid[WIDTH][HEIGHT];\n" )
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it "carries a macro forward only once, before the first type-defining item it precedes, not before a later one too" do
|
|
352
|
+
output_path = '/path/to/output'
|
|
353
|
+
name = 'my_module'
|
|
354
|
+
header_filename = 'my_module_types.h'
|
|
355
|
+
|
|
356
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
357
|
+
|
|
358
|
+
buf = StringIO.new()
|
|
359
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
360
|
+
|
|
361
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 1)
|
|
362
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Foo;", line_num: 2)
|
|
363
|
+
aggregate_stmt = CExtractorTypes::CStatement.new(text: "struct Bar { int x; };", line_num: 3)
|
|
364
|
+
|
|
365
|
+
c_module = CExtractorTypes::CModule.new(
|
|
366
|
+
type_definitions: [typedef_stmt],
|
|
367
|
+
macro_definitions: [macro_stmt],
|
|
368
|
+
aggregate_definitions: [aggregate_stmt],
|
|
369
|
+
element_sequence: [macro_stmt, typedef_stmt, aggregate_stmt]
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
373
|
+
|
|
374
|
+
expect( buf.string.scan("#define FOO 1").length ).to eq(1)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it "does not carry a macro into the shared header when it appears only after every type-defining item" do
|
|
378
|
+
output_path = '/path/to/output'
|
|
379
|
+
name = 'my_module'
|
|
380
|
+
header_filename = 'my_module_types.h'
|
|
381
|
+
|
|
382
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
383
|
+
|
|
384
|
+
buf = StringIO.new()
|
|
385
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
386
|
+
|
|
387
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Foo;", line_num: 1)
|
|
388
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 2)
|
|
389
|
+
|
|
390
|
+
c_module = CExtractorTypes::CModule.new(
|
|
391
|
+
type_definitions: [typedef_stmt],
|
|
392
|
+
macro_definitions: [macro_stmt],
|
|
393
|
+
element_sequence: [typedef_stmt, macro_stmt]
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
397
|
+
|
|
398
|
+
expect( buf.string ).to_not include("#define FOO 1")
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
225
402
|
context "#generate_header (private method)" do
|
|
226
403
|
# Define common StringIO buffer
|
|
227
404
|
let(:buf) { StringIO.new() }
|
|
@@ -282,6 +459,24 @@ describe GeneratorPartials do
|
|
|
282
459
|
expect( buf.string.strip() ).to eq file_contents.strip()
|
|
283
460
|
end
|
|
284
461
|
|
|
462
|
+
it "should reapply a leading qualifier decorator to an extern declaration" do
|
|
463
|
+
c_module = make_module(
|
|
464
|
+
make_var(name: 'cmd', type: 'cmd_t', text: 'cmd_t cmd = { 0 };', decorators: ['static', 'volatile'])
|
|
465
|
+
)
|
|
466
|
+
@generator.send(:generate_header, buf, 'mymod', [], [], c_module, true)
|
|
467
|
+
|
|
468
|
+
expect(buf.string).to include('extern volatile cmd_t cmd;')
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
it "should not reapply a qualifier already embedded in a pointer-level declarator" do
|
|
472
|
+
c_module = make_module(
|
|
473
|
+
make_var(name: 'lookup_table', type: 'uint8_t* const', text: 'uint8_t* const lookup_table;', decorators: ['static', 'const'])
|
|
474
|
+
)
|
|
475
|
+
@generator.send(:generate_header, buf, 'mymod', [], [], c_module, true)
|
|
476
|
+
|
|
477
|
+
expect(buf.string).to include('extern const uint8_t* const lookup_table;')
|
|
478
|
+
end
|
|
479
|
+
|
|
285
480
|
it "should generate a header file with #include statements, variable declarations, and function signatures" do
|
|
286
481
|
file_contents = <<~CONTENTS
|
|
287
482
|
#ifndef __CEEDLING_GENERATED_APPLES_AND_BANANAS_H__
|
|
@@ -360,23 +555,29 @@ describe GeneratorPartials do
|
|
|
360
555
|
expect( buf.string.strip() ).to eq file_contents.strip()
|
|
361
556
|
end
|
|
362
557
|
|
|
363
|
-
it "should emit
|
|
364
|
-
# One item of each category that can
|
|
365
|
-
# macro_definitions
|
|
366
|
-
# type_definitions
|
|
367
|
-
# aggregate_definitions → CStatement
|
|
368
|
-
# variable_declarations → CVariableDeclaration emitted as extern declaration
|
|
558
|
+
it "should emit macro and variable statements inline while routing typedefs and aggregates to the shared types header" do
|
|
559
|
+
# One item of each category that can appear in a generated Partial header:
|
|
560
|
+
# macro_definitions → CStatement emitted as-is, inline
|
|
561
|
+
# type_definitions → CStatement skipped here; belongs to generate_types instead
|
|
562
|
+
# aggregate_definitions → CStatement skipped here; belongs to generate_types instead
|
|
563
|
+
# variable_declarations → CVariableDeclaration emitted as extern declaration, inline
|
|
564
|
+
#
|
|
565
|
+
# A typedef or aggregate defines a type, and C rejects a second definition of the same
|
|
566
|
+
# type in one translation unit even when it's textually identical -- unlike a macro,
|
|
567
|
+
# which may legally repeat. Since a module's implementation and interface headers can
|
|
568
|
+
# both be included in the same test file, generate_header always defers type-defining
|
|
569
|
+
# content to the one shared types header generate_types produces, rather than risking
|
|
570
|
+
# two independent copies landing in the same translation unit.
|
|
369
571
|
#
|
|
370
572
|
# Ordering is intentionally interleaved (not grouped by category) to confirm that
|
|
371
|
-
# element_sequence — not typed-array membership — governs emit order
|
|
573
|
+
# element_sequence — not typed-array membership — governs emit order for the items
|
|
574
|
+
# that do remain inline.
|
|
372
575
|
file_contents = <<~CONTENTS
|
|
373
576
|
#ifndef __CEEDLING_GENERATED_ALL_STATEMENTS_H__
|
|
374
577
|
#define __CEEDLING_GENERATED_ALL_STATEMENTS_H__
|
|
375
578
|
|
|
376
579
|
#define MAX_ITEMS 16
|
|
377
|
-
typedef uint8_t Byte;
|
|
378
580
|
extern int item_count;
|
|
379
|
-
struct Config { int id; int flags; };
|
|
380
581
|
|
|
381
582
|
#endif // __CEEDLING_GENERATED_ALL_STATEMENTS_H__
|
|
382
583
|
|
|
@@ -443,13 +644,11 @@ describe GeneratorPartials do
|
|
|
443
644
|
expect( buf.string.strip() ).to eq file_contents.strip()
|
|
444
645
|
end
|
|
445
646
|
|
|
446
|
-
it "should interleave functions with other elements in element_sequence order" do
|
|
647
|
+
it "should interleave functions with other elements in element_sequence order, skipping a leading typedef routed to the shared types header" do
|
|
447
648
|
file_contents = <<~CONTENTS
|
|
448
649
|
#ifndef __CEEDLING_GENERATED_INTERLEAVED_H__
|
|
449
650
|
#define __CEEDLING_GENERATED_INTERLEAVED_H__
|
|
450
651
|
|
|
451
|
-
typedef uint8_t Byte;
|
|
452
|
-
|
|
453
652
|
void foo(void);
|
|
454
653
|
|
|
455
654
|
extern int counter;
|
|
@@ -460,6 +659,8 @@ describe GeneratorPartials do
|
|
|
460
659
|
|
|
461
660
|
CONTENTS
|
|
462
661
|
|
|
662
|
+
# typedef_stmt is a member of type_definitions, so generate_header leaves it out here;
|
|
663
|
+
# it's the sole responsibility of generate_types (see the "shared types header" tests)
|
|
463
664
|
typedef_stmt = make_stmt(text: "typedef uint8_t Byte;", line_num: 1)
|
|
464
665
|
var_decl = make_var( name: 'counter', type: 'int', text: 'int counter;', line_num: 5)
|
|
465
666
|
|
|
@@ -568,6 +769,16 @@ describe GeneratorPartials do
|
|
|
568
769
|
expect( buf.string.strip() ).to eq file_contents.strip()
|
|
569
770
|
end
|
|
570
771
|
|
|
772
|
+
it "should reapply a leading qualifier decorator to a variable definition" do
|
|
773
|
+
c_module = make_module(
|
|
774
|
+
make_var(name: 'cmd', type: 'cmd_t', text: 'cmd_t cmd = { 0 };', decorators: ['static', 'volatile'])
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
@generator.send(:generate_source, buf, [], [], c_module)
|
|
778
|
+
|
|
779
|
+
expect(buf.string).to include('volatile cmd_t cmd = { 0 };')
|
|
780
|
+
end
|
|
781
|
+
|
|
571
782
|
it "should generate a source file with include directives, variable declarations, and functions" do
|
|
572
783
|
file_contents = <<~CONTENTS
|
|
573
784
|
// Ceeding generated file
|