diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..6223e2b --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +RELEASE_TYPE: patch + +Add support for @reproduce_failure and print blob. diff --git a/include/hegel/json.h b/include/hegel/json.h index 4bc201a..ca5863e 100644 --- a/include/hegel/json.h +++ b/include/hegel/json.h @@ -47,6 +47,7 @@ namespace hegel::internal::json { bool is_array() const noexcept; bool is_object() const noexcept; + json_raw_ref& operator=(const std::string& other); json_raw_ref& operator=(const size_t& other); json_raw_ref& operator=(const double& other); json_raw_ref& operator=(const std::nullptr_t& other); diff --git a/include/hegel/options.h b/include/hegel/options.h index ded3534..e4900c4 100644 --- a/include/hegel/options.h +++ b/include/hegel/options.h @@ -53,5 +53,9 @@ namespace hegel::options { std::optional hegel_path; std::optional seed; + + std::optional failure_blob; + + bool print_blob; }; } // namespace hegel::options diff --git a/nix/flake.lock b/nix/flake.lock index 61b3c8c..a597f37 100644 --- a/nix/flake.lock +++ b/nix/flake.lock @@ -38,11 +38,11 @@ }, "locked": { "dir": "nix", - "lastModified": 1773172740, - "narHash": "sha256-gzW4T5EHKw5ss+YiwWQf9xitKYejA0dhVirzHSiRfnA=", + "lastModified": 1774970430, + "narHash": "sha256-P0sAAgeL9ziFoUyCLBAEscVz1o2CcZCS+ynmn6i9GD8=", "ref": "refs/heads/main", - "rev": "f02f2b383324e7eba04b33f97a46c0ba64047eb7", - "revCount": 343, + "rev": "73bcda3c91c2f7391717cde30ce75f207bd13a10", + "revCount": 429, "type": "git", "url": "https://github.com/hegeldev/hegel-core.git" }, @@ -54,11 +54,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1771369470, - "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", + "lastModified": 1772624091, + "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0182a361324364ae3f436a63005877674cf45efb", + "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", "type": "github" }, "original": { @@ -100,11 +100,11 @@ ] }, "locked": { - "lastModified": 1763662255, - "narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=", + "lastModified": 1772555609, + "narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=", "owner": "pyproject-nix", "repo": "build-system-pkgs", - "rev": "042904167604c681a090c07eb6967b4dd4dae88c", + "rev": "c37f66a953535c394244888598947679af231863", "type": "github" }, "original": { @@ -121,11 +121,11 @@ ] }, "locked": { - "lastModified": 1769936401, - "narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=", + "lastModified": 1771518446, + "narHash": "sha256-nFJSfD89vWTu92KyuJWDoTQJuoDuddkJV3TlOl1cOic=", "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812", + "rev": "eb204c6b3335698dec6c7fc1da0ebc3c6df05937", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1770770348, - "narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=", + "lastModified": 1772545244, + "narHash": "sha256-Ys+5UMOqp2kRvnSjyBcvGnjOhkIXB88On1ZcAstz1vY=", "owner": "pyproject-nix", "repo": "uv2nix", - "rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0", + "rev": "482aba340ded40ef557d331315f227d5eba84ced", "type": "github" }, "original": { diff --git a/nix/flake.nix b/nix/flake.nix index c1c92fb..b58a7af 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -4,7 +4,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"; - hegel.url = "git+https://github.com/hegeldev/hegel-core.git"; + hegel.url = "git+https://github.com/hegeldev/hegel-core.git?dir=nix"; }; outputs = @@ -126,6 +126,8 @@ inputsFrom = [ self.packages.${system}.default ]; packages = [ pkgs.clang-tools + pkgs.uv + pkgs.just ]; }; } diff --git a/src/hegel.cpp b/src/hegel.cpp index dd9a38c..69ca07e 100644 --- a/src/hegel.cpp +++ b/src/hegel.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -116,10 +117,20 @@ namespace hegel { } else { run_test_msg["seed"] = nullptr; } + + if (options.failure_blob.has_value()) { + run_test_msg["failure_blob"] = options.failure_blob.value(); + } else { + run_test_msg["failure_blob"] = nullptr; + } + conn.request(0, run_test_msg); // Event loop on test channel bool test_passed = true; + std::vector failure_blobs; + std::string health_check_msg; + std::string flaky_test_msg; int final_replays_remaining = 0; bool done = false; while (!done) { @@ -194,8 +205,18 @@ namespace hegel { if (payload.contains("results")) { auto& results = ImplUtil::raw(payload["results"]); test_passed = results.value("passed", true); + health_check_msg = + results.value("health_check_failure", ""); + flaky_test_msg = results.value("flaky", ""); final_replays_remaining = results.value("interesting_test_cases", 0); + for (const auto& blob : results["failure_blobs"]) { + auto byte_sequence = blob.get_binary(); + std::string failure_blob_string( + reinterpret_cast(byte_sequence.data()), + byte_sequence.size()); + failure_blobs.push_back(failure_blob_string); + } } if (final_replays_remaining <= 0) { done = true; @@ -210,6 +231,21 @@ namespace hegel { waitpid(child_pid, &status, 0); std::filesystem::remove_all(temp_dir); + if (test_passed && options.failure_blob.has_value()) { + throw std::runtime_error("Failure blob did not cause a failure"); + } + if (options.print_blob && !failure_blobs.empty()) { + std::cerr << "Failure blobs for reproduction:\n"; + for (const auto& blob : failure_blobs) { + std::cerr << blob << "\n"; + } + } + if (health_check_msg.length() > 0) { + std::cerr << health_check_msg << "\n"; + } + if (flaky_test_msg.length() > 0) { + std::cerr << flaky_test_msg << "\n"; + } if (!test_passed) { throw std::runtime_error("Hegel test failed"); } diff --git a/src/json.cpp b/src/json.cpp index 70950d8..29f6c0d 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -142,6 +142,10 @@ namespace hegel::internal::json { size_t json_raw_ref::size() const noexcept { return ref->data.size(); } + json_raw_ref& json_raw_ref::operator=(const std::string& other) { + ref->data = other; + return *this; + } json_raw_ref& json_raw_ref::operator=(const size_t& other) { ref->data = other; return *this;