Skip to content

Commit 69a48fc

Browse files
committed
tests: Fix crosscheck with olpc_cjson and cjson
First, enhance arbitrary_json to take regex for key space, so we can exclude e.g. control characters. When we're dealing with ASCII_ALPHANUMERIC, we can assert exact equivalency. Add a test case that (for cjson) also takes arbitrary keys, but compares via serde_json::Value (which automatically sorts) because we know the object ordering will be different. Signed-off-by: Colin Walters <[email protected]>
1 parent c65a811 commit 69a48fc

File tree

1 file changed

+34
-16
lines changed

1 file changed

+34
-16
lines changed

src/lib.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,6 @@ mod tests {
395395
use std::{cmp::Ordering, io::Result};
396396

397397
use proptest::prelude::*;
398-
use serde::Serialize;
399398
use serde_json::Number;
400399
use sha2::{Digest, Sha256};
401400
use similar_asserts::assert_eq;
@@ -554,24 +553,23 @@ mod tests {
554553
assert_eq!(&buf, &expected);
555554
}
556555

557-
fn arbitrary_json() -> impl Strategy<Value = serde_json::Value> {
556+
fn arbitrary_json(keyspace: &'static str) -> impl Strategy<Value = serde_json::Value> {
558557
use serde_json::Value;
559-
const S: &str = ".*";
560558
let leaf = prop_oneof![
561559
Just(Value::Null),
562560
any::<u32>().prop_map(|v| Value::Number(Number::from_u128(v.into()).unwrap())),
563561
any::<bool>().prop_map(Value::Bool),
564-
S.prop_map(Value::String),
562+
keyspace.prop_map(Value::String),
565563
];
566564
leaf.prop_recursive(
567565
8, // 8 levels deep
568566
256, // Shoot for maximum size of 256 nodes
569567
10, // We put up to 10 items per collection
570-
|inner| {
568+
move |inner| {
571569
prop_oneof![
572570
// Take the inner strategy and make the two recursive cases.
573571
prop::collection::vec(inner.clone(), 0..10).prop_map(Value::Array),
574-
prop::collection::hash_map(S, inner, 0..10)
572+
prop::collection::hash_map(keyspace, inner, 0..10)
575573
.prop_map(|v| { v.into_iter().collect() }),
576574
]
577575
},
@@ -580,7 +578,7 @@ mod tests {
580578

581579
proptest! {
582580
#[test]
583-
fn roundtrip_rfc8785(v in arbitrary_json()) {
581+
fn roundtrip_rfc8785(v in arbitrary_json(".*")) {
584582
let buf = encode!(&v).unwrap();
585583
let v2: serde_json::Value = serde_json::from_slice(&buf)
586584
.map_err(|e| format!("Failed to parse {v:?} -> {}: {e}", String::from_utf8_lossy(&buf))).unwrap();
@@ -677,24 +675,44 @@ mod tests {
677675
Ok(())
678676
}
679677

678+
// Regex that excludes basically everything except printable ASCII
679+
// because we know that e.g. olpc-cjson bombs on control characters,
680+
// and also because it does NFC orering that will cause non-equivalency
681+
// for some whitespace etc.
682+
const ASCII_ALPHANUMERIC: &str = r"[a-zA-Z0-9]*";
683+
680684
proptest! {
685+
// Verify strict equivalency with printable ASCII only keys
681686
#[test]
682-
#[ignore = "broken (control char)"]
683-
fn crosscheck_olpc_cjson(v in arbitrary_json()) {
687+
fn crosscheck_olpc_cjson_ascii(v in arbitrary_json(ASCII_ALPHANUMERIC)) {
688+
let canon_json = String::from_utf8(encode!(&v).unwrap()).unwrap();
684689
let mut olpc_cjson_serialized = Vec::new();
685690
let mut ser = serde_json::Serializer::with_formatter(&mut olpc_cjson_serialized, olpc_cjson::CanonicalFormatter::new());
686-
prop_assume!(v.serialize(&mut ser).is_ok());
687-
688-
let buf = encode!(&v).unwrap();
689-
assert_eq!(buf, olpc_cjson_serialized);
691+
v.serialize(&mut ser).unwrap();
692+
assert_eq!(canon_json, String::from_utf8(olpc_cjson_serialized).unwrap());
690693
}
691694
}
692695

693696
proptest! {
697+
// Verify strict equivalency with printable ASCII only keys
694698
#[test]
695-
#[ignore = "broken (ordering)"]
696-
fn crosscheck_cjson(v in arbitrary_json()) {
697-
assert_eq!(v.to_canon_json_vec().unwrap(), cjson::to_vec(&v).unwrap());
699+
fn crosscheck_cjson_ascii(v in arbitrary_json(ASCII_ALPHANUMERIC)) {
700+
let canon_json = String::from_utf8(encode!(&v).unwrap()).unwrap();
701+
let cjson = String::from_utf8(cjson::to_vec(&v).unwrap()).unwrap();
702+
assert_eq!(canon_json, cjson);
703+
}
704+
705+
// Verify equivalency (after sorting) with non-ASCII keys
706+
#[test]
707+
fn crosscheck_cjson(v in arbitrary_json(".*")) {
708+
let buf = encode!(&v).unwrap();
709+
let self_reparsed = serde_json::from_slice::<serde_json::Value>(&buf).unwrap();
710+
let buf = cjson::to_vec(&v).unwrap();
711+
let cjson_reparsed = serde_json::from_slice::<serde_json::Value>(&buf).unwrap();
712+
// As above with olpc-cjson, this relies on the fact that serde_json
713+
// sorts object keys by default.
714+
assert_eq!(self_reparsed, v);
715+
assert_eq!(cjson_reparsed, v);
698716
}
699717
}
700718
}

0 commit comments

Comments
 (0)