Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive unit testing infrastructure for the C2A DriverSuper component, introducing 75 GoogleTest (C++) test cases wrapped in a Rust crate for integration with cargo test. The implementation uses a custom build.rs script to invoke CMake, build the C++ tests, and generate Rust test wrappers that execute individual GoogleTest cases.
Changes:
- Added 46 test cases for StreamRecBuffer covering push/drop/confirm/move operations and edge cases
- Added 29 test cases for FrameAnalysis covering fixed-length, variable-length, and footer-terminated frame protocols
- Created Rust build system integration to run C++ tests via cargo test
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| component_driver/tests/test_stream_rec_buffer.cpp | Comprehensive tests for buffer management operations; embeds production code (critical issue) |
| component_driver/tests/test_frame_analysis.cpp | Tests for frame parsing with multiple protocol patterns; embeds production code (critical issue) |
| component_driver/tests/mocks/*.{h,c} | Mock implementations for HAL, TimeManager, and CCP dependencies |
| component_driver/tests/CMakeLists.txt | CMake configuration for building GoogleTest tests |
| component_driver/build.rs | Build script to invoke CMake and generate Rust test wrappers |
| component_driver/Cargo.toml | Package definition; missing build.rs reference (critical issue) |
| component_driver/src/lib.rs | Library entry point that includes generated tests |
| Cargo.toml | Workspace member addition |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { | ||
| if (stream_rec_buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (size == 0) return CDS_ERR_CODE_OK; | ||
|
|
||
| if (stream_rec_buffer->size + size > stream_rec_buffer->capacity) | ||
| return CDS_ERR_CODE_ERR; | ||
|
|
||
| memcpy(stream_rec_buffer->buffer + stream_rec_buffer->size, buffer, size); | ||
| stream_rec_buffer->size += size; | ||
| return CDS_ERR_CODE_OK; | ||
| } | ||
|
|
||
| uint16_t CDS_get_unprocessed_size_from_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer) | ||
| { | ||
| if (stream_rec_buffer == NULL) return 0; | ||
|
|
||
| uint16_t processed = stream_rec_buffer->pos_of_frame_head_candidate + | ||
| stream_rec_buffer->confirmed_frame_len; | ||
| if (processed >= stream_rec_buffer->size) return 0; | ||
| return stream_rec_buffer->size - processed; | ||
| } | ||
|
|
||
| void CDS_confirm_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
| stream_rec_buffer->confirmed_frame_len = size; | ||
| } | ||
|
|
||
| void CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | ||
| uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
| stream_rec_buffer->pos_of_frame_head_candidate += size; | ||
| stream_rec_buffer->confirmed_frame_len = 0; | ||
|
|
||
| if (stream_rec_buffer->pos_of_frame_head_candidate > stream_rec_buffer->size) | ||
| stream_rec_buffer->pos_of_frame_head_candidate = stream_rec_buffer->size; | ||
| } | ||
|
|
There was a problem hiding this comment.
The test file embeds the production implementation of StreamRecBuffer functions directly (lines 57-153). This creates several problems:
- The tests are not testing the actual production code, but rather a copy of it
- Code duplication means the embedded code can drift from the real implementation
- Changes to the production code won't be caught by these tests
- It's unclear whether the embedded implementation matches the current production code
Instead, these tests should link against the actual driver_super.c implementation. The CMakeLists.txt should be updated to compile driver_super.c and link it with the test executables.
| { | |
| if (stream_rec_buffer == NULL) return CDS_ERR_CODE_ERR; | |
| if (buffer == NULL) return CDS_ERR_CODE_ERR; | |
| if (size == 0) return CDS_ERR_CODE_OK; | |
| if (stream_rec_buffer->size + size > stream_rec_buffer->capacity) | |
| return CDS_ERR_CODE_ERR; | |
| memcpy(stream_rec_buffer->buffer + stream_rec_buffer->size, buffer, size); | |
| stream_rec_buffer->size += size; | |
| return CDS_ERR_CODE_OK; | |
| } | |
| uint16_t CDS_get_unprocessed_size_from_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer) | |
| { | |
| if (stream_rec_buffer == NULL) return 0; | |
| uint16_t processed = stream_rec_buffer->pos_of_frame_head_candidate + | |
| stream_rec_buffer->confirmed_frame_len; | |
| if (processed >= stream_rec_buffer->size) return 0; | |
| return stream_rec_buffer->size - processed; | |
| } | |
| void CDS_confirm_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, uint16_t size) | |
| { | |
| if (stream_rec_buffer == NULL) return; | |
| stream_rec_buffer->confirmed_frame_len = size; | |
| } | |
| void CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | |
| uint16_t size) | |
| { | |
| if (stream_rec_buffer == NULL) return; | |
| stream_rec_buffer->pos_of_frame_head_candidate += size; | |
| stream_rec_buffer->confirmed_frame_len = 0; | |
| if (stream_rec_buffer->pos_of_frame_head_candidate > stream_rec_buffer->size) | |
| stream_rec_buffer->pos_of_frame_head_candidate = stream_rec_buffer->size; | |
| } | |
| ; | |
| extern CDS_ERR_CODE CDS_rec_stream_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | |
| const uint8_t* buffer, | |
| uint16_t size); | |
| extern uint16_t CDS_get_unprocessed_size_from_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer); | |
| extern void CDS_confirm_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, uint16_t size); | |
| extern void CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | |
| uint16_t size); |
| typedef enum { | ||
| ENDIAN_TYPE_BIG, | ||
| ENDIAN_TYPE_LITTLE, | ||
| ENDIAN_TYPE_UNKNOWN | ||
| } ENDIAN_TYPE; | ||
|
|
||
| #define CDS_STREAM_MAX (3) | ||
| #define CDS_HAL_RX_BUFFER_SIZE (256) | ||
|
|
||
| typedef struct | ||
| { | ||
| uint8_t* buffer; | ||
| uint16_t capacity; | ||
| uint16_t size; | ||
| uint16_t pos_of_frame_head_candidate; | ||
| uint16_t confirmed_frame_len; | ||
| uint8_t is_frame_fixed; | ||
| uint16_t pos_of_last_rec; | ||
| } CDS_StreamRecBuffer; | ||
|
|
||
| typedef enum | ||
| { | ||
| CDS_ERR_CODE_OK = 0, | ||
| CDS_ERR_CODE_ERR = 1 | ||
| } CDS_ERR_CODE; | ||
|
|
||
| // 関数プロトタイプ | ||
| CDS_ERR_CODE CDS_init_stream_rec_buffer(CDS_StreamRecBuffer* stream_rec_buffer, | ||
| uint8_t* buffer, | ||
| const uint16_t buffer_capacity); | ||
| void CDS_clear_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer); |
There was a problem hiding this comment.
The type definitions and constants (lines 14-44) are duplicated from the production code rather than included from the actual header files. This duplication can lead to:
- Type mismatches if the production types change
- Maintenance burden keeping definitions in sync
- Potential for subtle bugs if sizes or layouts differ
These definitions should be included from the actual header files (e.g., driver_super.h) to ensure consistency with the production code.
| // 型定義 | ||
| // ================================================================ | ||
|
|
||
| typedef enum { | ||
| ENDIAN_TYPE_BIG, | ||
| ENDIAN_TYPE_LITTLE, | ||
| ENDIAN_TYPE_UNKNOWN | ||
| } ENDIAN_TYPE; | ||
|
|
||
| #define CDS_STREAM_MAX (3) | ||
| #define CDS_HAL_RX_BUFFER_SIZE (256) | ||
|
|
||
| typedef struct | ||
| { | ||
| uint8_t* buffer; | ||
| uint16_t capacity; | ||
| uint16_t size; | ||
| uint16_t pos_of_frame_head_candidate; | ||
| uint16_t confirmed_frame_len; | ||
| uint8_t is_frame_fixed; | ||
| uint16_t pos_of_last_rec; | ||
| } CDS_StreamRecBuffer; | ||
|
|
||
| typedef enum | ||
| { | ||
| CDS_ERR_CODE_OK = 0, | ||
| CDS_ERR_CODE_ERR = 1 | ||
| } CDS_ERR_CODE; | ||
|
|
||
| typedef enum | ||
| { | ||
| CDS_STREAM_REC_STATUS_FINDING_HEADER, | ||
| CDS_STREAM_REC_STATUS_RECEIVING_HEADER, | ||
| CDS_STREAM_REC_STATUS_RECEIVING_FRAMELENGTH, | ||
| CDS_STREAM_REC_STATUS_RECEIVING_DATA, | ||
| CDS_STREAM_REC_STATUS_RECEIVING_FOOTER, | ||
| CDS_STREAM_REC_STATUS_FIXED_FRAME, | ||
| CDS_STREAM_REC_STATUS_DISABLE, | ||
| CDS_STREAM_REC_STATUS_HEADER_MISMATCH, | ||
| CDS_STREAM_REC_STATUS_FOOTER_MISMATCH, | ||
| CDS_STREAM_REC_STATUS_RX_FRAME_TOO_LONG, | ||
| CDS_STREAM_REC_STATUS_RX_FRAME_TOO_SHORT, | ||
| CDS_STREAM_REC_STATUS_RX_ERR, | ||
| CDS_STREAM_REC_STATUS_VALIDATE_ERR, | ||
| CDS_STREAM_REC_STATUS_OTHER_ERR | ||
| } CDS_STREAM_REC_STATUS_CODE; | ||
|
|
||
| typedef struct | ||
| { | ||
| CDS_STREAM_REC_STATUS_CODE status_code; | ||
| uint16_t fixed_frame_len; | ||
| uint8_t tlm_disruption_status; | ||
| uint32_t count_of_carry_over_failures; | ||
| } CDS_StreamRecStatus; | ||
|
|
||
| // CDS_StreamConfig の簡易版(テストに必要な部分のみ) | ||
| typedef struct CDS_StreamConfig { | ||
| struct { | ||
| uint8_t is_enabled_; | ||
| uint8_t is_strict_frame_search_; | ||
| CDS_StreamRecBuffer* rx_buffer_; | ||
| const uint8_t* rx_header_; | ||
| uint16_t rx_header_size_; | ||
| const uint8_t* rx_footer_; | ||
| uint16_t rx_footer_size_; | ||
| int16_t rx_frame_size_; | ||
| uint16_t max_rx_frame_size_; | ||
| int16_t rx_framelength_pos_; | ||
| uint16_t rx_framelength_type_size_; | ||
| uint16_t rx_framelength_offset_; | ||
| ENDIAN_TYPE rx_framelength_endian_; | ||
| } settings; | ||
|
|
||
| struct { | ||
| CDS_StreamRecStatus rec_status_; | ||
| } info; | ||
| } CDS_StreamConfig; | ||
|
|
There was a problem hiding this comment.
The type definitions (lines 26-99) are duplicated from production code rather than included from actual header files. This creates maintenance issues:
- Type definitions can drift from the production implementation
- Changes to production types won't be caught by tests
- Potential for struct layout mismatches
These definitions should be included from the actual header files (e.g., driver_super.h) to ensure type consistency and avoid duplication.
| // 型定義 | |
| // ================================================================ | |
| typedef enum { | |
| ENDIAN_TYPE_BIG, | |
| ENDIAN_TYPE_LITTLE, | |
| ENDIAN_TYPE_UNKNOWN | |
| } ENDIAN_TYPE; | |
| #define CDS_STREAM_MAX (3) | |
| #define CDS_HAL_RX_BUFFER_SIZE (256) | |
| typedef struct | |
| { | |
| uint8_t* buffer; | |
| uint16_t capacity; | |
| uint16_t size; | |
| uint16_t pos_of_frame_head_candidate; | |
| uint16_t confirmed_frame_len; | |
| uint8_t is_frame_fixed; | |
| uint16_t pos_of_last_rec; | |
| } CDS_StreamRecBuffer; | |
| typedef enum | |
| { | |
| CDS_ERR_CODE_OK = 0, | |
| CDS_ERR_CODE_ERR = 1 | |
| } CDS_ERR_CODE; | |
| typedef enum | |
| { | |
| CDS_STREAM_REC_STATUS_FINDING_HEADER, | |
| CDS_STREAM_REC_STATUS_RECEIVING_HEADER, | |
| CDS_STREAM_REC_STATUS_RECEIVING_FRAMELENGTH, | |
| CDS_STREAM_REC_STATUS_RECEIVING_DATA, | |
| CDS_STREAM_REC_STATUS_RECEIVING_FOOTER, | |
| CDS_STREAM_REC_STATUS_FIXED_FRAME, | |
| CDS_STREAM_REC_STATUS_DISABLE, | |
| CDS_STREAM_REC_STATUS_HEADER_MISMATCH, | |
| CDS_STREAM_REC_STATUS_FOOTER_MISMATCH, | |
| CDS_STREAM_REC_STATUS_RX_FRAME_TOO_LONG, | |
| CDS_STREAM_REC_STATUS_RX_FRAME_TOO_SHORT, | |
| CDS_STREAM_REC_STATUS_RX_ERR, | |
| CDS_STREAM_REC_STATUS_VALIDATE_ERR, | |
| CDS_STREAM_REC_STATUS_OTHER_ERR | |
| } CDS_STREAM_REC_STATUS_CODE; | |
| typedef struct | |
| { | |
| CDS_STREAM_REC_STATUS_CODE status_code; | |
| uint16_t fixed_frame_len; | |
| uint8_t tlm_disruption_status; | |
| uint32_t count_of_carry_over_failures; | |
| } CDS_StreamRecStatus; | |
| // CDS_StreamConfig の簡易版(テストに必要な部分のみ) | |
| typedef struct CDS_StreamConfig { | |
| struct { | |
| uint8_t is_enabled_; | |
| uint8_t is_strict_frame_search_; | |
| CDS_StreamRecBuffer* rx_buffer_; | |
| const uint8_t* rx_header_; | |
| uint16_t rx_header_size_; | |
| const uint8_t* rx_footer_; | |
| uint16_t rx_footer_size_; | |
| int16_t rx_frame_size_; | |
| uint16_t max_rx_frame_size_; | |
| int16_t rx_framelength_pos_; | |
| uint16_t rx_framelength_type_size_; | |
| uint16_t rx_framelength_offset_; | |
| ENDIAN_TYPE rx_framelength_endian_; | |
| } settings; | |
| struct { | |
| CDS_StreamRecStatus rec_status_; | |
| } info; | |
| } CDS_StreamConfig; | |
| // 型定義: 本番コードのヘッダからインクルードして重複を回避 | |
| // ================================================================ | |
| #include "driver_super.h" |
| // "Test" サフィックスを除去 | ||
| let suite = suite.strip_suffix("Test").unwrap_or(suite); | ||
| format!("test_{}", camel_to_snake(suite)) | ||
| } else { |
There was a problem hiding this comment.
The test suite name parsing logic (lines 88-92) assumes test names follow the "SuiteTest.TestCase" pattern and strips the "Test" suffix. However, this logic will fail or produce incorrect names if:
- The suite name doesn't end with "Test" (returns the original suite name)
- The test name doesn't contain a dot (defaults to "test")
- The suite name contains "Test" in the middle (e.g., "TestingFramework" becomes "ingFramework")
Consider adding validation or logging when the expected pattern isn't matched, and handle edge cases more robustly.
| // "Test" サフィックスを除去 | |
| let suite = suite.strip_suffix("Test").unwrap_or(suite); | |
| format!("test_{}", camel_to_snake(suite)) | |
| } else { | |
| // "Test" サフィックスを除去(末尾に "Test" がない場合はそのまま利用) | |
| let (suite_without_suffix, had_suffix) = if let Some(stripped) = suite.strip_suffix("Test") { | |
| (stripped, true) | |
| } else { | |
| (suite, false) | |
| }; | |
| if !had_suffix { | |
| eprintln!( | |
| "Warning: expected test suite name to end with 'Test', but got '{suite}' in test '{test_name}'. Using full suite name for binary inference." | |
| ); | |
| } | |
| format!("test_{}", camel_to_snake(suite_without_suffix)) | |
| } else { | |
| eprintln!( | |
| "Warning: test name '{test_name}' does not contain a '.', defaulting test binary name to 'test'." | |
| ); |
| TEST_F(StreamRecBufferTest, NullPointerSafety) { | ||
| // これらの関数は NULL チェックで早期リターンする | ||
| CDS_clear_stream_rec_buffer_(nullptr); | ||
| CDS_drop_from_stream_rec_buffer_(nullptr, 10); | ||
| CDS_confirm_stream_rec_buffer_(nullptr, 10); | ||
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(nullptr, 10); | ||
| EXPECT_EQ(0, CDS_get_unprocessed_size_from_stream_rec_buffer_(nullptr)); |
There was a problem hiding this comment.
The test case at line 471 tests pushing data with a NULL buffer pointer. While NULL safety is good to test, the test expectation is unclear:
The function signature suggests CDS_push_to_stream_rec_buffer_(&rec_buffer_, nullptr, 10) should fail because the buffer pointer is NULL. However, the test doesn't verify that rec_buffer_ itself is properly initialized before the call.
Consider adding a comment explaining the expected behavior, or split this into separate test cases:
- Push with NULL buffer parameter
- Push with NULL stream_rec_buffer pointer
| TEST_F(StreamRecBufferTest, NullPointerSafety) { | |
| // これらの関数は NULL チェックで早期リターンする | |
| CDS_clear_stream_rec_buffer_(nullptr); | |
| CDS_drop_from_stream_rec_buffer_(nullptr, 10); | |
| CDS_confirm_stream_rec_buffer_(nullptr, 10); | |
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(nullptr, 10); | |
| EXPECT_EQ(0, CDS_get_unprocessed_size_from_stream_rec_buffer_(nullptr)); | |
| TEST_F(StreamRecBufferTest, NullPointerSafety) { | |
| // rec_buffer_ 自体は有効なバッファとして初期化しておく | |
| CDS_init_stream_rec_buffer(&rec_buffer_, buffer_, BUFFER_SIZE); | |
| // これらの関数は NULL チェックで早期リターンする | |
| CDS_clear_stream_rec_buffer_(nullptr); | |
| CDS_drop_from_stream_rec_buffer_(nullptr, 10); | |
| CDS_confirm_stream_rec_buffer_(nullptr, 10); | |
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(nullptr, 10); | |
| EXPECT_EQ(0, CDS_get_unprocessed_size_from_stream_rec_buffer_(nullptr)); | |
| // NULL の stream_rec_buffer* および NULL のデータバッファはいずれもエラーを返すことを確認 |
| // ================================================================ | ||
| // StreamRecBuffer 関数(driver_super.c から抽出) | ||
| // ================================================================ | ||
|
|
||
| CDS_ERR_CODE CDS_init_stream_rec_buffer(CDS_StreamRecBuffer* stream_rec_buffer, | ||
| uint8_t* buffer, | ||
| const uint16_t buffer_capacity) | ||
| { | ||
| if (stream_rec_buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (buffer_capacity == 0) return CDS_ERR_CODE_ERR; | ||
|
|
||
| stream_rec_buffer->buffer = buffer; | ||
| stream_rec_buffer->capacity = buffer_capacity; | ||
| stream_rec_buffer->size = 0; | ||
| stream_rec_buffer->pos_of_frame_head_candidate = 0; | ||
| stream_rec_buffer->confirmed_frame_len = 0; | ||
| stream_rec_buffer->is_frame_fixed = 0; | ||
| stream_rec_buffer->pos_of_last_rec = 0; | ||
| memset(buffer, 0x00, buffer_capacity); | ||
| return CDS_ERR_CODE_OK; | ||
| } | ||
|
|
||
| void CDS_clear_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
|
|
||
| stream_rec_buffer->size = 0; | ||
| stream_rec_buffer->pos_of_frame_head_candidate = 0; | ||
| stream_rec_buffer->confirmed_frame_len = 0; | ||
| stream_rec_buffer->is_frame_fixed = 0; | ||
| stream_rec_buffer->pos_of_last_rec = 0; | ||
|
|
||
| if (stream_rec_buffer->buffer != NULL) | ||
| { | ||
| memset(stream_rec_buffer->buffer, 0x00, stream_rec_buffer->capacity); | ||
| } | ||
| } | ||
|
|
||
| void CDS_drop_from_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
| if (size == 0) return; | ||
|
|
||
| if (size >= stream_rec_buffer->size) | ||
| { | ||
| CDS_clear_stream_rec_buffer_(stream_rec_buffer); | ||
| return; | ||
| } | ||
|
|
||
| uint16_t remaining = stream_rec_buffer->size - size; | ||
| memmove(stream_rec_buffer->buffer, stream_rec_buffer->buffer + size, remaining); | ||
| stream_rec_buffer->size = remaining; | ||
|
|
||
| if (stream_rec_buffer->pos_of_frame_head_candidate >= size) | ||
| stream_rec_buffer->pos_of_frame_head_candidate -= size; | ||
| else | ||
| stream_rec_buffer->pos_of_frame_head_candidate = 0; | ||
|
|
||
| if (stream_rec_buffer->pos_of_last_rec >= size) | ||
| stream_rec_buffer->pos_of_last_rec -= size; | ||
| else | ||
| stream_rec_buffer->pos_of_last_rec = 0; | ||
| } | ||
|
|
||
| CDS_ERR_CODE CDS_push_to_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | ||
| const uint8_t* buffer, uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (buffer == NULL) return CDS_ERR_CODE_ERR; | ||
| if (size == 0) return CDS_ERR_CODE_OK; | ||
|
|
||
| if (stream_rec_buffer->size + size > stream_rec_buffer->capacity) | ||
| return CDS_ERR_CODE_ERR; | ||
|
|
||
| memcpy(stream_rec_buffer->buffer + stream_rec_buffer->size, buffer, size); | ||
| stream_rec_buffer->size += size; | ||
| return CDS_ERR_CODE_OK; | ||
| } | ||
|
|
||
| uint16_t CDS_get_unprocessed_size_from_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer) | ||
| { | ||
| if (stream_rec_buffer == NULL) return 0; | ||
|
|
||
| uint16_t processed = stream_rec_buffer->pos_of_frame_head_candidate + | ||
| stream_rec_buffer->confirmed_frame_len; | ||
| if (processed >= stream_rec_buffer->size) return 0; | ||
| return stream_rec_buffer->size - processed; | ||
| } | ||
|
|
||
| void CDS_confirm_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
| stream_rec_buffer->confirmed_frame_len = size; | ||
| } | ||
|
|
||
| void CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(CDS_StreamRecBuffer* stream_rec_buffer, | ||
| uint16_t size) | ||
| { | ||
| if (stream_rec_buffer == NULL) return; | ||
| stream_rec_buffer->pos_of_frame_head_candidate += size; | ||
| stream_rec_buffer->confirmed_frame_len = 0; | ||
|
|
||
| if (stream_rec_buffer->pos_of_frame_head_candidate > stream_rec_buffer->size) | ||
| stream_rec_buffer->pos_of_frame_head_candidate = stream_rec_buffer->size; | ||
| } | ||
|
|
||
| // ================================================================ | ||
| // フレーム解析関数(driver_super.c から抽出) | ||
| // ================================================================ | ||
|
|
||
| // ヘッダ探索: ヘッダの先頭バイトを検索 | ||
| static void CDS_analyze_rx_buffer_finding_header_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
| const uint16_t unprocessed_data_len = CDS_get_unprocessed_size_from_stream_rec_buffer_(buffer); | ||
| uint8_t* p_header; | ||
| uint16_t found_header_offset; | ||
|
|
||
| if (p_stream_config->settings.rx_header_ == NULL) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_OTHER_ERR; | ||
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(buffer, unprocessed_data_len); | ||
| return; | ||
| } | ||
|
|
||
| p_header = (uint8_t*)memchr(&buffer->buffer[buffer->pos_of_frame_head_candidate], | ||
| (int)(p->settings.rx_header_[0]), | ||
| (size_t)unprocessed_data_len); | ||
|
|
||
| if (p_header == NULL) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FINDING_HEADER; | ||
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(buffer, unprocessed_data_len); | ||
| return; | ||
| } | ||
|
|
||
| found_header_offset = (uint16_t)(p_header - &buffer->buffer[buffer->pos_of_frame_head_candidate]); | ||
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(buffer, found_header_offset); | ||
| CDS_confirm_stream_rec_buffer_(buffer, 1); | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_HEADER; | ||
| return; | ||
| } | ||
|
|
||
| // ヘッダ受信中: 1 バイトずつヘッダを検証 | ||
| static void CDS_analyze_rx_buffer_receiving_header_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
| const uint16_t buffer_offset = buffer->pos_of_frame_head_candidate + buffer->confirmed_frame_len; | ||
|
|
||
| if (buffer->buffer[buffer_offset] == p->settings.rx_header_[buffer->confirmed_frame_len]) | ||
| { | ||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + 1); | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_HEADER; | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_HEADER_MISMATCH; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // フッタ受信中: 1 バイトずつフッタを検証 | ||
| static void CDS_analyze_rx_buffer_receiving_footer_(CDS_StreamConfig* p_stream_config, | ||
| uint16_t rx_frame_size) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
| const uint16_t buffer_offset = buffer->pos_of_frame_head_candidate + buffer->confirmed_frame_len; | ||
| uint16_t rec_footer_pos; | ||
|
|
||
| if (p->settings.rx_footer_size_ == 0) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FIXED_FRAME; | ||
| return; | ||
| } | ||
|
|
||
| rec_footer_pos = (uint16_t)(buffer->confirmed_frame_len - (rx_frame_size - p->settings.rx_footer_size_)); | ||
|
|
||
| if (buffer->buffer[buffer_offset] != p->settings.rx_footer_[rec_footer_pos]) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FOOTER_MISMATCH; | ||
| return; | ||
| } | ||
|
|
||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + 1); | ||
|
|
||
| if (buffer->confirmed_frame_len == rx_frame_size) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FIXED_FRAME; | ||
| } | ||
| else | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_FOOTER; | ||
| } | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // フレーム長データ取得(可変長フレーム用) | ||
| static uint32_t CDS_analyze_rx_buffer_get_framelength_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| uint32_t len = 0; | ||
| uint8_t i; | ||
| const uint16_t pos = p->settings.rx_framelength_pos_ + p->settings.rx_buffer_->pos_of_frame_head_candidate; | ||
| const uint16_t size = p->settings.rx_framelength_type_size_; | ||
|
|
||
| if (p->settings.rx_framelength_endian_ == ENDIAN_TYPE_BIG) | ||
| { | ||
| for (i = 0; i < size; ++i) | ||
| { | ||
| if (i == 0) | ||
| { | ||
| len = p->settings.rx_buffer_->buffer[pos]; | ||
| } | ||
| else | ||
| { | ||
| len <<= 8; | ||
| len |= p->settings.rx_buffer_->buffer[pos + i]; | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| for (i = 0; i < size; ++i) | ||
| { | ||
| if (i == 0) | ||
| { | ||
| len = p->settings.rx_buffer_->buffer[pos + size - 1]; | ||
| } | ||
| else | ||
| { | ||
| len <<= 8; | ||
| len |= p->settings.rx_buffer_->buffer[pos + size - 1 - i]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| len += p->settings.rx_framelength_offset_; | ||
| return len; | ||
| } | ||
|
|
||
| // 固定長フレーム解析 | ||
| static void CDS_analyze_rx_buffer_fixed_pickup_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
|
|
||
| if (buffer->confirmed_frame_len == 0 && p->settings.rx_header_size_ != 0) | ||
| { | ||
| CDS_analyze_rx_buffer_finding_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < p->settings.rx_header_size_) | ||
| { | ||
| CDS_analyze_rx_buffer_receiving_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < p->settings.rx_frame_size_ - p->settings.rx_footer_size_) | ||
| { | ||
| const uint16_t unprocessed_data_len = CDS_get_unprocessed_size_from_stream_rec_buffer_(buffer); | ||
| uint16_t pickup_data_len = (uint16_t)(p->settings.rx_frame_size_ - p->settings.rx_footer_size_ - buffer->confirmed_frame_len); | ||
|
|
||
| if (pickup_data_len > unprocessed_data_len) | ||
| { | ||
| pickup_data_len = unprocessed_data_len; | ||
| } | ||
|
|
||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + pickup_data_len); | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_DATA; | ||
|
|
||
| if (p->settings.rx_footer_size_ == 0 && buffer->confirmed_frame_len == p->settings.rx_frame_size_) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FIXED_FRAME; | ||
| } | ||
|
|
||
| return; | ||
| } | ||
| else | ||
| { | ||
| CDS_analyze_rx_buffer_receiving_footer_(p_stream_config, | ||
| (uint16_t)(p->settings.rx_frame_size_)); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // 可変長フレーム解析(フレーム長データ付き) | ||
| static void CDS_analyze_rx_buffer_variable_pickup_with_rx_frame_size_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
| const uint16_t unprocessed_data_len = CDS_get_unprocessed_size_from_stream_rec_buffer_(buffer); | ||
| uint32_t rx_frame_size = CDS_analyze_rx_buffer_get_framelength_(p_stream_config); | ||
|
|
||
| if (buffer->confirmed_frame_len == 0 && p->settings.rx_header_size_ != 0) | ||
| { | ||
| CDS_analyze_rx_buffer_finding_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < p->settings.rx_header_size_) | ||
| { | ||
| CDS_analyze_rx_buffer_receiving_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < p->settings.rx_framelength_pos_ + p->settings.rx_framelength_type_size_) | ||
| { | ||
| uint16_t pickup_data_len = (uint16_t)(p->settings.rx_framelength_pos_ + p->settings.rx_framelength_type_size_ - buffer->confirmed_frame_len); | ||
|
|
||
| if (pickup_data_len > unprocessed_data_len) | ||
| { | ||
| pickup_data_len = unprocessed_data_len; | ||
| } | ||
|
|
||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + pickup_data_len); | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_FRAMELENGTH; | ||
|
|
||
| if (buffer->confirmed_frame_len >= p->settings.rx_framelength_pos_ + p->settings.rx_framelength_type_size_) | ||
| { | ||
| rx_frame_size = CDS_analyze_rx_buffer_get_framelength_(p_stream_config); | ||
|
|
||
| if (rx_frame_size > buffer->capacity || rx_frame_size > p->settings.max_rx_frame_size_) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RX_FRAME_TOO_LONG; | ||
| return; | ||
| } | ||
|
|
||
| if (rx_frame_size < p->settings.rx_header_size_ + p->settings.rx_footer_size_) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RX_FRAME_TOO_SHORT; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < rx_frame_size - p->settings.rx_footer_size_) | ||
| { | ||
| uint16_t pickup_data_len = (uint16_t)(rx_frame_size - p->settings.rx_footer_size_ - buffer->confirmed_frame_len); | ||
|
|
||
| if (pickup_data_len > unprocessed_data_len) | ||
| { | ||
| pickup_data_len = unprocessed_data_len; | ||
| } | ||
|
|
||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + pickup_data_len); | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_DATA; | ||
|
|
||
| if (p->settings.rx_footer_size_ == 0 && buffer->confirmed_frame_len == rx_frame_size) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FIXED_FRAME; | ||
| } | ||
|
|
||
| return; | ||
| } | ||
| else | ||
| { | ||
| CDS_analyze_rx_buffer_receiving_footer_(p_stream_config, | ||
| (uint16_t)rx_frame_size); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // 可変長フレーム解析(フッタ終端) | ||
| static void CDS_analyze_rx_buffer_variable_pickup_with_footer_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamConfig* p = p_stream_config; | ||
| CDS_StreamRecBuffer* buffer = p->settings.rx_buffer_; | ||
|
|
||
| if (buffer->confirmed_frame_len == 0 && p->settings.rx_header_size_ != 0) | ||
| { | ||
| CDS_analyze_rx_buffer_finding_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else if (buffer->confirmed_frame_len < p->settings.rx_header_size_) | ||
| { | ||
| CDS_analyze_rx_buffer_receiving_header_(p_stream_config); | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| const uint16_t unprocessed_data_len = CDS_get_unprocessed_size_from_stream_rec_buffer_(buffer); | ||
| uint8_t* p_footer_last; | ||
| int32_t body_data_len; | ||
| uint16_t processed_data_len; | ||
| uint16_t i; | ||
| const uint16_t memchr_offset = buffer->pos_of_frame_head_candidate + buffer->confirmed_frame_len; | ||
| uint16_t estimated_rx_frame_end_pos; | ||
|
|
||
| p_footer_last = (uint8_t*)memchr(&(buffer->buffer[memchr_offset]), | ||
| (int)(p->settings.rx_footer_[p->settings.rx_footer_size_ - 1]), | ||
| (size_t)unprocessed_data_len); | ||
|
|
||
| if (p_footer_last == NULL) | ||
| { | ||
| processed_data_len = unprocessed_data_len; | ||
| } | ||
| else | ||
| { | ||
| processed_data_len = (uint16_t)(p_footer_last - &(buffer->buffer[memchr_offset]) + 1); | ||
| } | ||
|
|
||
| CDS_confirm_stream_rec_buffer_(buffer, buffer->confirmed_frame_len + processed_data_len); | ||
| if (buffer->confirmed_frame_len > p->settings.max_rx_frame_size_) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RX_FRAME_TOO_LONG; | ||
| return; | ||
| } | ||
|
|
||
| if (p_footer_last == NULL) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_DATA; | ||
| return; | ||
| } | ||
|
|
||
| body_data_len = buffer->confirmed_frame_len - p->settings.rx_header_size_ - p->settings.rx_footer_size_; | ||
| if (body_data_len < 0) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_DATA; | ||
| return; | ||
| } | ||
|
|
||
| estimated_rx_frame_end_pos = buffer->pos_of_frame_head_candidate + buffer->confirmed_frame_len; | ||
| for (i = 0; i < p->settings.rx_footer_size_; i++) | ||
| { | ||
| if (buffer->buffer[estimated_rx_frame_end_pos - i - 1] != p->settings.rx_footer_[p->settings.rx_footer_size_ - i - 1]) | ||
| { | ||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_RECEIVING_DATA; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| p->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FIXED_FRAME; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // メイン pickup 関数 | ||
| static void CDS_analyze_rx_buffer_pickup_(CDS_StreamConfig* p_stream_config) | ||
| { | ||
| CDS_StreamRecBuffer* buffer = p_stream_config->settings.rx_buffer_; | ||
| void (*pickup_func)(CDS_StreamConfig* p_stream_config); | ||
|
|
||
| if (p_stream_config->settings.rx_frame_size_ == 0) return; | ||
|
|
||
| if (p_stream_config->settings.rx_frame_size_ > 0) | ||
| { | ||
| pickup_func = CDS_analyze_rx_buffer_fixed_pickup_; | ||
| } | ||
| else if (p_stream_config->settings.rx_frame_size_ < 0) | ||
| { | ||
| if (p_stream_config->settings.rx_framelength_pos_ >= 0) | ||
| { | ||
| pickup_func = CDS_analyze_rx_buffer_variable_pickup_with_rx_frame_size_; | ||
| } | ||
| else | ||
| { | ||
| pickup_func = CDS_analyze_rx_buffer_variable_pickup_with_footer_; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| pickup_func = NULL; | ||
| return; | ||
| } | ||
|
|
||
| while (CDS_get_unprocessed_size_from_stream_rec_buffer_(buffer) > 0) | ||
| { | ||
| pickup_func(p_stream_config); | ||
|
|
||
| if (p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_FIXED_FRAME) | ||
| { | ||
| break; | ||
| } | ||
|
|
||
| if (p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_HEADER_MISMATCH || | ||
| p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_FOOTER_MISMATCH || | ||
| p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_RX_FRAME_TOO_LONG || | ||
| p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_RX_FRAME_TOO_SHORT) | ||
| { | ||
| CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_(buffer, 1); | ||
| p_stream_config->info.rec_status_.status_code = CDS_STREAM_REC_STATUS_FINDING_HEADER; | ||
| } | ||
| } | ||
|
|
||
| if (p_stream_config->info.rec_status_.status_code == CDS_STREAM_REC_STATUS_FIXED_FRAME) | ||
| { | ||
| buffer->is_frame_fixed = 1; | ||
| p_stream_config->info.rec_status_.fixed_frame_len = buffer->confirmed_frame_len; | ||
| } | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| } // extern "C" |
There was a problem hiding this comment.
The test file embeds the production implementation of frame analysis functions directly (lines 105-596). This has the same critical issues as in test_stream_rec_buffer.cpp:
- Tests are not validating the actual production code
- Code duplication creates maintenance burden
- Implementation drift between test copy and production code
- No guarantee the embedded code matches the actual implementation
The CMakeLists.txt should be updated to compile and link the actual driver_super.c source file instead of duplicating the implementation in test files.
| for (i, c) in s.chars().enumerate() { | ||
| if c.is_uppercase() { | ||
| if i > 0 { | ||
| result.push('_'); | ||
| } | ||
| result.push(c.to_lowercase().next().unwrap()); | ||
| } else { | ||
| result.push(c); | ||
| } | ||
| } |
There was a problem hiding this comment.
The camel_to_snake conversion function has a potential issue: it doesn't handle consecutive uppercase letters correctly. For example, "HTTPServer" would become "h_t_t_p_server" instead of the more idiomatic "http_server".
Consider using a more robust conversion that handles acronyms properly, such as:
- Only insert underscore if the previous character was lowercase
- Or use a well-tested conversion library
| for (i, c) in s.chars().enumerate() { | |
| if c.is_uppercase() { | |
| if i > 0 { | |
| result.push('_'); | |
| } | |
| result.push(c.to_lowercase().next().unwrap()); | |
| } else { | |
| result.push(c); | |
| } | |
| } | |
| let chars: Vec<char> = s.chars().collect(); | |
| for (i, &c) in chars.iter().enumerate() { | |
| if c.is_uppercase() { | |
| if i > 0 { | |
| let prev = chars[i - 1]; | |
| let next_is_lowercase = chars | |
| .get(i + 1) | |
| .map(|n| n.is_lowercase()) | |
| .unwrap_or(false); | |
| if prev.is_lowercase() || next_is_lowercase { | |
| result.push('_'); | |
| } | |
| } | |
| for lc in c.to_lowercase() { | |
| result.push(lc); | |
| } | |
| } else { | |
| result.push(c); | |
| } | |
| } |
| let configure_status = Command::new("cmake") | ||
| .args([tests_dir.to_str().unwrap(), "-DCMAKE_BUILD_TYPE=Debug"]) | ||
| .current_dir(&build_dir) | ||
| .status() | ||
| .expect("cmake configure failed"); | ||
|
|
||
| if !configure_status.success() { | ||
| panic!("cmake configure failed with status: {}", configure_status); | ||
| } | ||
|
|
||
| // CMake build | ||
| let build_status = Command::new("cmake") | ||
| .args(["--build", ".", "-j"]) | ||
| .current_dir(&build_dir) | ||
| .status() | ||
| .expect("cmake build failed"); | ||
|
|
||
| if !build_status.success() { | ||
| panic!("cmake build failed with status: {}", build_status); |
There was a problem hiding this comment.
The panic messages on build failures (lines 38, 49) don't include the actual error output from cmake/cmake --build. This makes debugging build failures difficult.
Consider capturing and displaying stderr in the panic message to provide more context when builds fail:
.output().expect("cmake configure failed");
if !result.status.success() {
eprintln!("{}", String::from_utf8_lossy(&result.stderr));
panic!("cmake configure failed");
}| let configure_status = Command::new("cmake") | |
| .args([tests_dir.to_str().unwrap(), "-DCMAKE_BUILD_TYPE=Debug"]) | |
| .current_dir(&build_dir) | |
| .status() | |
| .expect("cmake configure failed"); | |
| if !configure_status.success() { | |
| panic!("cmake configure failed with status: {}", configure_status); | |
| } | |
| // CMake build | |
| let build_status = Command::new("cmake") | |
| .args(["--build", ".", "-j"]) | |
| .current_dir(&build_dir) | |
| .status() | |
| .expect("cmake build failed"); | |
| if !build_status.success() { | |
| panic!("cmake build failed with status: {}", build_status); | |
| let configure_output = Command::new("cmake") | |
| .args([tests_dir.to_str().unwrap(), "-DCMAKE_BUILD_TYPE=Debug"]) | |
| .current_dir(&build_dir) | |
| .output() | |
| .expect("failed to run cmake configure"); | |
| if !configure_output.status.success() { | |
| let stderr = String::from_utf8_lossy(&configure_output.stderr); | |
| panic!( | |
| "cmake configure failed with status: {}. Stderr:\n{}", | |
| configure_output.status, stderr | |
| ); | |
| } | |
| // CMake build | |
| let build_output = Command::new("cmake") | |
| .args(["--build", ".", "-j"]) | |
| .current_dir(&build_dir) | |
| .output() | |
| .expect("failed to run cmake build"); | |
| if !build_output.status.success() { | |
| let stderr = String::from_utf8_lossy(&build_output.stderr); | |
| panic!( | |
| "cmake build failed with status: {}. Stderr:\n{}", | |
| build_output.status, stderr | |
| ); |
| target_include_directories(test_stream_rec_buffer PRIVATE | ||
| ${CMAKE_CURRENT_SOURCE_DIR} | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/.. | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../.. | ||
| ) | ||
|
|
||
| target_link_libraries(test_stream_rec_buffer | ||
| GTest::gtest_main | ||
| ) | ||
|
|
||
| include(GoogleTest) | ||
| gtest_discover_tests(test_stream_rec_buffer) | ||
|
|
||
| # FrameAnalysis のテスト | ||
| add_executable(test_frame_analysis | ||
| test_frame_analysis.cpp | ||
| mocks/mock_hal_handler_registry.c | ||
| mocks/mock_time_manager.c | ||
| ) | ||
|
|
||
| target_include_directories(test_frame_analysis PRIVATE | ||
| ${CMAKE_CURRENT_SOURCE_DIR} | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/.. | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../.. | ||
| ) |
There was a problem hiding this comment.
The include directories configuration (lines 25-29, 45-49) uses relative paths with ".." which makes the build structure fragile. If the directory layout changes, these paths will break.
Consider using CMake variables to make the paths more robust:
set(C2A_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..)
target_include_directories(test_stream_rec_buffer PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
${C2A_ROOT_DIR}
)This makes the intent clearer and easier to maintain.
| let output = std::process::Command::new(&binary) | ||
| .arg("--gtest_filter={test_name}") | ||
| .output() | ||
| .expect("failed to run test binary"); | ||
|
|
||
| if !output.status.success() {{ | ||
| eprintln!("{{}}", String::from_utf8_lossy(&output.stdout)); | ||
| eprintln!("{{}}", String::from_utf8_lossy(&output.stderr)); | ||
| panic!("C++ test {test_name} failed"); | ||
| }} |
There was a problem hiding this comment.
The generated test wrapper doesn't pass through test output in real-time. The output is only shown if the test fails (lines 107-109). This means:
- Users don't see test progress during long-running tests
- Debugging hangs or timeouts is difficult
- The test output format doesn't match typical cargo test output
Consider using .spawn() and streaming output instead of .output(), or add an option to show output for all tests during development.
| let output = std::process::Command::new(&binary) | |
| .arg("--gtest_filter={test_name}") | |
| .output() | |
| .expect("failed to run test binary"); | |
| if !output.status.success() {{ | |
| eprintln!("{{}}", String::from_utf8_lossy(&output.stdout)); | |
| eprintln!("{{}}", String::from_utf8_lossy(&output.stderr)); | |
| panic!("C++ test {test_name} failed"); | |
| }} | |
| let status = std::process::Command::new(&binary) | |
| .arg("--gtest_filter={test_name}") | |
| .stdout(std::process::Stdio::inherit()) | |
| .stderr(std::process::Stdio::inherit()) | |
| .status() | |
| .expect("failed to run test binary"); | |
| assert!(status.success(), "C++ test {test_name} failed"); |
Add unit test infrastructure and tests for CDS_StreamRecBuffer functions: - CDS_init_stream_rec_buffer() - CDS_clear_stream_rec_buffer_() - CDS_push_to_stream_rec_buffer_() - CDS_drop_from_stream_rec_buffer_() - CDS_get_unprocessed_size_from_stream_rec_buffer_() - CDS_confirm_stream_rec_buffer_() - CDS_move_forward_frame_head_candidate_of_stream_rec_buffer_() Tests cover: - Buffer initialization and error handling - Push/drop operations - Frame confirmation flow - Multiple frame processing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create c2a-core-component-driver crate as wrapper for C++ GoogleTest tests - build.rs builds C++ tests with CMake and generates Rust test wrappers - Each C++ test case is exposed as an individual Rust #[test] - Run with `cargo test -p c2a-core-component-driver` Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Japanese comments explaining what each test case verifies. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive edge case tests covering: - Buffer boundary conditions (exact capacity, full buffer) - pos_of_frame_head_candidate adjustment after drop - pos_of_last_rec adjustment after drop - Reset behavior when drop size exceeds positions - Empty buffer and zero-size operations - move_forward clamping and confirmed_frame_len reset - Sequential push/drop combinations - NULL pointer safety - Unprocessed size calculation edge cases Test count: 13 -> 28 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for: - memmove data integrity verification - Repeated drop/push buffer reuse pattern - Multiple frames in buffer processing - Fragmented frame reception simulation - Boundary condition (capacity-1 then +1) - Frame head candidate with confirmed length interaction - clear() complete reset verification - Large data processing (200 bytes) Test count: 28 -> 36 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add 10 more test cases covering: - MinimalBufferSize: 1-byte buffer edge case - SingleBytePushes: 100 consecutive single byte operations - SingleByteDrops: Sequential single byte removal - ContinuousFrameHeadSearch: Header search simulation - PatternSearchSimulation: Pattern matching in buffer - ReInitialization: Re-init with different buffer - InterleavedPushAndConfirm: Concurrent receive/process - ImmediatePushAfterDrop: Memory reuse pattern - ConfirmOverwrite: Frame length re-confirmation - ExactCapacityFrames: No-slack buffer processing Total test count: 46 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive unit tests for CDS_analyze_rx_buffer_* functions: - Fixed-length frames (header/footer combinations) - Variable-length frames with frame length field - Variable-length frames with footer termination - Multiple frame reception - Fragmented reception - Header/footer mismatch recovery - Noise prefix handling - EB90 frame format (C2A standard) - NMEA-like format Total test count: 75 (46 StreamRecBuffer + 29 FrameAnalysis) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add regex custom manager to track GIT_TAG versions in CMake FetchContent_Declare for dependencies like GoogleTest. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rewrite test_frame_analysis.cpp to test through CDS_receive() instead of copied static functions. This approach: - Uses actual driver_super.c implementation via HAL mocks - Tests the complete frame analysis flow end-to-end - Validates both fixed-length and variable-length frame parsing Key changes: - Add driver_super_mock_prelude.h for force-include trick - Extend HAL mock with append_rx_data and set_rx_chunk_size - Add mock headers for library/system/src_user dependencies - Update CMakeLists.txt to apply prelude to test compilation - Simplify test_stream_rec_buffer.cpp declarations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
a242319 to
45bb743
Compare
Summary
cargo test経由で実行できる仕組みを導入変更内容
component_driver/を Rust crate 化し、cargo test --workspaceでテスト実行可能にbuild.rsで CMake ビルド + ctest からテスト一覧を取得し、個別の Rust テストとして公開Test plan
cargo test -p c2a-core-component-driverで 75 tests passcargo test --workspaceが通ることを確認🤖 Generated with Claude Code