From a40e1e7692cbf3dc6f6e6a8c7229264d2e25fbec Mon Sep 17 00:00:00 2001 From: John Shumway Date: Fri, 19 Sep 2025 15:40:24 +0000 Subject: [PATCH] Add testing utils. --- experimental/builder/test/testing_utils.cpp | 104 ++++++++++++++++++++ experimental/builder/test/testing_utils.hpp | 14 +++ 2 files changed, 118 insertions(+) create mode 100644 experimental/builder/test/testing_utils.cpp create mode 100644 experimental/builder/test/testing_utils.hpp diff --git a/experimental/builder/test/testing_utils.cpp b/experimental/builder/test/testing_utils.cpp new file mode 100644 index 0000000000..cd1cc8a173 --- /dev/null +++ b/experimental/builder/test/testing_utils.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +namespace ck_tile::test { + +// Copilot-generated diff function, seems to work great. +// TODO: Study this, maybe add unit tests? +// +// This implementation is based on the Wagner-Fischer algorithm to find the +// optimal edit script. +std::string inlineDiff(const std::string& actual, const std::string& expected) +{ + const size_t n = expected.length(); + const size_t m = actual.length(); + + std::vector> dp(n + 1, std::vector(m + 1)); + + for(size_t i = 0; i <= n; ++i) + { + dp[i][0] = i; + } + for(size_t j = 0; j <= m; ++j) + { + dp[0][j] = j; + } + + for(size_t i = 1; i <= n; ++i) + { + for(size_t j = 1; j <= m; ++j) + { + int cost = (expected[i - 1] == actual[j - 1]) ? 0 : 1; + dp[i][j] = std::min({dp[i - 1][j] + 1, // Deletion + dp[i][j - 1] + 1, // Insertion + dp[i - 1][j - 1] + cost}); // Substitution/Match + } + } + + std::ostringstream diff; + std::string expected_diff, actual_diff; + bool in_diff = false; + + size_t i = n, j = m; + while(i > 0 || j > 0) + { + int cost = (i > 0 && j > 0 && expected[i - 1] == actual[j - 1]) ? 0 : 1; + + if(i > 0 && j > 0 && dp[i][j] == dp[i - 1][j - 1] + cost) + { + if(cost == 0) + { + if(in_diff) + { + std::reverse(expected_diff.begin(), expected_diff.end()); + std::reverse(actual_diff.begin(), actual_diff.end()); + diff << "]" << expected_diff << "|" << actual_diff << "["; + expected_diff.clear(); + actual_diff.clear(); + in_diff = false; + } + diff << expected[i - 1]; + } + else + { + in_diff = true; + expected_diff += expected[i - 1]; + actual_diff += actual[j - 1]; + } + --i; + --j; + } + else if(j > 0 && dp[i][j] == dp[i][j - 1] + 1) + { + in_diff = true; + actual_diff += actual[j - 1]; + --j; + } + else if(i > 0 && dp[i][j] == dp[i - 1][j] + 1) + { + in_diff = true; + expected_diff += expected[i - 1]; + --i; + } + } + + if(in_diff) + { + std::reverse(expected_diff.begin(), expected_diff.end()); + std::reverse(actual_diff.begin(), actual_diff.end()); + diff << "]" << expected_diff << "|" << actual_diff << "[]"; + } + + std::string result = diff.str(); + std::reverse(result.begin(), result.end()); + return result; +} + +std::string formatInlineDiff(const std::string& actual, const std::string& expected) +{ + return std::string("Inline diff: \"") + inlineDiff(actual, expected) + "\""; +} + +} // namespace ck_tile::test diff --git a/experimental/builder/test/testing_utils.hpp b/experimental/builder/test/testing_utils.hpp new file mode 100644 index 0000000000..ba0c7f72f4 --- /dev/null +++ b/experimental/builder/test/testing_utils.hpp @@ -0,0 +1,14 @@ +#include +#include +#include + +namespace ck_tile::test { + +// Returns a string highlighting differences between actual and expected. +// Differences are enclosed in brackets with actual and expected parts separated by '|'. +std::string inlineDiff(const std::string& actual, const std::string& expected); + +// A convenience alias for inlineDiff to improve readability in test assertions. +std::string formatInlineDiff(const std::string& actual, const std::string& expected); + +} // namespace ck_tile::testing