ceedling 1.1.1 → 1.1.2

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +3 -3
  4. data/assets/broken_uncovered_file.c +13 -0
  5. data/assets/test_example_file_2.c +22 -0
  6. data/assets/tests_with_partials_coverage/src/blanks.c +10 -0
  7. data/assets/tests_with_partials_coverage/src/blanks.h +1 -0
  8. data/assets/tests_with_partials_coverage/src/counter.c +12 -0
  9. data/assets/tests_with_partials_coverage/src/counter.h +1 -0
  10. data/assets/tests_with_partials_coverage/src/decorators.c +11 -0
  11. data/assets/tests_with_partials_coverage/src/decorators.h +4 -0
  12. data/assets/tests_with_partials_coverage/test/test_blanks.c +8 -0
  13. data/assets/tests_with_partials_coverage/test/test_counter.c +9 -0
  14. data/assets/tests_with_partials_coverage/test/test_decorators.c +8 -0
  15. data/docs/Changelog.md +17 -3
  16. data/lib/ceedling/c_extractor/c_extractor_declarations.rb +3 -1
  17. data/lib/ceedling/c_extractor/c_extractor_preprocessing.rb +20 -7
  18. data/lib/ceedling/generators/generator_partials.rb +13 -3
  19. data/lib/ceedling/objects.yml +1 -0
  20. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +10 -2
  21. data/lib/ceedling/preprocess/preprocessinator_reconstructor.rb +23 -15
  22. data/lib/version.rb +1 -1
  23. data/site-local/sitemap.xml.gz +0 -0
  24. data/spec/system/gcov_deployment_spec.rb +20 -6
  25. data/spec/system/support/gcov_common_test_cases.rb +31 -45
  26. data/spec/system/support/gcov_partials_test_cases.rb +116 -0
  27. data/spec/units/c_extractor/c_extractor_declarations_spec.rb +128 -0
  28. data/spec/units/c_extractor/c_extractor_definitions_spec.rb +7 -0
  29. data/spec/units/c_extractor/c_extractor_integration_spec.rb +17 -0
  30. data/spec/units/c_extractor/c_extractor_preprocessing_spec.rb +46 -0
  31. data/spec/units/generators/generator_partials_spec.rb +32 -6
  32. data/spec/units/preprocess/preprocessinator_file_assembler_spec.rb +96 -2
  33. data/spec/units/preprocess/preprocessinator_reconstructor_spec.rb +151 -1
  34. metadata +15 -2
@@ -17,7 +17,7 @@ RSpec.describe PreprocessinatorFileAssembler do
17
17
  # Use real ParsingParcels for _filter_conditionals and collect_file_contents_fallback tests
18
18
  let(:real_parsing_parcels) { ParsingParcels.new }
19
19
  let(:real_reconstructor) do
20
- PreprocessinatorReconstructor.new({ parsing_parcels: real_parsing_parcels })
20
+ PreprocessinatorReconstructor.new({ parsing_parcels: real_parsing_parcels, file_wrapper: @file_wrapper })
21
21
  end
22
22
 
23
23
  before :each do
@@ -359,9 +359,12 @@ RSpec.describe PreprocessinatorFileAssembler do
359
359
 
360
360
  let(:preprocessed_filepath) { '/build/mocks/test_module/module.h' }
361
361
 
362
+ # Binary mode ('wb'): `contents` lines assembled into this file come from
363
+ # an upstream preprocessed file and must reach disk with their line
364
+ # endings unaltered, which text mode would not guarantee.
362
365
  def stub_file_write(filepath)
363
366
  output = StringIO.new
364
- allow(@file_wrapper).to receive(:open).with(filepath, 'w').and_yield(output)
367
+ allow(@file_wrapper).to receive(:open).with(filepath, 'wb').and_yield(output)
365
368
  output
366
369
  end
367
370
 
@@ -413,6 +416,97 @@ RSpec.describe PreprocessinatorFileAssembler do
413
416
  expect(output.string).to include('void foo(void);')
414
417
  end
415
418
 
419
+ it 'opens its output file in binary mode' do
420
+ stub_file_write(preprocessed_filepath)
421
+
422
+ subject.assemble_preprocessed_header_file(
423
+ filename: 'module.h',
424
+ preprocessed_filepath: preprocessed_filepath,
425
+ contents: [],
426
+ extras: [],
427
+ includes: []
428
+ )
429
+
430
+ expect(@file_wrapper).to have_received(:open).with(preprocessed_filepath, 'wb')
431
+ end
432
+
433
+ it "passes content lines through to the output file unchanged, including embedded CRLF" do
434
+ output = stub_file_write(preprocessed_filepath)
435
+ content_line_with_crlf = "void foo(void) {\r\n return;\r\n}"
436
+
437
+ subject.assemble_preprocessed_header_file(
438
+ filename: 'module.h',
439
+ preprocessed_filepath: preprocessed_filepath,
440
+ contents: [content_line_with_crlf],
441
+ extras: [],
442
+ includes: []
443
+ )
444
+
445
+ expect(output.string).to include(content_line_with_crlf)
446
+ end
447
+
448
+ end
449
+
450
+
451
+ # ===========================================================================
452
+ describe '#assemble_preprocessed_code_file' do
453
+ # ===========================================================================
454
+
455
+ let(:preprocessed_filepath) { '/build/test/preprocess/module.c' }
456
+
457
+ # Binary mode ('wb'): `contents` lines assembled into this file come from
458
+ # an upstream preprocessed file and must reach disk with their line
459
+ # endings unaltered, which text mode would not guarantee.
460
+ def stub_file_write(filepath)
461
+ output = StringIO.new
462
+ allow(@file_wrapper).to receive(:open).with(filepath, 'wb').and_yield(output)
463
+ output
464
+ end
465
+
466
+ it 'writes provided includes and contents to the output file' do
467
+ output = stub_file_write(preprocessed_filepath)
468
+
469
+ subject.assemble_preprocessed_code_file(
470
+ filename: 'module.c',
471
+ preprocessed_filepath: preprocessed_filepath,
472
+ contents: ['void foo(void) {}'],
473
+ extras: [],
474
+ includes: ['#include "bar.h"']
475
+ )
476
+
477
+ expect(output.string).to include('#include "bar.h"')
478
+ expect(output.string).to include('void foo(void) {}')
479
+ end
480
+
481
+ it 'opens its output file in binary mode' do
482
+ stub_file_write(preprocessed_filepath)
483
+
484
+ subject.assemble_preprocessed_code_file(
485
+ filename: 'module.c',
486
+ preprocessed_filepath: preprocessed_filepath,
487
+ contents: [],
488
+ extras: [],
489
+ includes: []
490
+ )
491
+
492
+ expect(@file_wrapper).to have_received(:open).with(preprocessed_filepath, 'wb')
493
+ end
494
+
495
+ it "passes content lines through to the output file unchanged, including embedded CRLF" do
496
+ output = stub_file_write(preprocessed_filepath)
497
+ content_line_with_crlf = "void foo(void) {\r\n return;\r\n}"
498
+
499
+ subject.assemble_preprocessed_code_file(
500
+ filename: 'module.c',
501
+ preprocessed_filepath: preprocessed_filepath,
502
+ contents: [content_line_with_crlf],
503
+ extras: [],
504
+ includes: []
505
+ )
506
+
507
+ expect(output.string).to include(content_line_with_crlf)
508
+ end
509
+
416
510
  end
417
511
 
418
512
  end
@@ -12,9 +12,11 @@ require 'ceedling/parsing_parcels'
12
12
  describe PreprocessinatorReconstructor do
13
13
  before(:each) do
14
14
  @parsing_parcels = ParsingParcels.new()
15
+ @file_wrapper = double( "FileWrapper" )
15
16
  @extractor = described_class.new(
16
17
  {
17
- :parsing_parcels => @parsing_parcels
18
+ :parsing_parcels => @parsing_parcels,
19
+ :file_wrapper => @file_wrapper,
18
20
  }
19
21
  )
20
22
  end
@@ -75,6 +77,105 @@ describe PreprocessinatorReconstructor do
75
77
  expect( @extractor.extract_file_as_array_from_expansion( input, filepath ) ).to eq expected
76
78
  end
77
79
 
80
+ it "should preserve a run of multiple consecutive blank lines rather than collapsing to one" do
81
+ filepath = "this/path/MY_FILE.C"
82
+
83
+ file_contents = [
84
+ '# 1 "./this/path/MY_FILE.C" 99999',
85
+ 'void some_function(void) {',
86
+ '',
87
+ '',
88
+ '',
89
+ ' return 0;',
90
+ '}'
91
+ ]
92
+
93
+ # All three blank lines must survive -- e.g. a stripped multi-line comment
94
+ # or literal blank lines in the source, either way nothing gets dropped.
95
+ expected = [
96
+ 'void some_function(void) {',
97
+ '',
98
+ '',
99
+ '',
100
+ ' return 0;',
101
+ '}'
102
+ ]
103
+
104
+ input = StringIO.new( file_contents.join( "\n" ) )
105
+
106
+ expect( @extractor.extract_file_as_array_from_expansion( input, filepath ) ).to eq expected
107
+ end
108
+
109
+ it "should preserve independent runs of differing blank-line counts between separate code lines" do
110
+ filepath = "path/multi.c"
111
+
112
+ file_contents = [
113
+ '# 1 "path/multi.c" 1',
114
+ 'int a;',
115
+ '',
116
+ '',
117
+ 'int b;',
118
+ '',
119
+ 'int c;',
120
+ '',
121
+ '',
122
+ '',
123
+ 'int d;'
124
+ ]
125
+
126
+ # Each run's count must stay attached to its own gap -- not merged with a
127
+ # neighboring run, not truncated to a shared count.
128
+ expected = [
129
+ 'int a;',
130
+ '',
131
+ '',
132
+ 'int b;',
133
+ '',
134
+ 'int c;',
135
+ '',
136
+ '',
137
+ '',
138
+ 'int d;'
139
+ ]
140
+
141
+ input = StringIO.new( file_contents.join( "\n" ) )
142
+
143
+ expect( @extractor.extract_file_as_array_from_expansion( input, filepath ) ).to eq expected
144
+ end
145
+
146
+ it "should preserve a following blank-line run after a marker-continuation (dangling semicolon) fragment" do
147
+ filepath = "sys/types_wrapper.c"
148
+
149
+ file_contents = [
150
+ '# 1 "sys/types_wrapper.c" 1',
151
+ 'typedef unsigned long uint32_t', # System typedef expansion, unterminated
152
+ '# 1 "sys/types_wrapper.c" 2', # GCC re-marks the SAME source line...
153
+ ';', # ...to attach a dangling ';' as a continuation, not a new line
154
+ '',
155
+ '',
156
+ 'void some_function(void) {',
157
+ ' return;',
158
+ '}',
159
+ '# 1 "some/useless/file.c"'
160
+ ]
161
+
162
+ # The typedef and its dangling ';' must aggregate onto a single output
163
+ # line (existing behavior), and the two blank lines that follow -- e.g.
164
+ # from a stripped comment after the typedef -- must both still survive.
165
+ expected = [
166
+ 'typedef unsigned long uint32_t;',
167
+ '',
168
+ '',
169
+ 'void some_function(void) {',
170
+ ' return;',
171
+ '}'
172
+ ]
173
+
174
+ input = StringIO.new( file_contents.join( "\n" ) )
175
+
176
+ expect( @extractor.extract_file_as_array_from_expansion( input, filepath ) ).to eq expected
177
+ end
178
+
78
179
  it "should extract text of original file from preprocessed expansion with complex preprocessor line marker sequence" do
79
180
  filepath = "dir/our_file.c"
80
181
 
@@ -207,6 +308,8 @@ describe PreprocessinatorReconstructor do
207
308
  '',
208
309
  'static _Bool ecs_foo__init_structure(void);',
209
310
  '',
311
+ '',
312
+ '',
210
313
  'void ecs_foo_init(void) {',
211
314
  ' var1 = ecs_foo__init_structure();',
212
315
  '}',
@@ -697,4 +800,51 @@ describe PreprocessinatorReconstructor do
697
800
 
698
801
  end
699
802
 
803
+ context "#compact_file_from_expansion" do
804
+ it "should open its input in binary mode and its output in binary mode" do
805
+ # Binary mode on both ends: this content is read again downstream by code
806
+ # that depends on its line count being exact, so nothing here should be
807
+ # subject to platform-specific line ending translation.
808
+ input_handle = StringIO.new( '# 1 "WANT.c" 5' + "\n" + 'text_we_want();' + "\n" )
809
+ output_handle = StringIO.new
810
+
811
+ expect(@file_wrapper).to receive(:open).with( 'input.c', 'rb' ).and_yield( input_handle )
812
+ expect(@file_wrapper).to receive(:open).with( 'output.c', 'wb' ).and_yield( output_handle )
813
+
814
+ @extractor.compact_file_from_expansion(
815
+ input_filepath: 'input.c',
816
+ source_filepath: 'WANT.c',
817
+ output_filepath: 'output.c'
818
+ )
819
+ end
820
+
821
+ it "should extract the same content from CRLF-terminated input as from LF-terminated input" do
822
+ # GCC's preprocessor output on Windows is CRLF-terminated. Extraction must
823
+ # produce identical results regardless of the input's line endings, since
824
+ # the exact line count of what's extracted matters to code downstream.
825
+ lf_input = StringIO.new(
826
+ '# 1 "WANT.c" 5' + "\n" + "line_one();\n" + "\n" + "line_two();\n"
827
+ )
828
+ crlf_input = StringIO.new(
829
+ '# 1 "WANT.c" 5' + "\r\n" + "line_one();\r\n" + "\r\n" + "line_two();\r\n"
830
+ )
831
+ lf_output = StringIO.new
832
+ crlf_output = StringIO.new
833
+
834
+ allow(@file_wrapper).to receive(:open).with( 'lf.c', 'rb' ).and_yield( lf_input )
835
+ allow(@file_wrapper).to receive(:open).with( 'lf_out.c', 'wb' ).and_yield( lf_output )
836
+ allow(@file_wrapper).to receive(:open).with( 'crlf.c', 'rb' ).and_yield( crlf_input )
837
+ allow(@file_wrapper).to receive(:open).with( 'crlf_out.c', 'wb' ).and_yield( crlf_output )
838
+
839
+ @extractor.compact_file_from_expansion(
840
+ input_filepath: 'lf.c', source_filepath: 'WANT.c', output_filepath: 'lf_out.c'
841
+ )
842
+ @extractor.compact_file_from_expansion(
843
+ input_filepath: 'crlf.c', source_filepath: 'WANT.c', output_filepath: 'crlf_out.c'
844
+ )
845
+
846
+ expect( crlf_output.string ).to eq( lf_output.string )
847
+ end
848
+ end
849
+
700
850
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ceedling
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark VanderVoord
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-07-23 00:00:00.000000000 Z
13
+ date: 2026-07-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -167,6 +167,7 @@ files:
167
167
  - Gemfile.lock
168
168
  - README.md
169
169
  - Rakefile
170
+ - assets/broken_uncovered_file.c
170
171
  - assets/ceedling
171
172
  - assets/ceedling.cmd
172
173
  - assets/default_gitignore
@@ -179,6 +180,7 @@ files:
179
180
  - assets/example_file_with_stdio.h
180
181
  - assets/project.yml
181
182
  - assets/test_example_file.c
183
+ - assets/test_example_file_2.c
182
184
  - assets/test_example_file_boom.c
183
185
  - assets/test_example_file_crash_assert.c
184
186
  - assets/test_example_file_crash_sigsegv.c
@@ -203,6 +205,15 @@ files:
203
205
  - assets/tests_with_fallback_conditionals/src/conditional_module.h
204
206
  - assets/tests_with_fallback_conditionals/src/optional_dep.h
205
207
  - assets/tests_with_fallback_conditionals/test/test_conditionals.c
208
+ - assets/tests_with_partials_coverage/src/blanks.c
209
+ - assets/tests_with_partials_coverage/src/blanks.h
210
+ - assets/tests_with_partials_coverage/src/counter.c
211
+ - assets/tests_with_partials_coverage/src/counter.h
212
+ - assets/tests_with_partials_coverage/src/decorators.c
213
+ - assets/tests_with_partials_coverage/src/decorators.h
214
+ - assets/tests_with_partials_coverage/test/test_blanks.c
215
+ - assets/tests_with_partials_coverage/test/test_counter.c
216
+ - assets/tests_with_partials_coverage/test/test_decorators.c
206
217
  - assets/tests_with_preprocessing/src/adc_hardwareA.c
207
218
  - assets/tests_with_preprocessing/src/adc_hardwareA.h
208
219
  - assets/tests_with_preprocessing/src/adc_hardwareB.c
@@ -917,6 +928,7 @@ files:
917
928
  - spec/system/support/cppcheck_helpers.rb
918
929
  - spec/system/support/gcov_common_test_cases.rb
919
930
  - spec/system/support/gcov_helpers.rb
931
+ - spec/system/support/gcov_partials_test_cases.rb
920
932
  - spec/system/support/valgrind_common_test_cases.rb
921
933
  - spec/system/support/valgrind_helpers.rb
922
934
  - spec/system/upgrade_as_vendor_spec.rb
@@ -1762,6 +1774,7 @@ test_files:
1762
1774
  - spec/system/support/cppcheck_helpers.rb
1763
1775
  - spec/system/support/gcov_common_test_cases.rb
1764
1776
  - spec/system/support/gcov_helpers.rb
1777
+ - spec/system/support/gcov_partials_test_cases.rb
1765
1778
  - spec/system/support/valgrind_common_test_cases.rb
1766
1779
  - spec/system/support/valgrind_helpers.rb
1767
1780
  - spec/system/upgrade_as_vendor_spec.rb