-
Notifications
You must be signed in to change notification settings - Fork 4
test: add DriverSuper unit tests #473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d2a7004
689ee07
69093ad
ff59a84
f17f11a
ca6844a
1545a01
3654032
45bb743
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,8 @@ | ||||||||
| [package] | ||||||||
| name = "c2a-core-component-driver" | ||||||||
| description = "C2A Component Driver unit tests" | ||||||||
| version.workspace = true | ||||||||
| edition = "2021" | ||||||||
|
||||||||
| edition = "2021" | |
| edition = "2021" | |
| build = "build.rs" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,130 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::io::Write; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::path::PathBuf; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::process::Command; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// CamelCase を snake_case に変換 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn camel_to_snake(s: &str) -> String { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut result = String::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+17
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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); | |
| } | |
| } |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |
| ); |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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'." | |
| ); |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| //! C2A Component Driver unit tests | ||
| //! | ||
| //! This crate wraps C++ GoogleTest tests for the component_driver module. | ||
| //! Run with `cargo test` to execute all C++ unit tests. | ||
| //! | ||
| //! Each C++ test case is exposed as an individual Rust test. | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| include!(concat!(env!("OUT_DIR"), "/generated_tests.rs")); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| cmake_minimum_required(VERSION 3.14) | ||
|
|
||
| project(component_driver_tests) | ||
|
|
||
| # GoogleTest を FetchContent で取得 | ||
| include(FetchContent) | ||
| FetchContent_Declare( | ||
| googletest | ||
| GIT_REPOSITORY https://github.com/google/googletest.git | ||
| GIT_TAG v1.14.0 | ||
| ) | ||
|
|
||
| set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) | ||
| FetchContent_MakeAvailable(googletest) | ||
|
|
||
| enable_testing() | ||
|
|
||
| set(MOCK_PRELUDE_FLAG "-include ${CMAKE_CURRENT_SOURCE_DIR}/mocks/driver_super_mock_prelude.h") | ||
|
|
||
| # StreamRecBuffer のテスト | ||
| add_executable(test_stream_rec_buffer | ||
| test_stream_rec_buffer.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../driver_super.c | ||
| mocks/mock_hal_handler_registry.c | ||
| mocks/mock_time_manager.c | ||
| ) | ||
|
|
||
| # driver_super.c にモック prelude を適用(test_stream_rec_buffer 用) | ||
| set_source_files_properties( | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../driver_super.c | ||
| TARGET_DIRECTORY test_stream_rec_buffer | ||
| PROPERTIES COMPILE_FLAGS "${MOCK_PRELUDE_FLAG}" | ||
| ) | ||
|
|
||
| target_include_directories(test_stream_rec_buffer PRIVATE | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/mocks # モックが実際のヘッダより優先される | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/mocks/src_user # <src_user/...> インクルード用 | ||
| ${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 | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../driver_super.c | ||
| mocks/mock_hal_handler_registry.c | ||
| mocks/mock_time_manager.c | ||
| ) | ||
|
|
||
| # driver_super.c と test_frame_analysis.cpp にモック prelude を適用 | ||
| set_source_files_properties( | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../driver_super.c | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/test_frame_analysis.cpp | ||
| TARGET_DIRECTORY test_frame_analysis | ||
| PROPERTIES COMPILE_FLAGS "${MOCK_PRELUDE_FLAG}" | ||
| ) | ||
|
|
||
| target_include_directories(test_frame_analysis PRIVATE | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/mocks # モックが実際のヘッダより優先される | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/mocks/src_user # <src_user/...> インクルード用 | ||
| ${CMAKE_CURRENT_SOURCE_DIR} | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/.. | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../.. | ||
| ) | ||
|
Comment on lines
+35
to
+72
|
||
|
|
||
| target_link_libraries(test_frame_analysis | ||
| GTest::gtest_main | ||
| ) | ||
|
|
||
| gtest_discover_tests(test_frame_analysis) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description mentions testing with
cargo test -p c2a-core-component-driver, but the actual package name in Cargo.toml uses underscores in "component_driver" while the package name uses hyphens "component-driver".This inconsistency should be verified - Cargo normalizes package names (hyphens in package names, underscores in code), but it's worth confirming the command in the PR description actually works as stated.