This is an automated email from the ASF dual-hosted git repository.
zyxxoo pushed a commit to branch refactor/rust-rewrite-design
in repository https://gitbox.apache.org/repos/asf/hugegraph.git
The following commit(s) were added to refs/heads/refactor/rust-rewrite-design
by this push:
new b32967d03 test(rust): add Java Rust contract differential oracle
b32967d03 is described below
commit b32967d0394479b3177796a0dccc69fff11136b0
Author: vaughn <[email protected]>
AuthorDate: Sun Sep 13 17:11:23 2026 +0800
test(rust): add Java Rust contract differential oracle
---
tools/raft-linearizability/README.md | 9 +++++++
tools/raft-linearizability/compare_traces.py | 38 ++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/tools/raft-linearizability/README.md
b/tools/raft-linearizability/README.md
index 07b834afa..3ea5c4714 100644
--- a/tools/raft-linearizability/README.md
+++ b/tools/raft-linearizability/README.md
@@ -26,3 +26,12 @@ For HugeGraph capture, map a committed vertex mutation to
`write` and a vertex
lookup to `read`, using the canonical vertex identifier as the register key.
Record HTTP send/receive monotonic times plus status and body hash in metadata;
the checker evaluates ordering and values only.
+
+## Java/Rust contract replay
+
+`compare_traces.py` is a dependency-free differential oracle. Give it Java and
Rust replay traces (arrays or `{ "operations": [...] }`). It compares only
`op`, `key`, `value`, and `result`, ignoring timestamps and implementation
metadata.
+
+```bash
+python3 tools/raft-linearizability/compare_traces.py java.json rust.json
+```
+Exit 0 means normalized traces are identical; exit 1 reports divergence.
diff --git a/tools/raft-linearizability/compare_traces.py
b/tools/raft-linearizability/compare_traces.py
new file mode 100644
index 000000000..3ff78585a
--- /dev/null
+++ b/tools/raft-linearizability/compare_traces.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+"""Compare Java and Rust replay traces using a stable, independent oracle.
+
+Each input is a JSON array (or an object containing ``operations``). Records
+are compared after selecting the contract fields ``op``, ``key``, ``value``
+and ``result``; transport timestamps and implementation metadata are ignored.
+"""
+import argparse, json, sys
+
+FIELDS = ("op", "key", "value", "result")
+
+def load(path):
+ with open(path, encoding="utf-8") as f:
+ data = json.load(f)
+ if isinstance(data, dict):
+ data = data.get("operations")
+ if not isinstance(data, list):
+ raise ValueError("trace must be an array or {operations: array}")
+ return [{k: row.get(k) for k in FIELDS} for row in data]
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument("java_trace"); ap.add_argument("rust_trace")
+ args = ap.parse_args()
+ try:
+ java, rust = load(args.java_trace), load(args.rust_trace)
+ except (OSError, ValueError, json.JSONDecodeError, AttributeError) as e:
+ print(json.dumps({"equal": False, "error": str(e)})); return 2
+ equal = java == rust
+ out = {"equal": equal, "java_count": len(java), "rust_count": len(rust)}
+ if not equal:
+ out["first_difference"] = next(({"index": i, "java": j, "rust": r}
+ for i, (j, r) in enumerate(zip(java, rust)) if j != r), None)
+ print(json.dumps(out, sort_keys=True))
+ return 0 if equal else 1
+
+if __name__ == "__main__":
+ sys.exit(main())