From 289289aa19a999b2f8d0f8ab92cf7b201f26369e Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 5 Apr 2026 12:43:45 +0200 Subject: [PATCH 1/4] tests: improve assertion output to better show whitespace differences --- tests/lib/include/patch/test.h | 59 ++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/tests/lib/include/patch/test.h b/tests/lib/include/patch/test.h index a0c135b..4e7a55b 100644 --- a/tests/lib/include/patch/test.h +++ b/tests/lib/include/patch/test.h @@ -4,8 +4,10 @@ #pragma once #include +#include #include #include +#include #include #include #include @@ -28,6 +30,54 @@ void test_error_format(const T& a) std::cerr << static_cast::type>(a); } +inline std::string escaped_string_for_test_output(const std::string& value) +{ + std::ostringstream out; + out << '"'; + + for (unsigned char c : value) { + switch (c) { + case '\\': + out << "\\\\"; + break; + case '"': + out << "\\\""; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + if (c >= 0x20 && c < 0x7f) { + out << static_cast(c); + } else { + out << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(c) << std::dec; + } + break; + } + } + + out << '"'; + return out.str(); +} + +template +void maybe_print_string_diff_details(const A&, const B&) +{ +} + +inline void maybe_print_string_diff_details(const std::string& lhs, const std::string& rhs) +{ + std::cerr << " (lhs len=" << lhs.size() << ", rhs len=" << rhs.size() << ")"; + std::cerr << "\n lhs escaped: " << ::Patch::escaped_string_for_test_output(lhs); + std::cerr << "\n rhs escaped: " << ::Patch::escaped_string_for_test_output(rhs); +} + class test_assertion_failure : public std::runtime_error { public: using runtime_error::runtime_error; @@ -66,12 +116,15 @@ class test_assertion_failure : public std::runtime_error { #define EXPECT_EQ(lhs, rhs) \ do { \ /* NOLINTNEXTLINE(readability-container-size-empty) */ \ - if ((lhs) != (rhs)) { \ + const auto patch_lhs = (lhs); \ + const auto patch_rhs = (rhs); \ + if (patch_lhs != patch_rhs) { \ std::cerr << "FAIL: " __FILE__ << ":" << __LINE__ << ": "; \ std::cerr << "'EXPECT_EQ(" << #lhs << ", " << #rhs << ")' failed, "; \ - Patch::test_error_format(lhs); \ + Patch::test_error_format(patch_lhs); \ std::cerr << " != "; \ - Patch::test_error_format(rhs); \ + Patch::test_error_format(patch_rhs); \ + Patch::maybe_print_string_diff_details(patch_lhs, patch_rhs); \ std::cerr << '\n'; \ throw Patch::test_assertion_failure("Test failed"); \ } \ From 7e62e95039ff47323b603e457f9c476b2410b0c3 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 5 Apr 2026 12:34:11 +0200 Subject: [PATCH 2/4] meta: bump MSVC upload artifact version --- .github/workflows/msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 6246019..2407585 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -46,7 +46,7 @@ jobs: # Upload SARIF file as an Artifact to download and view - name: Upload SARIF as an Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: sarif-file path: ${{ steps.run-analysis.outputs.sarif }} From 56ea674da6f703a21369528f8a44be7f8f5c830b Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 5 Apr 2026 12:34:46 +0200 Subject: [PATCH 3/4] meta: remove sonar github action --- .github/workflows/sonar.yml | 59 ------------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 .github/workflows/sonar.yml diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml deleted file mode 100644 index 383c100..0000000 --- a/.github/workflows/sonar.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: sonar cloud static analysis -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened] -jobs: - analysis: - name: static analysis - runs-on: ubuntu-latest - env: - SONAR_SCANNER_VERSION: 4.7.0.2747 - SONAR_SERVER_URL: "https://sonarcloud.io" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: cache sonar cloud packages - uses: actions/cache@v1 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: set up sonar-scanner - env: - SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip - run: | - mkdir -p $HOME/.sonar - curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ - echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH - - name: configure sonar-scanner - run: | - echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH - echo "sonar.projectKey=shannonbooth_patch" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.projectVersion=${{ github.sha }}" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.organization=shannonbooth" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.cfamily.compile-commands=${{ github.workspace }}/build/compile_commands.json" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.cfamily.threads=2" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.cfamily.cache.enabled=false" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.cfamily.reportingCppStandardOverride=c++11" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.host.url=${{ env.SONAR_SERVER_URL }}" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.sources=src,include" >> ${{ github.workspace }}/sonar-project.properties - echo "sonar.tests=tests" >> ${{ github.workspace }}/sonar-project.properties - - name: configure - run: cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=on -DBUILD_TESTING=on -S . -B build - - name: compile - run: cmake --build build -j2 - - name: run sonar-scanner, upload results - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: sonar-scanner - From 5c30d167eb27b871f78cce39e7684962dd2ebc6e Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 5 Apr 2026 12:26:58 +0200 Subject: [PATCH 4/4] applier: handle empty input correctly when closing --ifdef hunks --- src/applier.cpp | 8 +++++--- tests/test_defines.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/applier.cpp b/src/applier.cpp index e346a50..660ca13 100644 --- a/src/applier.cpp +++ b/src/applier.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause -// Copyright 2022-2024 Shannon Booth +// Copyright 2022-2026 Shannon Booth #include #include @@ -117,8 +117,10 @@ static LineNumber write_define_hunk(LineWriter& output, const Hunk& hunk, const } } - if (define_state != DefineState::Outside) - output << "#endif" << lines.at(lines.size() - 1).newline; + if (define_state != DefineState::Outside) { + const auto newline = lines.empty() ? NewLine::LF : lines.at(lines.size() - 1).newline; + output << "#endif" << newline; + } return static_cast(line_number); } diff --git a/tests/test_defines.cpp b/tests/test_defines.cpp index 09359c3..35ea25a 100644 --- a/tests/test_defines.cpp +++ b/tests/test_defines.cpp @@ -314,3 +314,28 @@ int main() } )"); } + +PATCH_TEST(defines_add_file_from_empty_input) +{ + { + Patch::File file("diff.patch", std::ios_base::out); + file << R"(--- /dev/null 2022-01-30 13:57:31.173528027 +1300 ++++ added.cpp 2022-01-30 13:57:36.321216497 +1300 +@@ -0,0 +1,3 @@ ++int main() ++{ ++} +)"; + } + + Process process(patch_path, { patch_path, "-i", "diff.patch", "--ifdef", "TEST_PATCH", nullptr }); + EXPECT_EQ(process.stdout_data(), "patching file added.cpp\n"); + EXPECT_EQ(process.stderr_data(), ""); + EXPECT_EQ(process.return_code(), 0); + EXPECT_FILE_EQ("added.cpp", R"(#ifdef TEST_PATCH +int main() +{ +} +#endif +)"); +}