From 2ca779115f0ab2158bd1457b5fb2229ef6683934 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 15:03:16 +0200 Subject: [PATCH 01/13] ci: add Mull workflow for can_partnered_control_function This workflow runs mutation testing for the CAN partnered control function, including setup, compilation, and test execution. --- ...tnered-control-function-all-unit-tests.yml | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 .github/workflows/mull-can-partnered-control-function-all-unit-tests.yml diff --git a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml new file mode 100644 index 000000000..221874ff7 --- /dev/null +++ b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml @@ -0,0 +1,193 @@ +name: Mull - CAN PARTNERED CONTROL FUNCTION - All Tests + +on: + workflow_dispatch: + + # Le workflow n'étant pas encore présent sur main, ce déclencheur permet + # de l'exécuter lorsque son propre fichier est créé ou modifié. + push: + branches: + - quality/mutation-testing-can-partnered-control-function + paths: + - .github/workflows/mull-can-partnered-control-function-all-unit-tests.yml + +permissions: + contents: read + +jobs: + can-partnered-control-function: + name: Mutation testing - can_partnered_control_function - all tests + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Clang 19 and build dependencies + shell: bash + run: | + set -euo pipefail + + sudo apt-get update + sudo apt-get install -y \ + clang-19 \ + llvm-19 \ + build-essential \ + cmake \ + ninja-build \ + curl + + - name: Install Mull for LLVM 19 + shell: bash + run: | + set -euo pipefail + + curl -1sLf \ + "https://dl.cloudsmith.io/public/mull-project/mull-stable/setup.deb.sh" \ + | sudo -E bash + + sudo apt-get update + sudo apt-get install -y mull-19 + + - name: Verify tools + shell: bash + run: | + set -euo pipefail + + clang-19 --version + llvm-profdata-19 --version + llvm-cov-19 --version + mull-runner-19 --version + + test -f /usr/lib/mull-ir-frontend-19 + + - name: Create Mull configuration + shell: bash + run: | + set -euo pipefail + + cat > mull-can-partnered-control-function.yml <<'EOF' + includePaths: + - ".*isobus/src/can_partnered_control_function\\.cpp$" + excludePaths: + - ".*googletest.*" + - ".*googlemock.*" + - ".*gtest.*" + - ".*gmock.*" + quiet: false + EOF + + echo "=== Mull configuration ===" + cat mull-can-partnered-control-function.yml + + - name: Configure tests for Mull and LLVM coverage + shell: bash + env: + CC: clang-19 + CXX: clang++-19 + MULL_CONFIG: ${{ github.workspace }}/mull-can-partnered-control-function.yml + run: | + set -euo pipefail + + cmake -S . -B build \ + -G Ninja \ + -DBUILD_TESTING=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_C_FLAGS="-O0 -g -grecord-command-line -fprofile-instr-generate -fcoverage-mapping -fpass-plugin=/usr/lib/mull-ir-frontend-19" \ + -DCMAKE_CXX_FLAGS="-O0 -g -grecord-command-line -fprofile-instr-generate -fcoverage-mapping -fpass-plugin=/usr/lib/mull-ir-frontend-19" \ + -DCMAKE_EXE_LINKER_FLAGS="-fprofile-instr-generate" + + - name: Compile unit tests + shell: bash + run: | + set -euo pipefail + + cmake --build build --parallel + test -x build/test/unit_tests + + - name: Run all unit tests and generate coverage + shell: bash + env: + LLVM_PROFILE_FILE: ${{ github.workspace }}/all-tests.profraw + run: | + set -euo pipefail + + rm -f all-tests.profraw all-tests.profdata + + cd build/test + + ./unit_tests \ + --gtest_color=no \ + 2>&1 | tee "${GITHUB_WORKSPACE}/all-tests-baseline.log" + + cd "${GITHUB_WORKSPACE}" + + test -s all-tests.profraw + + llvm-profdata-19 merge \ + -sparse \ + all-tests.profraw \ + -o all-tests.profdata + + test -s all-tests.profdata + + - name: Generate readable coverage report + shell: bash + run: | + set -euo pipefail + + llvm-cov-19 show \ + build/test/unit_tests \ + -instr-profile=all-tests.profdata \ + isobus/src/can_partnered_control_function.cpp \ + | tee can-partnered-control-function-coverage.txt + + - name: Run Mull with all unit tests + shell: bash + env: + MULL_CONFIG: ${{ github.workspace }}/mull-can-partnered-control-function.yml + run: | + set -euo pipefail + + mkdir -p "${GITHUB_WORKSPACE}/mull-results" + + cd build/test + + mull-runner-19 \ + --allow-surviving \ + --debug \ + --timeout 120000 \ + --coverage-info "${GITHUB_WORKSPACE}/all-tests.profdata" \ + --reporters IDE \ + --reporters Elements \ + --report-dir "${GITHUB_WORKSPACE}/mull-results" \ + --report-name can-partnered-control-function-all-tests \ + ./unit_tests \ + --gtest_color=no \ + 2>&1 | tee "${GITHUB_WORKSPACE}/mull-results/mull-console.log" + + - name: Display Mull results + if: always() + shell: bash + run: | + echo "=== Generated Mull files ===" + find mull-results -maxdepth 2 -type f -print 2>/dev/null || true + + echo + echo "=== Mull console output ===" + cat mull-results/mull-console.log 2>/dev/null || true + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: mull-can-partnered-control-function-all-unit-tests + path: | + mull-results/ + mull-can-partnered-control-function.yml + all-tests-baseline.log + all-tests.profdata + can-partnered-control-function-coverage.txt + if-no-files-found: warn + retention-days: 30 From 285bebeeb7d0283ab3b39b972e63073c315cacd1 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 15:18:56 +0200 Subject: [PATCH 02/13] ci: add workflow to identify tests covering can_partnered_control_function mutants This workflow identifies unit tests that cover mutants in the CAN PARTNERED CONTROL FUNCTION. It includes steps for setting up the environment, compiling tests, and generating coverage reports. --- ...tnered-control-function-covering-tests.yml | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 .github/workflows/identify-can-partnered-control-function-covering-tests.yml diff --git a/.github/workflows/identify-can-partnered-control-function-covering-tests.yml b/.github/workflows/identify-can-partnered-control-function-covering-tests.yml new file mode 100644 index 000000000..174a280a7 --- /dev/null +++ b/.github/workflows/identify-can-partnered-control-function-covering-tests.yml @@ -0,0 +1,247 @@ +name: Identify Tests Covering CAN PARTNERED CONTROL FUNCTION Mutants + +on: + workflow_dispatch: + + push: + branches: + - quality/mutation-testing-can-partnered-control-function + paths: + - .github/workflows/identify-can-partnered-control-function-covering-tests.yml + +permissions: + contents: read + +jobs: + identify-tests: + name: Identify unit tests covering can_partnered_control_function mutants + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Clang 19 and build dependencies + shell: bash + run: | + set -euo pipefail + + sudo apt-get update + sudo apt-get install -y \ + clang-19 \ + llvm-19 \ + build-essential \ + cmake \ + ninja-build + + - name: Configure tests with LLVM coverage + shell: bash + env: + CC: clang-19 + CXX: clang++-19 + run: | + set -euo pipefail + + cmake -S . -B build \ + -G Ninja \ + -DBUILD_TESTING=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_C_FLAGS="-O0 -g -fprofile-instr-generate -fcoverage-mapping" \ + -DCMAKE_CXX_FLAGS="-O0 -g -fprofile-instr-generate -fcoverage-mapping" \ + -DCMAKE_EXE_LINKER_FLAGS="-fprofile-instr-generate" + + - name: Compile unit tests + shell: bash + run: | + set -euo pipefail + + cmake --build build --parallel + test -x build/test/unit_tests + + - name: List selected unit tests + shell: bash + run: | + set -euo pipefail + + cd build/test + + ./unit_tests \ + --gtest_list_tests \ + --gtest_color=no \ + > "${GITHUB_WORKSPACE}/gtest-list.txt" + + awk ' + /^[^[:space:]]/ { + suite = $1 + sub(/\.$/, "", suite) + next + } + /^[[:space:]]+/ { + test = $1 + sub(/#.*/, "", test) + if (suite != "" && test != "") { + print suite "." test + } + } + ' "${GITHUB_WORKSPACE}/gtest-list.txt" \ + > "${GITHUB_WORKSPACE}/selected-tests.txt" + + test -s "${GITHUB_WORKSPACE}/selected-tests.txt" + echo "Selected tests: $(wc -l < "${GITHUB_WORKSPACE}/selected-tests.txt")" + + - name: Identify tests covering surviving mutants + shell: bash + run: | + set -euo pipefail + + SOURCE_FILE="${GITHUB_WORKSPACE}/isobus/src/can_partnered_control_function.cpp" + RESULTS_DIR="${GITHUB_WORKSPACE}/per-test-coverage" + REPORT_FILE="${GITHUB_WORKSPACE}/tests-covering-can-partnered-control-function-mutants.txt" + FAILED_FILE="${GITHUB_WORKSPACE}/tests-not-analysed.txt" + + test -f "${SOURCE_FILE}" + mkdir -p "${RESULTS_DIR}" + + + TARGET_LINES="53 82 87 174" + for line in ${TARGET_LINES}; do + : > "${RESULTS_DIR}/line-${line}-tests.txt" + done + : > "${FAILED_FILE}" + + cd build/test + + index=0 + while IFS= read -r test_name; do + index=$((index + 1)) + + profile_raw="${RESULTS_DIR}/test-${index}.profraw" + profile_data="${RESULTS_DIR}/test-${index}.profdata" + coverage_text="${RESULTS_DIR}/test-${index}.txt" + + echo "[${index}] ${test_name}" + + if ! LLVM_PROFILE_FILE="${profile_raw}" \ + ./unit_tests \ + --gtest_filter="${test_name}" \ + --gtest_color=no \ + > /dev/null 2>&1; then + echo "${test_name}" >> "${FAILED_FILE}" + rm -f "${profile_raw}" "${profile_data}" "${coverage_text}" + continue + fi + + if [ ! -s "${profile_raw}" ]; then + echo "${test_name}" >> "${FAILED_FILE}" + rm -f "${profile_raw}" "${profile_data}" "${coverage_text}" + continue + fi + + llvm-profdata-19 merge \ + -sparse \ + "${profile_raw}" \ + -o "${profile_data}" + + llvm-cov-19 show \ + ./unit_tests \ + -instr-profile="${profile_data}" \ + "${SOURCE_FILE}" \ + > "${coverage_text}" + + for target_line in ${TARGET_LINES}; do + if awk -F'|' -v target_line="${target_line}" ' + { + line = $1 + count = $2 + gsub(/[[:space:]]/, "", line) + gsub(/[[:space:]]/, "", count) + if (line == target_line && count != "" && count != "0") { + found = 1 + } + } + END { exit(found ? 0 : 1) } + ' "${coverage_text}"; then + echo "${test_name}" >> "${RESULTS_DIR}/line-${target_line}-tests.txt" + fi + done + + rm -f "${profile_raw}" "${profile_data}" "${coverage_text}" + done < "${GITHUB_WORKSPACE}/selected-tests.txt" + + for line in ${TARGET_LINES}; do + sort -u "${RESULTS_DIR}/line-${line}-tests.txt" \ + -o "${RESULTS_DIR}/line-${line}-tests.txt" + done + + print_covering_tests() { + line="$1" + echo "Tests covering line ${line}:" + if [ -s "${RESULTS_DIR}/line-${line}-tests.txt" ]; then + cat "${RESULTS_DIR}/line-${line}-tests.txt" + else + echo "No individual test found" + fi + echo + } + + { + echo "Surviving mutants in can_partnered_control_function.cpp" + echo + echo "Line 53 - Mutated expression" + echo "Mutant: Replaced < with <= [cxx_lt_to_le]" + echo + print_covering_tests 53 + + echo "Line 82 - Mutated expression" + echo "Mutant: Replaced == with != [cxx_eq_to_ne]" + echo + print_covering_tests 82 + + echo "Line 87 - Mutated expressions" + echo "Mutant 1: Replaced < with <= [cxx_lt_to_le]" + echo "Mutant 2: Replaced ++ with -- [cxx_post_inc_to_post_dec]" + echo + print_covering_tests 87 + + echo "Line 174 - Mutated expression" + echo "Mutant: Replaced == with != [cxx_eq_to_ne]" + echo + print_covering_tests 174 + + echo "Tests not analysed successfully" + if [ -s "${FAILED_FILE}" ]; then + cat "${FAILED_FILE}" + else + echo "None" + fi + } | tee "${REPORT_FILE}" + + - name: Publish summary + if: always() + shell: bash + run: | + { + echo "## Tests covering CAN PARTNERED CONTROL FUNCTION mutants" + echo + echo '```text' + cat tests-covering-can-partnered-control-function-mutants.txt 2>/dev/null || \ + echo "The identification report was not generated." + echo '```' + } >> "${GITHUB_STEP_SUMMARY}" + + - name: Upload identification report + if: always() + uses: actions/upload-artifact@v4 + with: + name: can-partnered-control-function-covering-tests + path: | + tests-covering-can-partnered-control-function-mutants.txt + tests-not-analysed.txt + selected-tests.txt + gtest-list.txt + per-test-coverage/line-53-tests.txt + per-test-coverage/line-82-tests.txt + per-test-coverage/line-87-tests.txt + per-test-coverage/line-174-tests.txt + if-no-files-found: warn + retention-days: 30 From 29a07b073bbdef34968466f8ed80ae723011336c Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 15:49:47 +0200 Subject: [PATCH 03/13] test: strengthen partnered control function coverage --- test/core_network_management_tests.cpp | 39 +++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/test/core_network_management_tests.cpp b/test/core_network_management_tests.cpp index 74170cb19..1ee85cb77 100644 --- a/test/core_network_management_tests.cpp +++ b/test/core_network_management_tests.cpp @@ -356,13 +356,42 @@ TEST_F(CoreTest, SimilarControlFunctions) // Make a partner that is a fuel system // Using a less common function to avoid interfering with other tests when not running under CTest - const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); - const std::vector nameFilters = { filterFuelSystem }; - auto TestPartner = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, nameFilters); + auto TestPartner = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, nameFilters); + const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); + const isobus::NAMEFilter filterEcuInstance(isobus::NAME::NAMEParameters::EcuInstance, 0); + const std::vector nameFilters = { filterFuelSystem, filterEcuInstance }; // Quick test to make sure partner is working EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::FunctionCode)); - + EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::EcuInstance)); + + isobus::NAME::NAMEParameters filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; + std::uint32_t filterValue = 0; + + EXPECT_TRUE(TestPartner->get_name_filter_parameter(0, filterParameter, filterValue)); + EXPECT_EQ(isobus::NAME::NAMEParameters::FunctionCode, filterParameter); + EXPECT_EQ(static_cast(isobus::NAME::Function::FuelSystem), filterValue); + + filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; + filterValue = 0; + + EXPECT_TRUE(TestPartner->get_name_filter_parameter(1, filterParameter, filterValue)); + EXPECT_EQ(isobus::NAME::NAMEParameters::EcuInstance, filterParameter); + EXPECT_EQ(0, filterValue); + + filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; + filterValue = 0xFFFFFFFF; + + EXPECT_FALSE(TestPartner->get_name_filter_parameter(TestPartner->get_number_name_filters(), filterParameter, filterValue)); + EXPECT_EQ(isobus::NAME::NAMEParameters::IdentityNumber, filterParameter); + EXPECT_EQ(0xFFFFFFFF, filterValue); + + const std::vector emptyNameFilters; + auto partnerWithoutFilters = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, emptyNameFilters); + + EXPECT_EQ(0, partnerWithoutFilters->get_number_name_filters()); + EXPECT_FALSE(partnerWithoutFilters->check_matches_name(NAME(0))); + // Request the address claim PGN CANNetworkManager::CANNetwork.process_receive_can_message_frame(test_helpers::create_message_frame_pgn_request( 0xEE00, // Address Claim PGN @@ -422,5 +451,7 @@ TEST_F(CoreTest, SimilarControlFunctions) // Partner should never change EXPECT_EQ(TestPartner->get_NAME().get_full_name(), 0xa0000F000425e9f8); + + CANNetworkManager::CANNetwork.deactivate_control_function(partnerWithoutFilters); CANNetworkManager::CANNetwork.deactivate_control_function(TestPartner); } From 72064b72b91feddc3296ee7c16a959e86c8e50c0 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 15:53:09 +0200 Subject: [PATCH 04/13] test: verify partnered control function PGN callback filtering Added test case to verify that unrelated PGN messages do not trigger TC callbacks. --- test/tc_client_tests.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/tc_client_tests.cpp b/test/tc_client_tests.cpp index d8be91891..71e184588 100644 --- a/test/tc_client_tests.cpp +++ b/test/tc_client_tests.cpp @@ -1440,6 +1440,37 @@ TEST_F(TaskControllerClientTest, CallbackTests) interfaceUnderTest.add_value_command_callback(value_command_callback, nullptr); interfaceUnderTest.test_wrapper_set_state(TaskControllerClient::StateMachineState::Connected); + valueRequested = false; + valueCommanded = false; + requestedDDI = 0; + commandedDDI = 0; + requestedElement = 0; + commandedElement = 0; + commandedValue = 0; + + // A message with a different PGN must not invoke the registered TC callbacks. + testFrame.identifier = 0x18CC86F7; + testFrame.data[0] = 0x82; + testFrame.data[1] = 0x04; + testFrame.data[2] = 0x12; + testFrame.data[3] = 0x34; + testFrame.data[4] = 0x00; + testFrame.data[5] = 0x00; + testFrame.data[6] = 0x00; + testFrame.data[7] = 0x00; + + CANNetworkManager::CANNetwork.process_receive_can_message_frame(testFrame); + CANNetworkManager::CANNetwork.update(); + interfaceUnderTest.update(); + + EXPECT_FALSE(valueRequested); + EXPECT_FALSE(valueCommanded); + EXPECT_EQ(0, requestedDDI); + EXPECT_EQ(0, requestedElement); + EXPECT_EQ(0, commandedDDI); + EXPECT_EQ(0, commandedElement); + EXPECT_EQ(0, commandedValue); + // Status message testFrame.identifier = 0x18CBFFF7; testFrame.data[0] = 0xFE; // Status mux From 834ef56a6d823935a4f302bdf05b6b226b381916 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 15:55:05 +0200 Subject: [PATCH 05/13] ci: rerun mutation testing with updated tests --- .../mull-can-partnered-control-function-all-unit-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml index 221874ff7..4fd80cad2 100644 --- a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml +++ b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml @@ -1,5 +1,7 @@ name: Mull - CAN PARTNERED CONTROL FUNCTION - All Tests +#Re-run #1 + on: workflow_dispatch: From fa87aaa5573ee37d11496b8464a8053ccd4b71f8 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 16:41:24 +0200 Subject: [PATCH 06/13] test: restore partnered control function name filters --- test/core_network_management_tests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/core_network_management_tests.cpp b/test/core_network_management_tests.cpp index 1ee85cb77..451ad3c89 100644 --- a/test/core_network_management_tests.cpp +++ b/test/core_network_management_tests.cpp @@ -357,6 +357,10 @@ TEST_F(CoreTest, SimilarControlFunctions) // Make a partner that is a fuel system // Using a less common function to avoid interfering with other tests when not running under CTest + const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); + const isobus::NAMEFilter filterEcuInstance(isobus::NAME::NAMEParameters::EcuInstance, 0); + const std::vector nameFilters = {filterFuelSystem, filterEcuInstance}; + auto TestPartner = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, nameFilters); const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); const isobus::NAMEFilter filterEcuInstance(isobus::NAME::NAMEParameters::EcuInstance, 0); From 9c7f9538a54fb992c558846fb439bf0339d3af78 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 16:43:36 +0200 Subject: [PATCH 07/13] ci: rerun mutation testing after test fix Added a comment for re-running the workflow. --- .../mull-can-partnered-control-function-all-unit-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml index 4fd80cad2..f7f5e700d 100644 --- a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml +++ b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml @@ -1,6 +1,7 @@ name: Mull - CAN PARTNERED CONTROL FUNCTION - All Tests #Re-run #1 +#Re-run #2 on: workflow_dispatch: From 0720d5c7eb6c9ce6ccb56f96827daf85c4d5e155 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 16:51:03 +0200 Subject: [PATCH 08/13] test: remove duplicate partnered control function filters Removed redundant filter declarations in test. --- test/core_network_management_tests.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/core_network_management_tests.cpp b/test/core_network_management_tests.cpp index 451ad3c89..7d40ed792 100644 --- a/test/core_network_management_tests.cpp +++ b/test/core_network_management_tests.cpp @@ -362,9 +362,6 @@ TEST_F(CoreTest, SimilarControlFunctions) const std::vector nameFilters = {filterFuelSystem, filterEcuInstance}; auto TestPartner = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, nameFilters); - const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); - const isobus::NAMEFilter filterEcuInstance(isobus::NAME::NAMEParameters::EcuInstance, 0); - const std::vector nameFilters = { filterFuelSystem, filterEcuInstance }; // Quick test to make sure partner is working EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::FunctionCode)); EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::EcuInstance)); From 0a128b8bfb0afca651f933408d396ce480a30220 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 16:52:37 +0200 Subject: [PATCH 09/13] ci: rerun #3 mutation testing after compilation fix --- .../mull-can-partnered-control-function-all-unit-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml index f7f5e700d..10034d2b1 100644 --- a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml +++ b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml @@ -2,6 +2,7 @@ name: Mull - CAN PARTNERED CONTROL FUNCTION - All Tests #Re-run #1 #Re-run #2 +#Re-run #3 on: workflow_dispatch: From 75d41c93f4fdaf7ae939945ae1b1b6c2a5499b15 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 17:11:26 +0200 Subject: [PATCH 10/13] Enhance core network management tests Added test for ManufacturerCode and check_matches_name. --- test/core_network_management_tests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/core_network_management_tests.cpp b/test/core_network_management_tests.cpp index 7d40ed792..3afe3479e 100644 --- a/test/core_network_management_tests.cpp +++ b/test/core_network_management_tests.cpp @@ -365,7 +365,14 @@ TEST_F(CoreTest, SimilarControlFunctions) // Quick test to make sure partner is working EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::FunctionCode)); EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::EcuInstance)); + EXPECT_EQ(0, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::ManufacturerCode)); + NAME partiallyMatchingNAME(0); + partiallyMatchingNAME.set_function_code(static_cast(isobus::NAME::Function::FuelSystem)); + partiallyMatchingNAME.set_ecu_instance(1); + + EXPECT_FALSE(TestPartner->check_matches_name(partiallyMatchingNAME)); + isobus::NAME::NAMEParameters filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; std::uint32_t filterValue = 0; From 5e0d002d434ffc0da41c5e46394c21814e59522f Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 17:21:26 +0200 Subject: [PATCH 11/13] Implement wrong PGN callback in tests Added a callback for wrong PGN messages and tested its invocation. --- test/tc_client_tests.cpp | 45 +++++++++++++--------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/test/tc_client_tests.cpp b/test/tc_client_tests.cpp index 71e184588..0e588b3ab 100644 --- a/test/tc_client_tests.cpp +++ b/test/tc_client_tests.cpp @@ -1408,6 +1408,13 @@ bool value_command_callback(std::uint16_t element, return true; } +static bool wrongPGNCallbackCalled = false; + +void wrong_pgn_callback(const CANMessage &, void *) +{ + wrongPGNCallbackCalled = true; +} + TEST_F(TaskControllerClientTest, CallbackTests) { VirtualCANPlugin serverTC; @@ -1440,37 +1447,6 @@ TEST_F(TaskControllerClientTest, CallbackTests) interfaceUnderTest.add_value_command_callback(value_command_callback, nullptr); interfaceUnderTest.test_wrapper_set_state(TaskControllerClient::StateMachineState::Connected); - valueRequested = false; - valueCommanded = false; - requestedDDI = 0; - commandedDDI = 0; - requestedElement = 0; - commandedElement = 0; - commandedValue = 0; - - // A message with a different PGN must not invoke the registered TC callbacks. - testFrame.identifier = 0x18CC86F7; - testFrame.data[0] = 0x82; - testFrame.data[1] = 0x04; - testFrame.data[2] = 0x12; - testFrame.data[3] = 0x34; - testFrame.data[4] = 0x00; - testFrame.data[5] = 0x00; - testFrame.data[6] = 0x00; - testFrame.data[7] = 0x00; - - CANNetworkManager::CANNetwork.process_receive_can_message_frame(testFrame); - CANNetworkManager::CANNetwork.update(); - interfaceUnderTest.update(); - - EXPECT_FALSE(valueRequested); - EXPECT_FALSE(valueCommanded); - EXPECT_EQ(0, requestedDDI); - EXPECT_EQ(0, requestedElement); - EXPECT_EQ(0, commandedDDI); - EXPECT_EQ(0, commandedElement); - EXPECT_EQ(0, commandedValue); - // Status message testFrame.identifier = 0x18CBFFF7; testFrame.data[0] = 0xFE; // Status mux @@ -1625,6 +1601,10 @@ TEST_F(TaskControllerClientTest, CallbackTests) // Test time interval measurement commands interfaceUnderTest.add_request_value_callback(request_value_command_callback, nullptr); interfaceUnderTest.add_value_command_callback(value_command_callback, nullptr); + + wrongPGNCallbackCalled = false; + TestPartnerTC->add_parameter_group_number_callback(0xCC00, wrong_pgn_callback, nullptr, internalECU); + // Create a command testFrame.identifier = 0x18CB86F7; testFrame.data[0] = 0xA4; @@ -1667,6 +1647,7 @@ TEST_F(TaskControllerClientTest, CallbackTests) CANNetworkManager::CANNetwork.update(); interfaceUnderTest.update(); + EXPECT_FALSE(wrongPGNCallbackCalled); EXPECT_EQ(true, valueRequested); EXPECT_EQ(requestedDDI, 0x3919); @@ -1775,6 +1756,8 @@ TEST_F(TaskControllerClientTest, CallbackTests) EXPECT_EQ(requestedDDI, 0x03); EXPECT_EQ(requestedElement, 0x4); + TestPartnerTC->remove_parameter_group_number_callback(0xCC00, wrong_pgn_callback, nullptr, internalECU); + CANHardwareInterface::stop(); CANNetworkManager::CANNetwork.deactivate_control_function(TestPartnerTC); From 858d2fd1c07ea2e886651995736055b055f51ed8 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Tue, 4 Aug 2026 17:22:19 +0200 Subject: [PATCH 12/13] Add comment for re-running workflow #4 --- .../mull-can-partnered-control-function-all-unit-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml index 10034d2b1..ad8f4ce6f 100644 --- a/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml +++ b/.github/workflows/mull-can-partnered-control-function-all-unit-tests.yml @@ -3,6 +3,7 @@ name: Mull - CAN PARTNERED CONTROL FUNCTION - All Tests #Re-run #1 #Re-run #2 #Re-run #3 +#Re-run #4 on: workflow_dispatch: From 405cb06d5b5796396a5daae81d6d2cd387f36c49 Mon Sep 17 00:00:00 2001 From: Pissaro007 Date: Wed, 5 Aug 2026 08:23:11 +0000 Subject: [PATCH 13/13] style: apply clang-format to mutation tests --- test/core_network_management_tests.cpp | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/core_network_management_tests.cpp b/test/core_network_management_tests.cpp index 3afe3479e..921dd0a81 100644 --- a/test/core_network_management_tests.cpp +++ b/test/core_network_management_tests.cpp @@ -359,47 +359,47 @@ TEST_F(CoreTest, SimilarControlFunctions) const isobus::NAMEFilter filterFuelSystem(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::FuelSystem)); const isobus::NAMEFilter filterEcuInstance(isobus::NAME::NAMEParameters::EcuInstance, 0); - const std::vector nameFilters = {filterFuelSystem, filterEcuInstance}; - + const std::vector nameFilters = { filterFuelSystem, filterEcuInstance }; + auto TestPartner = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, nameFilters); // Quick test to make sure partner is working EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::FunctionCode)); EXPECT_EQ(1, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::EcuInstance)); EXPECT_EQ(0, TestPartner->get_number_name_filters_with_parameter_type(isobus::NAME::NAMEParameters::ManufacturerCode)); - + NAME partiallyMatchingNAME(0); partiallyMatchingNAME.set_function_code(static_cast(isobus::NAME::Function::FuelSystem)); partiallyMatchingNAME.set_ecu_instance(1); - + EXPECT_FALSE(TestPartner->check_matches_name(partiallyMatchingNAME)); - + isobus::NAME::NAMEParameters filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; std::uint32_t filterValue = 0; - + EXPECT_TRUE(TestPartner->get_name_filter_parameter(0, filterParameter, filterValue)); EXPECT_EQ(isobus::NAME::NAMEParameters::FunctionCode, filterParameter); EXPECT_EQ(static_cast(isobus::NAME::Function::FuelSystem), filterValue); - + filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; filterValue = 0; - + EXPECT_TRUE(TestPartner->get_name_filter_parameter(1, filterParameter, filterValue)); EXPECT_EQ(isobus::NAME::NAMEParameters::EcuInstance, filterParameter); EXPECT_EQ(0, filterValue); - + filterParameter = isobus::NAME::NAMEParameters::IdentityNumber; filterValue = 0xFFFFFFFF; - + EXPECT_FALSE(TestPartner->get_name_filter_parameter(TestPartner->get_number_name_filters(), filterParameter, filterValue)); EXPECT_EQ(isobus::NAME::NAMEParameters::IdentityNumber, filterParameter); EXPECT_EQ(0xFFFFFFFF, filterValue); const std::vector emptyNameFilters; auto partnerWithoutFilters = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, emptyNameFilters); - + EXPECT_EQ(0, partnerWithoutFilters->get_number_name_filters()); - EXPECT_FALSE(partnerWithoutFilters->check_matches_name(NAME(0))); - + EXPECT_FALSE(partnerWithoutFilters->check_matches_name(NAME(0))); + // Request the address claim PGN CANNetworkManager::CANNetwork.process_receive_can_message_frame(test_helpers::create_message_frame_pgn_request( 0xEE00, // Address Claim PGN @@ -459,7 +459,7 @@ TEST_F(CoreTest, SimilarControlFunctions) // Partner should never change EXPECT_EQ(TestPartner->get_NAME().get_full_name(), 0xa0000F000425e9f8); - - CANNetworkManager::CANNetwork.deactivate_control_function(partnerWithoutFilters); + + CANNetworkManager::CANNetwork.deactivate_control_function(partnerWithoutFilters); CANNetworkManager::CANNetwork.deactivate_control_function(TestPartner); }