OXYGENHUB

Property tests: the order of changes never changes the state 37c299cec358

Random edit sessions in two or three diverging clones (inserts, deletes,
replacements, new and removed files, so most cases conflict) are rebuilt
in shuffled orders through State::add_unchecked and in random dependency
orders through State::add. Every order must validate and give identical
checkpoint bytes, heads and materialized trees; the checkpoint must
restore the same state; and pushing the clones in a random order must
give a fresh clone that state too. 32 cases per run, via proptest.
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-09-27 23:08 UTC
Signature
✔ signed by Andy McCall
Hash
37c299cec358d02a66552773afc4503b5c3b271ce09deea42475aed98cfea061
Depends on
135212de5370 2b5ee371060e 750251b21a96 e993a086c13f ff0af1ee299d
Depended on by
2cd40ea1c04c f874dc8659c2 8d80356371c5 e7ddd552b619 1d360c91a289
In bookmarks
agents-in-readme bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
In tags
v0.1.0 v0.10.0 v0.11.0 v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
Edits
22 atoms, 3 tree operations, 4 files

Browse the tree at this change

added crates/oxy-core/tests/commutativity.proptest-regressions +7 −0
1 +
# Seeds for failure cases proptest has generated in the past. It is
2 +
# automatically read and these particular cases re-run before any
3 +
# novel cases are generated.
4 +
#
5 +
# It is recommended to check this file in to source control so that
6 +
# everyone who runs the test benefits from these saved cases.
7 +
cc 150dc6c3551a31ba699711e0bebca2b54b29cbf003a0e8e9035d978f4bcfd65d # shrinks to sessions = [[[Insert { file: 0, at: 82, word: 2 }]], [[Insert { file: 3, at: 15216962264731610119, word: 0 }, Replace { file: 1, at: 1617507299102404918, word: 0 }, Delete { file: 1, at: 6875205767748926991 }, Delete { file: 0, at: 4285533219516317091 }], [Delete { file: 0, at: 14950834157168413373 }, Delete { file: 3, at: 7384109036823047037 }, Delete { file: 0, at: 15334878224541300421 }, Insert { file: 3, at: 6722035807729590280, word: 2 }], [Insert { file: 3, at: 5966679767442278895, word: 2 }, Replace { file: 1, at: 17587470115659482894, word: 0 }]]], seed = 2287439951785091165
added crates/oxy-core/tests/commutativity.rs +252 −0
1 +
//! Property tests for spec §7.3: a state is a function of its set of
2 +
//! changes, never of the order they arrived in.
3 +
//!
4 +
//! Each case seeds a repository with a few files, lets two or three clones
5 +
//! diverge through random edit sessions (inserts, deletes, replacements,
6 +
//! new and removed files, so conflicts happen too), then rebuilds the
7 +
//! union of every clone's changes in many orders: arbitrary orders through
8 +
//! `State::add_unchecked` and random dependency-respecting orders through
9 +
//! `State::add`. Every order must validate and give byte-identical
10 +
//! checkpoints and identical materialized trees. Finally the clones push
11 +
//! to one origin in a random order and a fresh clone must see that state,
12 +
//! and a checkpoint of the state must restore it exactly.
13 +
14 +
use oxy_core::objects::Change;
15 +
use oxy_core::repo::Repo;
16 +
use oxy_core::state::State;
17 +
use proptest::prelude::*;
18 +
use rand::rngs::StdRng;
19 +
use rand::seq::SliceRandom;
20 +
use rand::{Rng, SeedableRng};
21 +
use std::collections::{BTreeMap, BTreeSet};
22 +
use std::path::Path;
23 +
24 +
/// Point `$OXY_HOME` at a throwaway directory once per test process.
25 +
fn hermetic() {
26 +
    static ONCE: std::sync::Once = std::sync::Once::new();
27 +
    static DIR: std::sync::OnceLock<tempfile::TempDir> = std::sync::OnceLock::new();
28 +
    ONCE.call_once(|| {
29 +
        let d = tempfile::tempdir().unwrap();
30 +
        std::env::set_var("OXY_HOME", d.path());
31 +
        DIR.set(d).ok();
32 +
    });
33 +
}
34 +
35 +
/// Files the edits address; the first `SEEDED` exist in the base state.
36 +
const FILES: [&str; 4] = ["a.txt", "b.txt", "dir/c.txt", "d.txt"];
37 +
const SEEDED: usize = 3;
38 +
39 +
/// A few short lines, so identical lines are common and the diff has
40 +
/// to choose between equally good alignments.
41 +
const WORDS: [&str; 5] = ["alpha\n", "beta\n", "gamma\n", "delta\n", "alpha\n"];
42 +
43 +
#[derive(Clone, Debug)]
44 +
enum Edit {
45 +
    Insert { file: usize, at: usize, word: usize },
46 +
    Delete { file: usize, at: usize },
47 +
    Replace { file: usize, at: usize, word: usize },
48 +
    Remove { file: usize },
49 +
}
50 +
51 +
fn edit() -> impl Strategy<Value = Edit> {
52 +
    let file = 0..FILES.len();
53 +
    prop_oneof![
54 +
        4 => (file.clone(), any::<usize>(), 0..WORDS.len()).prop_map(|(file, at, word)| Edit::Insert { file, at, word }),
55 +
        3 => (file.clone(), any::<usize>()).prop_map(|(file, at)| Edit::Delete { file, at }),
56 +
        3 => (file.clone(), any::<usize>(), 0..WORDS.len()).prop_map(|(file, at, word)| Edit::Replace { file, at, word }),
57 +
        1 => file.prop_map(|file| Edit::Remove { file }),
58 +
    ]
59 +
}
60 +
61 +
/// One clone's session: a few commits of a few edits each.
62 +
fn session() -> impl Strategy<Value = Vec<Vec<Edit>>> {
63 +
    prop::collection::vec(prop::collection::vec(edit(), 1..5), 1..4)
64 +
}
65 +
66 +
fn lines(root: &Path, rel: &str) -> Option<Vec<String>> {
67 +
    let text = std::fs::read_to_string(root.join(rel)).ok()?;
68 +
    Some(text.split_inclusive('\n').map(String::from).collect())
69 +
}
70 +
71 +
fn apply(root: &Path, e: &Edit) {
72 +
    let file = match e {
73 +
        Edit::Remove { file } => {
74 +
            let _ = std::fs::remove_file(root.join(FILES[*file]));
75 +
            return;
76 +
        }
77 +
        Edit::Insert { file, .. } | Edit::Delete { file, .. } | Edit::Replace { file, .. } => *file,
78 +
    };
79 +
    let mut l = lines(root, FILES[file]).unwrap_or_default();
80 +
    match *e {
81 +
        Edit::Insert { at, word, .. } => l.insert(at % (l.len() + 1), WORDS[word].to_string()),
82 +
        Edit::Delete { at, .. } if !l.is_empty() => {
83 +
            let i = at % l.len();
84 +
            l.remove(i);
85 +
        }
86 +
        Edit::Replace { at, word, .. } if !l.is_empty() => {
87 +
            let i = at % l.len();
88 +
            l[i] = WORDS[word].to_string();
89 +
        }
90 +
        _ => {}
91 +
    }
92 +
    let path = root.join(FILES[file]);
93 +
    std::fs::create_dir_all(path.parent().unwrap()).unwrap();
94 +
    std::fs::write(path, l.concat()).unwrap();
95 +
}
96 +
97 +
/// Every change reachable from `repo`'s bookmark heads.
98 +
fn changes_of(repo: &Repo, into: &mut BTreeMap<oxy_core::hash::ChangeHash, Change>) {
99 +
    let mut stack = repo.bookmark_heads().unwrap();
100 +
    while let Some(h) = stack.pop() {
101 +
        if into.contains_key(&h) {
102 +
            continue;
103 +
        }
104 +
        let c = repo.store().get_change(&h).unwrap();
105 +
        stack.extend(c.deps.iter().copied());
106 +
        into.insert(h, c);
107 +
    }
108 +
}
109 +
110 +
/// What must not depend on order: the checkpoint bytes, the heads and
111 +
/// the materialized tree.
112 +
fn fingerprint(
113 +
    state: &State,
114 +
) -> (
115 +
    Vec<u8>,
116 +
    BTreeSet<oxy_core::hash::ChangeHash>,
117 +
    oxy_core::tree::TreeView,
118 +
    BTreeMap<String, oxy_core::state::FileState>,
119 +
) {
120 +
    let snap = state.materialize();
121 +
    (
122 +
        state.checkpoint_payload(),
123 +
        state.heads().into_iter().collect(),
124 +
        snap.tree,
125 +
        snap.files,
126 +
    )
127 +
}
128 +
129 +
fn run_case(sessions: Vec<Vec<Vec<Edit>>>, seed: u64) -> Result<(), TestCaseError> {
130 +
    hermetic();
131 +
    let dir = tempfile::tempdir().unwrap();
132 +
    let root = dir.path();
133 +
    let origin_path = root.join("origin");
134 +
    Repo::init(&origin_path).unwrap();
135 +
    let url = origin_path.to_string_lossy().to_string();
136 +
137 +
    // The shared base.
138 +
    let seed_repo = Repo::clone(&url, &root.join("seed")).unwrap();
139 +
    for (i, f) in FILES.iter().take(SEEDED).enumerate() {
140 +
        let p = root.join("seed").join(f);
141 +
        std::fs::create_dir_all(p.parent().unwrap()).unwrap();
142 +
        std::fs::write(p, WORDS[..=i + 1].concat()).unwrap();
143 +
    }
144 +
    seed_repo.commit("base", &[]).unwrap();
145 +
    assert!(seed_repo
146 +
        .push("origin", false)
147 +
        .unwrap()
148 +
        .iter()
149 +
        .all(|r| r.ok));
150 +
151 +
    // Diverge.
152 +
    let mut clones = Vec::new();
153 +
    for (n, session) in sessions.iter().enumerate() {
154 +
        let wc = root.join(format!("clone{n}"));
155 +
        let repo = Repo::clone(&url, &wc).unwrap();
156 +
        for (k, commit) in session.iter().enumerate() {
157 +
            for e in commit {
158 +
                apply(&wc, e);
159 +
            }
160 +
            let s = repo.snapshot("oxy status").unwrap();
161 +
            if s.statuses.is_empty() {
162 +
                continue;
163 +
            }
164 +
            repo.commit(&format!("clone {n} commit {k}"), &[]).unwrap();
165 +
        }
166 +
        clones.push(repo);
167 +
    }
168 +
169 +
    let mut all = BTreeMap::new();
170 +
    for c in &clones {
171 +
        changes_of(c, &mut all);
172 +
    }
173 +
    let changes: Vec<Change> = all.values().cloned().collect();
174 +
175 +
    // The reference: every change, sorted by hash.
176 +
    let mut reference = State::new();
177 +
    for c in &changes {
178 +
        reference.add_unchecked(c.clone());
179 +
    }
180 +
    prop_assert!(reference.validate().is_ok());
181 +
    let want = fingerprint(&reference);
182 +
    // A checkpoint restores the same state.
183 +
    let restored = State::from_checkpoint_payload(&want.0).unwrap();
184 +
    prop_assert!(
185 +
        fingerprint(&restored) == want,
186 +
        "checkpoint round trip changed the state"
187 +
    );
188 +
189 +
    let mut rng = StdRng::seed_from_u64(seed);
190 +
    for _ in 0..4 {
191 +
        // Any order at all.
192 +
        let mut order = changes.clone();
193 +
        order.shuffle(&mut rng);
194 +
        let mut st = State::new();
195 +
        for c in order {
196 +
            st.add_unchecked(c);
197 +
        }
198 +
        prop_assert!(st.validate().is_ok());
199 +
        prop_assert!(
200 +
            fingerprint(&st) == want,
201 +
            "unchecked order changed the state"
202 +
        );
203 +
204 +
        // A random order that respects dependencies, the order a fetch sees.
205 +
        let mut pending: Vec<&Change> = changes.iter().collect();
206 +
        let mut st = State::new();
207 +
        while !pending.is_empty() {
208 +
            let ready: Vec<usize> = (0..pending.len())
209 +
                .filter(|&i| pending[i].deps.iter().all(|d| st.contains(d)))
210 +
                .collect();
211 +
            prop_assert!(!ready.is_empty(), "dependency cycle");
212 +
            let pick = ready[rng.gen_range(0..ready.len())];
213 +
            st.add(pending.swap_remove(pick).clone()).unwrap();
214 +
        }
215 +
        prop_assert!(
216 +
            fingerprint(&st) == want,
217 +
            "dependency order changed the state"
218 +
        );
219 +
    }
220 +
221 +
    // Through the protocol: push in a random order, then a fresh clone.
222 +
    let mut order: Vec<usize> = (0..clones.len()).collect();
223 +
    order.shuffle(&mut rng);
224 +
    for i in order {
225 +
        let c = &clones[i];
226 +
        // A clone behind the origin is told to pull first, as a user is.
227 +
        if !c
228 +
            .push("origin", false)
229 +
            .is_ok_and(|r| r.iter().all(|r| r.ok))
230 +
        {
231 +
            c.pull("origin").unwrap();
232 +
            prop_assert!(
233 +
                c.push("origin", false).unwrap().iter().all(|r| r.ok),
234 +
                "push after pull"
235 +
            );
236 +
        }
237 +
    }
238 +
    let fresh = Repo::clone(&url, &root.join("fresh")).unwrap();
239 +
    let heads = fresh.bookmark_heads().unwrap();
240 +
    let pushed = fresh.load_state_full(&heads).unwrap();
241 +
    prop_assert!(fingerprint(&pushed) == want, "the pushed state differs");
242 +
    Ok(())
243 +
}
244 +
245 +
proptest! {
246 +
    #![proptest_config(ProptestConfig { cases: 32, ..ProptestConfig::default() })]
247 +
248 +
    #[test]
249 +
    fn order_of_changes_is_irrelevant(sessions in prop::collection::vec(session(), 2..4), seed in any::<u64>()) {
250 +
        run_case(sessions, seed)?;
251 +
    }
252 +
}
modified Cargo.lock +143 −4
…
231 unchanged lines
232 232
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
233 233
234 234
[[package]]
235 +
name = "bit-set"
236 +
version = "0.8.0"
237 +
source = "registry+https://github.com/rust-lang/crates.io-index"
238 +
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
239 +
dependencies = [
240 +
 "bit-vec 0.8.0",
241 +
]
242 +
243 +
[[package]]
235 244
name = "bit-vec"
245 +
version = "0.8.0"
246 +
source = "registry+https://github.com/rust-lang/crates.io-index"
247 +
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
248 +
249 +
[[package]]
250 +
name = "bit-vec"
236 251
version = "0.9.1"
237 252
source = "registry+https://github.com/rust-lang/crates.io-index"
238 253
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
…
449 unchanged lines
688 703
689 704
[[package]]
690 705
name = "getrandom"
706 +
version = "0.3.4"
707 +
source = "registry+https://github.com/rust-lang/crates.io-index"
708 +
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
709 +
dependencies = [
710 +
 "cfg-if",
711 +
 "libc",
712 +
 "r-efi 5.3.0",
713 +
 "wasip2",
714 +
]
715 +
716 +
[[package]]
717 +
name = "getrandom"
691 718
version = "0.4.3"
692 719
source = "registry+https://github.com/rust-lang/crates.io-index"
693 720
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
694 721
dependencies = [
695 722
 "cfg-if",
696 723
 "libc",
697 -
 "r-efi",
724 +
 "r-efi 6.0.0",
698 725
 "rand_core 0.10.1",
699 726
]
700 727
…
520 unchanged lines
1221 1248
 "hex",
1222 1249
 "ignore",
1223 1250
 "ml-dsa",
1224 -
 "rand",
1251 +
 "proptest",
1252 +
 "rand 0.8.8",
1225 1253
 "rustls",
1226 1254
 "rustls-pemfile",
1227 1255
 "similar",
…
95 unchanged lines
1323 1351
]
1324 1352
1325 1353
[[package]]
1354 +
name = "proptest"
1355 +
version = "1.11.0"
1356 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1357 +
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
1358 +
dependencies = [
1359 +
 "bit-set",
1360 +
 "bit-vec 0.8.0",
1361 +
 "bitflags",
1362 +
 "num-traits",
1363 +
 "rand 0.9.5",
1364 +
 "rand_chacha 0.9.0",
1365 +
 "rand_xorshift",
1366 +
 "regex-syntax",
1367 +
 "rusty-fork",
1368 +
 "tempfile",
1369 +
 "unarray",
1370 +
]
1371 +
1372 +
[[package]]
1373 +
name = "quick-error"
1374 +
version = "1.2.3"
1375 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1376 +
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
1377 +
1378 +
[[package]]
1326 1379
name = "quote"
1327 1380
version = "1.0.47"
1328 1381
source = "registry+https://github.com/rust-lang/crates.io-index"
…
4 unchanged lines
1333 1386
1334 1387
[[package]]
1335 1388
name = "r-efi"
1389 +
version = "5.3.0"
1390 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1391 +
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
1392 +
1393 +
[[package]]
1394 +
name = "r-efi"
1336 1395
version = "6.0.0"
1337 1396
source = "registry+https://github.com/rust-lang/crates.io-index"
1338 1397
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
…
5 unchanged lines
1344 1403
checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c"
1345 1404
dependencies = [
1346 1405
 "libc",
1347 -
 "rand_chacha",
1406 +
 "rand_chacha 0.3.1",
1348 1407
 "rand_core 0.6.4",
1349 1408
]
1350 1409
1351 1410
[[package]]
1411 +
name = "rand"
1412 +
version = "0.9.5"
1413 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1414 +
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
1415 +
dependencies = [
1416 +
 "rand_chacha 0.9.0",
1417 +
 "rand_core 0.9.5",
1418 +
]
1419 +
1420 +
[[package]]
1352 1421
name = "rand_chacha"
1353 1422
version = "0.3.1"
1354 1423
source = "registry+https://github.com/rust-lang/crates.io-index"
…
4 unchanged lines
1359 1428
]
1360 1429
1361 1430
[[package]]
1431 +
name = "rand_chacha"
1432 +
version = "0.9.0"
1433 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1434 +
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
1435 +
dependencies = [
1436 +
 "ppv-lite86",
1437 +
 "rand_core 0.9.5",
1438 +
]
1439 +
1440 +
[[package]]
1362 1441
name = "rand_core"
1363 1442
version = "0.6.4"
1364 1443
source = "registry+https://github.com/rust-lang/crates.io-index"
…
4 unchanged lines
1369 1448
1370 1449
[[package]]
1371 1450
name = "rand_core"
1451 +
version = "0.9.5"
1452 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1453 +
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
1454 +
dependencies = [
1455 +
 "getrandom 0.3.4",
1456 +
]
1457 +
1458 +
[[package]]
1459 +
name = "rand_core"
1372 1460
version = "0.10.1"
1373 1461
source = "registry+https://github.com/rust-lang/crates.io-index"
1374 1462
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
1375 1463
1376 1464
[[package]]
1465 +
name = "rand_xorshift"
1466 +
version = "0.4.0"
1467 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1468 +
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
1469 +
dependencies = [
1470 +
 "rand_core 0.9.5",
1471 +
]
1472 +
1473 +
[[package]]
1377 1474
name = "rcgen"
1378 1475
version = "0.14.10"
1379 1476
source = "registry+https://github.com/rust-lang/crates.io-index"
…
120 unchanged lines
1500 1597
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
1501 1598
1502 1599
[[package]]
1600 +
name = "rusty-fork"
1601 +
version = "0.3.1"
1602 +
source = "registry+https://github.com/rust-lang/crates.io-index"
1603 +
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
1604 +
dependencies = [
1605 +
 "fnv",
1606 +
 "quick-error",
1607 +
 "tempfile",
1608 +
 "wait-timeout",
1609 +
]
1610 +
1611 +
[[package]]
1503 1612
name = "ryu"
1504 1613
version = "1.0.23"
1505 1614
source = "registry+https://github.com/rust-lang/crates.io-index"
…
434 unchanged lines
1940 2049
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
1941 2050
1942 2051
[[package]]
2052 +
name = "unarray"
2053 +
version = "0.1.4"
2054 +
source = "registry+https://github.com/rust-lang/crates.io-index"
2055 +
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
2056 +
2057 +
[[package]]
1943 2058
name = "unicode-ident"
1944 2059
version = "1.0.26"
1945 2060
source = "registry+https://github.com/rust-lang/crates.io-index"
…
52 unchanged lines
1998 2113
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
1999 2114
2000 2115
[[package]]
2116 +
name = "wait-timeout"
2117 +
version = "0.2.1"
2118 +
source = "registry+https://github.com/rust-lang/crates.io-index"
2119 +
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
2120 +
dependencies = [
2121 +
 "libc",
2122 +
]
2123 +
2124 +
[[package]]
2001 2125
name = "walkdir"
2002 2126
version = "2.5.0"
2003 2127
source = "registry+https://github.com/rust-lang/crates.io-index"
…
10 unchanged lines
2014 2138
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
2015 2139
2016 2140
[[package]]
2141 +
name = "wasip2"
2142 +
version = "1.0.4+wasi-0.2.12"
2143 +
source = "registry+https://github.com/rust-lang/crates.io-index"
2144 +
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
2145 +
dependencies = [
2146 +
 "wit-bindgen",
2147 +
]
2148 +
2149 +
[[package]]
2017 2150
name = "wasm-bindgen"
2018 2151
version = "0.2.129"
2019 2152
source = "registry+https://github.com/rust-lang/crates.io-index"
…
173 unchanged lines
2193 2326
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
2194 2327
2195 2328
[[package]]
2329 +
name = "wit-bindgen"
2330 +
version = "0.57.1"
2331 +
source = "registry+https://github.com/rust-lang/crates.io-index"
2332 +
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
2333 +
2334 +
[[package]]
2196 2335
name = "writeable"
2197 2336
version = "0.6.4"
2198 2337
source = "registry+https://github.com/rust-lang/crates.io-index"
…
23 unchanged lines
2222 2361
source = "registry+https://github.com/rust-lang/crates.io-index"
2223 2362
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
2224 2363
dependencies = [
2225 -
 "bit-vec",
2364 +
 "bit-vec 0.9.1",
2226 2365
 "time",
2227 2366
]
2228 2367
…
142 unchanged lines
modified crates/oxy-core/Cargo.toml +1 −0
…
20 unchanged lines
21 21
zstd = "0.13"
22 22
23 23
[dev-dependencies]
24 +
proptest = "1"
24 25
tempfile = "3"