diff --git a/.dockerignore b/.dockerignore index 53f52ff..68810dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -18,4 +18,3 @@ dist/ **/.pytest_cache/ **/__pycache__/ **/*.pyc - diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..62e721b --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,81 @@ +# Pre-commit checks for pull requests +# Ensures all commits follow project conventions + +name: Pre-commit Checks + +on: + pull_request: + branches: [ "main" ] + +jobs: + pre-commit: + name: Pre-commit Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed for commit message validation + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install pre-commit + run: pip install pre-commit + + - name: Run pre-commit on all files + run: pre-commit run --all-files --show-diff-on-failure + + - name: Validate commit messages + run: | + # Get the range of commits in this PR + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + + echo "Checking commits from $BASE_SHA to $HEAD_SHA" + + # Install conventional-pre-commit for validation + pip install conventional-pre-commit + + # Check each commit message in the PR + FAILED=0 + for COMMIT in $(git rev-list $BASE_SHA..$HEAD_SHA); do + MSG=$(git log --format=%B -n 1 $COMMIT) + echo "Checking commit $COMMIT:" + echo "$MSG" + echo "---" + + # Validate using conventional-pre-commit pattern + if ! echo "$MSG" | grep -qE "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .+"; then + echo "❌ Commit message does not follow Conventional Commits format" + echo " Expected: (): " + echo " Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert" + FAILED=1 + else + echo "✅ Valid commit message" + fi + echo "" + done + + if [ $FAILED -eq 1 ]; then + echo "" + echo "============================================" + echo "❌ One or more commit messages are invalid!" + echo "============================================" + echo "" + echo "Please follow the Conventional Commits format:" + echo " (): " + echo "" + echo "Examples:" + echo " feat: add new UDP transport option" + echo " fix(sd): resolve multicast join issue" + echo " docs: update README with build instructions" + echo " chore: update dependencies" + echo "" + exit 1 + fi + + echo "✅ All commit messages are valid!" diff --git a/.gitignore b/.gitignore index 048842e..d5a35cc 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,4 @@ docs/diagrams/svg/ # Git .git/ *.patch -*.diff \ No newline at end of file +*.diff diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..95bf5f4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +# Pre-commit hooks configuration +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +repos: + # Standard pre-commit hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + # File quality checks + - id: trailing-whitespace + exclude: ^(.*\.patch|.*\.diff)$ + - id: end-of-file-fixer + exclude: ^(.*\.patch|.*\.diff)$ + - id: check-yaml + - id: check-json + - id: check-added-large-files + args: ['--maxkb=500'] + - id: check-merge-conflict + - id: detect-private-key + + # C++ specific + - id: check-case-conflict + - id: mixed-line-ending + args: ['--fix=lf'] + + # Commit message linting (Conventional Commits) + - repo: https://github.com/compilerla/conventional-pre-commit + rev: v3.1.0 + hooks: + - id: conventional-pre-commit + stages: [commit-msg] + args: + - feat + - fix + - docs + - style + - refactor + - perf + - test + - build + - ci + - chore + - revert diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb6ec98..0aedfd8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,9 @@ Thank you for your interest in contributing to the SOME/IP Stack! This document - [Code of Conduct](#code-of-conduct) - [Getting Started](#getting-started) - [Development Workflow](#development-workflow) + - [Branch Naming](#branch-naming) + - [Commit Messages](#commit-messages) + - [Pre-commit Hooks Setup](#pre-commit-hooks-setup) - [Coding Standards](#coding-standards) - [Testing](#testing) - [Documentation](#documentation) @@ -82,34 +85,68 @@ This project follows a code of conduct to ensure a welcoming environment for all ### Commit Messages -Follow conventional commit format: +We enforce [Conventional Commits](https://www.conventionalcommits.org/) format using pre-commit hooks and CI validation. **All commits in PRs must follow this format or the pipeline will fail.** ``` -type(scope): description +(): [optional body] [optional footer] ``` -Types: +**Types:** - `feat`: New features - `fix`: Bug fixes -- `docs`: Documentation -- `style`: Code style changes -- `refactor`: Code refactoring -- `test`: Testing -- `chore`: Maintenance - -Examples: +- `docs`: Documentation changes +- `style`: Code style changes (formatting, no logic change) +- `refactor`: Code refactoring (no feature/fix) +- `perf`: Performance improvements +- `test`: Adding or updating tests +- `build`: Build system or dependencies +- `ci`: CI/CD configuration +- `chore`: Maintenance tasks +- `revert`: Reverting previous commits + +**Examples:** ``` feat(transport): add TCP transport binding fix(serialization): handle endianness correctly on ARM test(message): add comprehensive message validation tests + +docs: update README with build instructions + +chore: update dependencies ``` +### Pre-commit Hooks Setup + +We use [pre-commit](https://pre-commit.com/) to enforce code quality and commit message format locally: + +```bash +# Install pre-commit +pip install pre-commit + +# Install hooks (one-time setup) +pre-commit install +pre-commit install --hook-type commit-msg + +# Run manually on all files +pre-commit run --all-files +``` + +**What the hooks check:** +- Trailing whitespace and end-of-file issues +- Valid YAML and JSON files +- No large files added (>500KB) +- No merge conflicts or private keys +- Consistent line endings (LF) +- Commit message follows Conventional Commits format + +> **Note:** The CI pipeline will run these same checks on all PRs. Setting up pre-commit locally helps catch issues before pushing. + ## Coding Standards ### C++ Standards @@ -289,11 +326,13 @@ Result send_message(const Message& message, const Endpoint& destination); ### Before Submitting -1. **Code Review**: Self-review your code -2. **Tests**: Add/update tests for new functionality -3. **Documentation**: Update relevant documentation -4. **Linting**: Ensure code follows style guidelines -5. **Testing**: All tests pass locally +1. **Pre-commit Hooks**: Run `pre-commit run --all-files` to check for issues +2. **Commit Messages**: Ensure all commits follow Conventional Commits format +3. **Code Review**: Self-review your code +4. **Tests**: Add/update tests for new functionality +5. **Documentation**: Update relevant documentation +6. **Linting**: Ensure code follows style guidelines +7. **Testing**: All tests pass locally (`ctest --output-on-failure`) ### Pull Request Template @@ -325,11 +364,17 @@ Any additional information or context ### Review Process -1. **Automated Checks**: CI/CD runs tests and linting +1. **Automated Checks**: CI/CD runs: + - Pre-commit hooks (code quality checks) + - Commit message validation (Conventional Commits format) + - Build verification (multiple compilers) + - Test suite execution 2. **Peer Review**: At least one maintainer review 3. **Approval**: Maintainers approve changes 4. **Merge**: Squash merge with descriptive commit message +> **Important:** PRs with invalid commit messages will fail CI and cannot be merged. + ## Reporting Issues ### Bug Reports diff --git a/docs/SOMEIP_ACCEPTANCE_TEST_PLAN.md b/docs/SOMEIP_ACCEPTANCE_TEST_PLAN.md index 2a03b97..302a775 100644 --- a/docs/SOMEIP_ACCEPTANCE_TEST_PLAN.md +++ b/docs/SOMEIP_ACCEPTANCE_TEST_PLAN.md @@ -616,4 +616,3 @@ See `TRACEABILITY_MATRIX.md` for full requirement mapping. --- *This test plan ensures comprehensive validation of the SOME/IP stack implementation against the Open SOME/IP Specification, following industry-standard V-Model methodology.* - diff --git a/docs/TEST_PLAN_STATUS.md b/docs/TEST_PLAN_STATUS.md index 77c86a3..aad8363 100644 --- a/docs/TEST_PLAN_STATUS.md +++ b/docs/TEST_PLAN_STATUS.md @@ -13,8 +13,8 @@ # SOME/IP Test Plan Implementation Status -**Reference Document**: [SOMEIP_ACCEPTANCE_TEST_PLAN.md](./SOMEIP_ACCEPTANCE_TEST_PLAN.md) -**Last Updated**: 2025-12-14 +**Reference Document**: [SOMEIP_ACCEPTANCE_TEST_PLAN.md](./SOMEIP_ACCEPTANCE_TEST_PLAN.md) +**Last Updated**: 2025-12-14 **Overall Progress**: 🟡 ~65% Complete --- @@ -374,4 +374,3 @@ grep -c "TEST\|TEST_F" tests/test_*.cpp --- *This document tracks the implementation status of tests defined in [SOMEIP_ACCEPTANCE_TEST_PLAN.md](./SOMEIP_ACCEPTANCE_TEST_PLAN.md)* - diff --git a/docs/diagrams/v_model_test_strategy.puml b/docs/diagrams/v_model_test_strategy.puml index c8814ab..040e9f8 100644 --- a/docs/diagrams/v_model_test_strategy.puml +++ b/docs/diagrams/v_model_test_strategy.puml @@ -64,9 +64,9 @@ rectangle "**5. Unit Testing**\n(Level 1)" as UT #FFEBEE { - test_endpoint.cpp - test_tp.cpp - test_sd.cpp - + **Coverage Target:** ≥95% - **Requirements:** + **Requirements:** - feat_req_someip_44-103 - feat_req_someip_167-299 - feat_req_someiptp_759-820 @@ -81,7 +81,7 @@ rectangle "**4. Component Testing**\n(Level 2)" as CT #FCE4EC { - TP segmentation/reassembly - RPC client/server - SD client/server - + **Framework:** GTest + pytest end note } @@ -93,7 +93,7 @@ rectangle "**3. Integration Testing**\n(Level 3)" as IT #F3E5F5 { - Service Discovery flow - Event publish/subscribe - TP with transport - + **Location:** tests/integration/ tests/test_integration.py @@ -107,7 +107,7 @@ rectangle "**2. System Testing**\n(Level 4)" as ST #E8EAF6 { - Behavior conformance - Robustness testing - Performance testing - + **Location:** tests/specification_test.py tests/conformance_test.py @@ -122,8 +122,8 @@ rectangle "**1. Acceptance Testing**\n(Level 5)" as AT #E3F2FD { - Functional completeness - Quality attributes - Documentation - - **Validation:** + + **Validation:** Cross-stack testing Wireshark verification end note @@ -165,4 +165,3 @@ endlegend footer Open SOME/IP Specification v2025 | Test Plan v1.0 @enduml - diff --git a/examples/cross_platform_demo/Dockerfile.linux b/examples/cross_platform_demo/Dockerfile.linux index 1b0d692..5e01d83 100644 --- a/examples/cross_platform_demo/Dockerfile.linux +++ b/examples/cross_platform_demo/Dockerfile.linux @@ -30,4 +30,3 @@ EXPOSE 30490/udp 30490/tcp USER 1000 CMD ["./hello_world_server"] - diff --git a/examples/cross_platform_demo/README.md b/examples/cross_platform_demo/README.md index 8a3337a..14ad798 100644 --- a/examples/cross_platform_demo/README.md +++ b/examples/cross_platform_demo/README.md @@ -106,4 +106,3 @@ On native Linux, Docker containers share the host network namespace (when using ./examples/cross_platform_demo/stop_linux_server.sh docker image rm someip-linux-server:local ``` - diff --git a/examples/cross_platform_demo/run_linux_server.sh b/examples/cross_platform_demo/run_linux_server.sh index c23ecef..419a17d 100755 --- a/examples/cross_platform_demo/run_linux_server.sh +++ b/examples/cross_platform_demo/run_linux_server.sh @@ -23,4 +23,3 @@ docker run -d --name "${CONTAINER_NAME}" \ echo "Container '${CONTAINER_NAME}' is running. Follow logs with:" echo " docker logs -f ${CONTAINER_NAME}" - diff --git a/examples/cross_platform_demo/run_mac_client.sh b/examples/cross_platform_demo/run_mac_client.sh index f98cdbd..9a7fc8d 100755 --- a/examples/cross_platform_demo/run_mac_client.sh +++ b/examples/cross_platform_demo/run_mac_client.sh @@ -22,4 +22,3 @@ fi echo "Running hello_world_client against ${SERVER_HOST}:${PORT} ..." HELLO_SERVER_HOST="${SERVER_HOST}" HELLO_SERVER_PORT="${PORT}" \ "${BUILD_DIR}/bin/hello_world_client" <<<"${MSG}" - diff --git a/examples/cross_platform_demo/stop_linux_server.sh b/examples/cross_platform_demo/stop_linux_server.sh index 2ffb8b8..c467ec6 100755 --- a/examples/cross_platform_demo/stop_linux_server.sh +++ b/examples/cross_platform_demo/stop_linux_server.sh @@ -6,4 +6,3 @@ CONTAINER_NAME="${CONTAINER_NAME:-someip-server}" echo "Stopping container '${CONTAINER_NAME}' (if running)..." docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true echo "Done." - diff --git a/examples/infra_test/README.md b/examples/infra_test/README.md index 3518aac..fa74294 100644 --- a/examples/infra_test/README.md +++ b/examples/infra_test/README.md @@ -122,9 +122,9 @@ docker network create someip-net # Run listener container docker run --rm -it --network someip-net --name listener python:3.11-slim \ - python3 -c "..." + python3 -c "..." -# Run sender container +# Run sender container docker run --rm --network someip-net python:3.11-slim \ python3 -c "..." ``` @@ -133,5 +133,3 @@ Note: Standard Docker bridge networks don't support multicast. You may need: - `--network host` (Linux only) - macvlan network - Or run both apps in same container - - diff --git a/examples/infra_test/multicast_listener.py b/examples/infra_test/multicast_listener.py index 67798b0..893df0b 100644 --- a/examples/infra_test/multicast_listener.py +++ b/examples/infra_test/multicast_listener.py @@ -18,17 +18,17 @@ def main(): # Create UDP socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - + # Bind to the multicast port sock.bind(('', MCAST_PORT)) - + # Join multicast group mreq = struct.pack("4sl", socket.inet_aton(MCAST_GROUP), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) - + print("Listening... (Ctrl+C to stop)") print() - + try: while True: data, addr = sock.recvfrom(1024) @@ -43,5 +43,3 @@ def main(): if __name__ == "__main__": main() - - diff --git a/examples/infra_test/multicast_sender.py b/examples/infra_test/multicast_sender.py index 07329fc..2bfd4e0 100644 --- a/examples/infra_test/multicast_sender.py +++ b/examples/infra_test/multicast_sender.py @@ -17,22 +17,20 @@ def main(): # Create UDP socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) - + # Set TTL for multicast sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) - + # Enable loopback (so we can receive our own messages if listener is on same host) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) - + message = b"SOMEIP-SD-TEST-" + str(int(time.time())).encode() - + print(f"Sending: {message}") sock.sendto(message, (MCAST_GROUP, MCAST_PORT)) print("Sent!") - + sock.close() if __name__ == "__main__": main() - - diff --git a/examples/protocol_checker/README.md b/examples/protocol_checker/README.md index f5e728d..601e78f 100644 --- a/examples/protocol_checker/README.md +++ b/examples/protocol_checker/README.md @@ -72,4 +72,3 @@ The server: - Not a conformance test suite For full interoperability testing with vsomeip, see `examples/vsomeip_interop/`. - diff --git a/examples/protocol_checker/raw_someip_client.c b/examples/protocol_checker/raw_someip_client.c index 8aec2a8..fdba7ac 100644 --- a/examples/protocol_checker/raw_someip_client.c +++ b/examples/protocol_checker/raw_someip_client.c @@ -85,25 +85,25 @@ int main(int argc, char* argv[]) { /* Build SOME/IP REQUEST message */ uint8_t request[SOMEIP_HEADER_SIZE]; - + /* Message ID: Service ID (16-bit) + Method ID (16-bit) */ request[0] = (service_id >> 8) & 0xFF; request[1] = service_id & 0xFF; request[2] = (method_id >> 8) & 0xFF; request[3] = method_id & 0xFF; - + /* Length: 8 (header remainder, no payload) */ request[4] = 0x00; request[5] = 0x00; request[6] = 0x00; request[7] = 0x08; - + /* Request ID: Client ID (16-bit) + Session ID (16-bit) */ request[8] = 0x00; /* Client ID high */ request[9] = 0x01; /* Client ID low */ request[10] = 0x00; /* Session ID high */ request[11] = 0x01; /* Session ID low */ - + /* Protocol version, interface version, message type, return code */ request[12] = 0x01; /* Protocol version */ request[13] = 0x01; /* Interface version */ @@ -152,7 +152,7 @@ int main(int argc, char* argv[]) { uint8_t return_code = response[15]; printf("\nMessage Type: 0x%02x (%s)\n", msg_type, - msg_type == SOMEIP_MSG_RESPONSE ? "RESPONSE" : + msg_type == SOMEIP_MSG_RESPONSE ? "RESPONSE" : msg_type == 0x81 ? "ERROR" : "OTHER"); printf("Return Code: 0x%02x (%s)\n", return_code, return_code == 0x00 ? "E_OK" : "ERROR"); @@ -168,4 +168,3 @@ int main(int argc, char* argv[]) { close(sock); return 1; } - diff --git a/examples/protocol_checker/raw_someip_server.c b/examples/protocol_checker/raw_someip_server.c index 1f4eed8..863086e 100644 --- a/examples/protocol_checker/raw_someip_server.c +++ b/examples/protocol_checker/raw_someip_server.c @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) { dump_hex(" RX", buffer, recv_len); if (recv_len < SOMEIP_HEADER_SIZE) { - printf(" ERROR: Message too short (need %d bytes, got %zd)\n\n", + printf(" ERROR: Message too short (need %d bytes, got %zd)\n\n", SOMEIP_HEADER_SIZE, recv_len); continue; } @@ -93,7 +93,7 @@ int main(int argc, char* argv[]) { /* Parse SOME/IP header */ uint16_t service_id = (buffer[0] << 8) | buffer[1]; uint16_t method_id = (buffer[2] << 8) | buffer[3]; - uint32_t length = (buffer[4] << 24) | (buffer[5] << 16) | + uint32_t length = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; uint16_t client_id = (buffer[8] << 8) | buffer[9]; uint16_t session_id = (buffer[10] << 8) | buffer[11]; @@ -104,7 +104,7 @@ int main(int argc, char* argv[]) { printf(" SOME/IP Header:\n"); printf(" Service: 0x%04x, Method: 0x%04x\n", service_id, method_id); - printf(" Length: %u, Client: 0x%04x, Session: 0x%04x\n", + printf(" Length: %u, Client: 0x%04x, Session: 0x%04x\n", length, client_id, session_id); printf(" Protocol: %d, Interface: %d, Type: 0x%02x, RC: 0x%02x\n", protocol_ver, interface_ver, msg_type, return_code); @@ -136,4 +136,3 @@ int main(int argc, char* argv[]) { close(sock); return 0; } - diff --git a/include/tp/README.md b/include/tp/README.md index 6d31fb6..a4eff7a 100644 --- a/include/tp/README.md +++ b/include/tp/README.md @@ -167,4 +167,3 @@ The TP layer includes comprehensive unit tests covering: - Timeout and cleanup behavior - Error condition handling - Memory usage limits - diff --git a/include/tp/tp_manager.h b/include/tp/tp_manager.h index 2add0e0..66077ec 100644 --- a/include/tp/tp_manager.h +++ b/include/tp/tp_manager.h @@ -189,4 +189,3 @@ class TpManager { } // namespace someip #endif // SOMEIP_TP_MANAGER_H - diff --git a/include/tp/tp_reassembler.h b/include/tp/tp_reassembler.h index 875fd85..71a36be 100644 --- a/include/tp/tp_reassembler.h +++ b/include/tp/tp_reassembler.h @@ -119,4 +119,3 @@ class TpReassembler { } // namespace someip #endif // SOMEIP_TP_REASSEMBLER_H - diff --git a/include/transport/udp_transport.h b/include/transport/udp_transport.h index 3dad921..cd878b3 100644 --- a/include/transport/udp_transport.h +++ b/include/transport/udp_transport.h @@ -39,7 +39,7 @@ struct UdpTransportConfig { bool enable_broadcast{false}; // Enable broadcast sending std::string multicast_interface{}; // Interface for multicast (empty = INADDR_ANY) int multicast_ttl{1}; // Multicast TTL (1 = local network only) - + // SOME/IP spec recommends max 1400 bytes to avoid IP fragmentation // Set to 0 to disable this check size_t max_message_size{1400}; diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh index e039899..f474b81 100755 --- a/scripts/run_clang_tidy.sh +++ b/scripts/run_clang_tidy.sh @@ -58,21 +58,21 @@ FILES_WITH_ISSUES=0 # Find only .cpp source files while IFS= read -r file; do echo "Processing $file" - + OUTPUT=$("$CLANG_TIDY_EXE" --config-file="$CONFIG_FILE" -p "$BUILD_DIR" $EXTRA_ARGS "$file" 2>&1) - + # Count warnings and errors in this file FILE_WARNINGS=$(echo "$OUTPUT" | grep -c "warning:" || true) FILE_ERRORS=$(echo "$OUTPUT" | grep -c "error:" || true) - + if [ "$FILE_WARNINGS" -gt 0 ] || [ "$FILE_ERRORS" -gt 0 ]; then FILES_WITH_ISSUES=$((FILES_WITH_ISSUES + 1)) TOTAL_WARNINGS=$((TOTAL_WARNINGS + FILE_WARNINGS)) TOTAL_ERRORS=$((TOTAL_ERRORS + FILE_ERRORS)) - + # Print to console echo "$OUTPUT" | grep -E "(warning:|error:|note:)" - + # Save to report { echo "----------------------------------------------" diff --git a/src/tp/tp_reassembler.cpp b/src/tp/tp_reassembler.cpp index 4735258..1695462 100644 --- a/src/tp/tp_reassembler.cpp +++ b/src/tp/tp_reassembler.cpp @@ -271,4 +271,4 @@ std::vector TpReassemblyBuffer::get_complete_message() const { } } // namespace tp -} // namespace someip \ No newline at end of file +} // namespace someip diff --git a/tests/README.md b/tests/README.md index c58d0a8..1c59ca7 100644 --- a/tests/README.md +++ b/tests/README.md @@ -434,4 +434,4 @@ kill -9 pytest --cov=../src --cov-report=html # View report in browser open htmlcov/index.html -``` \ No newline at end of file +``` diff --git a/tests/test_sd.cpp b/tests/test_sd.cpp index 7b3e628..13bf871 100644 --- a/tests/test_sd.cpp +++ b/tests/test_sd.cpp @@ -404,7 +404,7 @@ TEST_F(SdTest, SdMessageSerialization) { auto serialized = original.serialize(); EXPECT_GT(serialized.size(), 0u); - + // Verify flags are set correctly in first byte EXPECT_EQ(serialized[0] & 0x80, 0x80); // Reboot flag } diff --git a/tests/test_serialization.cpp b/tests/test_serialization.cpp index 1bb4f24..010928a 100644 --- a/tests/test_serialization.cpp +++ b/tests/test_serialization.cpp @@ -197,10 +197,10 @@ TEST_F(SerializationTest, SerializeDeserializeUint64) { for (uint64_t value : test_values) { serializer.reset(); serializer.serialize_uint64(value); - + // Verify buffer size is exactly 8 bytes EXPECT_EQ(serializer.get_buffer().size(), 8u); - + deserializer = Deserializer(serializer.get_buffer()); auto result = deserializer.deserialize_uint64(); EXPECT_TRUE(result.is_success()) << "Failed for value: 0x" << std::hex << value; @@ -227,9 +227,9 @@ TEST_F(SerializationTest, SerializeDeserializeInt8) { for (int8_t value : test_values) { serializer.reset(); serializer.serialize_int8(value); - + EXPECT_EQ(serializer.get_buffer().size(), 1u); - + deserializer = Deserializer(serializer.get_buffer()); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_int8(), value); } @@ -250,9 +250,9 @@ TEST_F(SerializationTest, SerializeDeserializeInt16) { for (int16_t value : test_values) { serializer.reset(); serializer.serialize_int16(value); - + EXPECT_EQ(serializer.get_buffer().size(), 2u); - + deserializer = Deserializer(serializer.get_buffer()); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_int16(), value); } @@ -273,9 +273,9 @@ TEST_F(SerializationTest, SerializeDeserializeInt32) { for (int32_t value : test_values) { serializer.reset(); serializer.serialize_int32(value); - + EXPECT_EQ(serializer.get_buffer().size(), 4u); - + deserializer = Deserializer(serializer.get_buffer()); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_int32(), value); } @@ -296,9 +296,9 @@ TEST_F(SerializationTest, SerializeDeserializeInt64) { for (int64_t value : test_values) { serializer.reset(); serializer.serialize_int64(value); - + EXPECT_EQ(serializer.get_buffer().size(), 8u); - + deserializer = Deserializer(serializer.get_buffer()); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_int64(), value); } @@ -325,10 +325,10 @@ TEST_F(SerializationTest, SerializeDeserializeFloat) { for (float value : test_values) { serializer.reset(); serializer.serialize_float(value); - + // IEEE 754 float32 is 4 bytes EXPECT_EQ(serializer.get_buffer().size(), 4u); - + deserializer = Deserializer(serializer.get_buffer()); auto float_result = deserializer.deserialize_float(); EXPECT_TRUE(float_result.is_success()); @@ -395,10 +395,10 @@ TEST_F(SerializationTest, SerializeDeserializeDouble) { for (double value : test_values) { serializer.reset(); serializer.serialize_double(value); - + // IEEE 754 float64 (double) is 8 bytes EXPECT_EQ(serializer.get_buffer().size(), 8u); - + deserializer = Deserializer(serializer.get_buffer()); auto double_result = deserializer.deserialize_double(); EXPECT_TRUE(double_result.is_success()); @@ -454,13 +454,13 @@ TEST_F(SerializationTest, SerializeDeserializeUint8Array) { serializer.reset(); serializer.serialize_array(test_array); - + deserializer = Deserializer(serializer.get_buffer()); auto length_result = deserializer.deserialize_uint32(); EXPECT_TRUE(length_result.is_success()); uint32_t length = length_result.get_value(); EXPECT_EQ(length, test_array.size()); - + auto array_result = deserializer.deserialize_array(length); EXPECT_TRUE(array_result.is_success()); auto result = array_result.get_value(); @@ -475,13 +475,13 @@ TEST_F(SerializationTest, SerializeDeserializeInt16Array) { serializer.reset(); serializer.serialize_array(test_array); - + deserializer = Deserializer(serializer.get_buffer()); auto length_result = deserializer.deserialize_uint32(); EXPECT_TRUE(length_result.is_success()); uint32_t length = length_result.get_value(); EXPECT_EQ(length, test_array.size()); - + auto array_result = deserializer.deserialize_array(length); EXPECT_TRUE(array_result.is_success()); auto result = array_result.get_value(); @@ -496,13 +496,13 @@ TEST_F(SerializationTest, SerializeDeserializeFloatArray) { serializer.reset(); serializer.serialize_array(test_array); - + deserializer = Deserializer(serializer.get_buffer()); auto length_result = deserializer.deserialize_uint32(); EXPECT_TRUE(length_result.is_success()); uint32_t length = length_result.get_value(); EXPECT_EQ(length, test_array.size()); - + auto array_result = deserializer.deserialize_array(length); EXPECT_TRUE(array_result.is_success()); auto result = array_result.get_value(); @@ -520,13 +520,13 @@ TEST_F(SerializationTest, SerializeDeserializeEmptyArray) { serializer.reset(); serializer.serialize_array(empty_array); - + deserializer = Deserializer(serializer.get_buffer()); auto length_result = deserializer.deserialize_uint32(); EXPECT_TRUE(length_result.is_success()); uint32_t length = length_result.get_value(); EXPECT_EQ(length, 0u); - + auto array_result = deserializer.deserialize_array(length); EXPECT_TRUE(array_result.is_success()); auto result = array_result.get_value(); @@ -544,13 +544,13 @@ TEST_F(SerializationTest, SerializeDeserializeStringArray) { serializer.reset(); serializer.serialize_array(test_array); - + deserializer = Deserializer(serializer.get_buffer()); auto length_result = deserializer.deserialize_uint32(); EXPECT_TRUE(length_result.is_success()); uint32_t length = length_result.get_value(); EXPECT_EQ(length, test_array.size()); - + auto array_result = deserializer.deserialize_array(length); EXPECT_TRUE(array_result.is_success()); auto result = array_result.get_value(); @@ -562,10 +562,10 @@ TEST_F(SerializationTest, SerializeDeserializeStringArray) { // ============================================================================= TEST_F(SerializationTest, VerifyBigEndianUint16) { Serializer serializer; - + // 0x1234 should serialize as [0x12, 0x34] in big-endian serializer.serialize_uint16(0x1234); - + const auto& buffer = serializer.get_buffer(); ASSERT_EQ(buffer.size(), 2u); EXPECT_EQ(buffer[0], 0x12); // High byte first @@ -574,10 +574,10 @@ TEST_F(SerializationTest, VerifyBigEndianUint16) { TEST_F(SerializationTest, VerifyBigEndianUint32) { Serializer serializer; - + // 0x12345678 should serialize as [0x12, 0x34, 0x56, 0x78] in big-endian serializer.serialize_uint32(0x12345678); - + const auto& buffer = serializer.get_buffer(); ASSERT_EQ(buffer.size(), 4u); EXPECT_EQ(buffer[0], 0x12); @@ -588,10 +588,10 @@ TEST_F(SerializationTest, VerifyBigEndianUint32) { TEST_F(SerializationTest, VerifyBigEndianUint64) { Serializer serializer; - + // 0x0102030405060708 should serialize as [0x01, 0x02, ..., 0x08] in big-endian serializer.serialize_uint64(0x0102030405060708ULL); - + const auto& buffer = serializer.get_buffer(); ASSERT_EQ(buffer.size(), 8u); EXPECT_EQ(buffer[0], 0x01); @@ -606,10 +606,10 @@ TEST_F(SerializationTest, VerifyBigEndianUint64) { TEST_F(SerializationTest, VerifyBigEndianNegativeInt16) { Serializer serializer; - + // -1 (0xFFFF) should serialize as [0xFF, 0xFF] in big-endian serializer.serialize_int16(-1); - + const auto& buffer = serializer.get_buffer(); ASSERT_EQ(buffer.size(), 2u); EXPECT_EQ(buffer[0], 0xFF); @@ -621,14 +621,14 @@ TEST_F(SerializationTest, VerifyBigEndianNegativeInt16) { // ============================================================================= TEST_F(SerializationTest, AlignTo4Bytes) { Serializer serializer; - + // Serialize 1 byte, then align to 4 serializer.serialize_uint8(0x12); EXPECT_EQ(serializer.get_size(), 1u); - + serializer.align_to(4); EXPECT_EQ(serializer.get_size(), 4u); // Should be padded to 4 bytes - + // Verify padding is zeros const auto& buffer = serializer.get_buffer(); EXPECT_EQ(buffer[0], 0x12); @@ -639,38 +639,38 @@ TEST_F(SerializationTest, AlignTo4Bytes) { TEST_F(SerializationTest, AlignTo8Bytes) { Serializer serializer; - + // Serialize 3 bytes, then align to 8 serializer.serialize_uint8(0x01); serializer.serialize_uint8(0x02); serializer.serialize_uint8(0x03); EXPECT_EQ(serializer.get_size(), 3u); - + serializer.align_to(8); EXPECT_EQ(serializer.get_size(), 8u); // Should be padded to 8 bytes } TEST_F(SerializationTest, AlignAlreadyAligned) { Serializer serializer; - + // Serialize 4 bytes, then align to 4 (should do nothing) serializer.serialize_uint32(0x12345678); EXPECT_EQ(serializer.get_size(), 4u); - + serializer.align_to(4); EXPECT_EQ(serializer.get_size(), 4u); // No change } TEST_F(SerializationTest, DeserializerAlign) { Serializer serializer; - + // Create buffer: [0x12, pad, pad, pad, 0x56, 0x78, 0x9A, 0xBC] serializer.serialize_uint8(0x12); serializer.align_to(4); serializer.serialize_uint32(0x56789ABC); - + Deserializer deserializer(serializer.get_buffer()); - + auto byte_result = deserializer.deserialize_uint8(); EXPECT_TRUE(byte_result.is_success()); uint8_t byte_val = byte_result.get_value(); @@ -689,13 +689,13 @@ TEST_F(SerializationTest, DeserializerAlign) { // ============================================================================= TEST_F(SerializationTest, BooleanUsesLowestBitOnly) { Serializer serializer; - + // True should serialize as 0x01 serializer.serialize_bool(true); EXPECT_EQ(serializer.get_buffer()[0], 0x01); - + serializer.reset(); - + // False should serialize as 0x00 serializer.serialize_bool(false); EXPECT_EQ(serializer.get_buffer()[0], 0x00); @@ -709,12 +709,12 @@ TEST_F(SerializationTest, DeserializerPositionTracking) { serializer.serialize_uint32(0x12345678); serializer.serialize_uint16(0xABCD); serializer.serialize_uint8(0xFF); - + Deserializer deserializer(serializer.get_buffer()); - + EXPECT_EQ(deserializer.get_position(), 0u); EXPECT_EQ(deserializer.get_remaining(), 7u); - + auto dummy32 = deserializer.deserialize_uint32(); EXPECT_TRUE(dummy32.is_success()); EXPECT_EQ(deserializer.get_position(), 4u); @@ -736,12 +736,12 @@ TEST_F(SerializationTest, DeserializerSkip) { serializer.serialize_uint32(0x11111111); serializer.serialize_uint32(0x22222222); serializer.serialize_uint32(0x33333333); - + Deserializer deserializer(serializer.get_buffer()); - + deserializer.skip(4); // Skip first uint32 EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 0x22222222u); - + deserializer.skip(4); // Skip last uint32 EXPECT_EQ(deserializer.get_remaining(), 0u); } @@ -751,17 +751,17 @@ TEST_F(SerializationTest, DeserializerSetPosition) { serializer.serialize_uint32(0x11111111); serializer.serialize_uint32(0x22222222); serializer.serialize_uint32(0x33333333); - + Deserializer deserializer(serializer.get_buffer()); - + // Jump to third uint32 EXPECT_TRUE(deserializer.set_position(8)); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 0x33333333u); - + // Jump back to first EXPECT_TRUE(deserializer.set_position(0)); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 0x11111111u); - + // Invalid position should fail EXPECT_FALSE(deserializer.set_position(100)); } @@ -769,13 +769,13 @@ TEST_F(SerializationTest, DeserializerSetPosition) { TEST_F(SerializationTest, DeserializerReset) { Serializer serializer; serializer.serialize_uint32(0x12345678); - + Deserializer deserializer(serializer.get_buffer()); - + auto reset_result = deserializer.deserialize_uint32(); EXPECT_TRUE(reset_result.is_success()); EXPECT_EQ(deserializer.get_position(), 4u); - + deserializer.reset(); EXPECT_EQ(deserializer.get_position(), 0u); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 0x12345678u); @@ -786,7 +786,7 @@ TEST_F(SerializationTest, DeserializerReset) { // ============================================================================= TEST_F(SerializationTest, NestedDataStructure) { Serializer serializer; - + // Simulate a complex struct: // - uint16 id // - uint32 timestamp @@ -794,7 +794,7 @@ TEST_F(SerializationTest, NestedDataStructure) { // - bool active // - string name // - array data - + serializer.serialize_uint16(0x1234); // id serializer.serialize_uint32(1702500000); // timestamp (unix) serializer.serialize_float(25.5f); // temperature @@ -802,10 +802,10 @@ TEST_F(SerializationTest, NestedDataStructure) { serializer.serialize_string("Sensor01"); // name std::vector data = {0xAA, 0xBB, 0xCC, 0xDD}; serializer.serialize_array(data); // data - + // Deserialize and verify Deserializer deserializer(serializer.get_buffer()); - + EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint16(), 0x1234u); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 1702500000u); auto float_result = deserializer.deserialize_float(); @@ -815,7 +815,7 @@ TEST_F(SerializationTest, NestedDataStructure) { EXPECT_TRUE(bool_result.is_success()); EXPECT_TRUE(bool_result.get_value()); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_string(), "Sensor01"); - + auto data_len_result = deserializer.deserialize_uint32(); EXPECT_TRUE(data_len_result.is_success()); EXPECT_EQ(data_len_result.get_value(), 4u); @@ -824,7 +824,7 @@ TEST_F(SerializationTest, NestedDataStructure) { EXPECT_TRUE(array_result.is_success()); auto result_data = array_result.get_value(); EXPECT_EQ(result_data, data); - + EXPECT_EQ(deserializer.get_remaining(), 0u); } @@ -835,16 +835,16 @@ TEST_F(SerializationTest, MoveBuffer) { Serializer serializer; serializer.serialize_uint32(0x12345678); serializer.serialize_uint32(0xABCDEF01); - + // Get size before move size_t size_before = serializer.get_size(); EXPECT_EQ(size_before, 8u); - + // Move buffer out std::vector moved_buffer = serializer.move_buffer(); - + EXPECT_EQ(moved_buffer.size(), 8u); - + // Verify contents Deserializer deserializer(moved_buffer); EXPECT_DESERIALIZE_SUCCESS(deserializer.deserialize_uint32(), 0x12345678u); diff --git a/tests/test_udp_transport.cpp b/tests/test_udp_transport.cpp index 2b0ba0c..31d9fb5 100644 --- a/tests/test_udp_transport.cpp +++ b/tests/test_udp_transport.cpp @@ -636,4 +636,3 @@ TEST_F(UdpTransportTest, MultipleMessagesRapidFire) { sender.stop(); receiver.stop(); } -