automerge-rb 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +175 -0
- data/ext/automerge_ext/automerge_ext.c +1805 -0
- data/ext/automerge_ext/extconf.rb +132 -0
- data/lib/automerge/version.rb +6 -0
- data/lib/automerge.rb +110 -0
- data/rust-toolchain.toml +4 -0
- data/vendor/automerge-rust/Cargo.lock +1909 -0
- data/vendor/automerge-rust/Cargo.toml +15 -0
- data/vendor/automerge-rust/automerge/Cargo.toml +78 -0
- data/vendor/automerge-rust/automerge/README.md +5 -0
- data/vendor/automerge-rust/automerge/benches/load_save.rs +102 -0
- data/vendor/automerge-rust/automerge/benches/map.rs +260 -0
- data/vendor/automerge-rust/automerge/benches/range.rs +37 -0
- data/vendor/automerge-rust/automerge/benches/sync.rs +95 -0
- data/vendor/automerge-rust/automerge/examples/README.md +7 -0
- data/vendor/automerge-rust/automerge/examples/quickstart.rs +57 -0
- data/vendor/automerge-rust/automerge/examples/watch.rs +94 -0
- data/vendor/automerge-rust/automerge/fuzz/Cargo.toml +29 -0
- data/vendor/automerge-rust/automerge/fuzz/fuzz_targets/load.rs +37 -0
- data/vendor/automerge-rust/automerge/src/autocommit.rs +1286 -0
- data/vendor/automerge-rust/automerge/src/automerge/current_state.rs +546 -0
- data/vendor/automerge-rust/automerge/src/automerge/tests.rs +2023 -0
- data/vendor/automerge-rust/automerge/src/automerge.rs +2071 -0
- data/vendor/automerge-rust/automerge/src/autoserde.rs +128 -0
- data/vendor/automerge-rust/automerge/src/change.rs +357 -0
- data/vendor/automerge-rust/automerge/src/change_graph.rs +1215 -0
- data/vendor/automerge-rust/automerge/src/change_queue.rs +46 -0
- data/vendor/automerge-rust/automerge/src/clock.rs +206 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/boolean.rs +83 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/delta.rs +148 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/generic/group.rs +138 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/generic/simple.rs +76 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/generic.rs +93 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/key.rs +272 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/obj_id.rs +202 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/opid_list.rs +329 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/raw.rs +38 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/rle.rs +216 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range/value.rs +547 -0
- data/vendor/automerge-rust/automerge/src/columnar/column_range.rs +17 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/boolean.rs +197 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/col_error.rs +88 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/column_decoder.rs +133 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/decodable_impls.rs +175 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/delta.rs +96 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/encodable_impls.rs +200 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/leb128.rs +82 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/properties.rs +178 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/raw.rs +101 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding/rle.rs +239 -0
- data/vendor/automerge-rust/automerge/src/columnar/encoding.rs +67 -0
- data/vendor/automerge-rust/automerge/src/columnar/splice_error.rs +47 -0
- data/vendor/automerge-rust/automerge/src/columnar.rs +14 -0
- data/vendor/automerge-rust/automerge/src/convert.rs +112 -0
- data/vendor/automerge-rust/automerge/src/cursor.rs +296 -0
- data/vendor/automerge-rust/automerge/src/decoding.rs +475 -0
- data/vendor/automerge-rust/automerge/src/error.rs +159 -0
- data/vendor/automerge-rust/automerge/src/exid.rs +238 -0
- data/vendor/automerge-rust/automerge/src/hydrate/list.rs +140 -0
- data/vendor/automerge-rust/automerge/src/hydrate/map.rs +132 -0
- data/vendor/automerge-rust/automerge/src/hydrate/tests.rs +40 -0
- data/vendor/automerge-rust/automerge/src/hydrate/text.rs +89 -0
- data/vendor/automerge-rust/automerge/src/hydrate.rs +368 -0
- data/vendor/automerge-rust/automerge/src/indexed_cache.rs +113 -0
- data/vendor/automerge-rust/automerge/src/iter/doc.rs +603 -0
- data/vendor/automerge-rust/automerge/src/iter/keys.rs +93 -0
- data/vendor/automerge-rust/automerge/src/iter/list_range.rs +433 -0
- data/vendor/automerge-rust/automerge/src/iter/map_range.rs +316 -0
- data/vendor/automerge-rust/automerge/src/iter/spans.rs +601 -0
- data/vendor/automerge-rust/automerge/src/iter/tools.rs +427 -0
- data/vendor/automerge-rust/automerge/src/iter/values.rs +36 -0
- data/vendor/automerge-rust/automerge/src/iter.rs +25 -0
- data/vendor/automerge-rust/automerge/src/legacy/mod.rs +364 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/actor_id.rs +25 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/change_hash.rs +29 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/element_id.rs +27 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/mod.rs +31 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/object_id.rs +36 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/op.rs +668 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/op_type.rs +26 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/opid.rs +25 -0
- data/vendor/automerge-rust/automerge/src/legacy/serde_impls/scalar_value.rs +63 -0
- data/vendor/automerge-rust/automerge/src/legacy/utility_impls/element_id.rs +66 -0
- data/vendor/automerge-rust/automerge/src/legacy/utility_impls/key.rs +49 -0
- data/vendor/automerge-rust/automerge/src/legacy/utility_impls/mod.rs +4 -0
- data/vendor/automerge-rust/automerge/src/legacy/utility_impls/object_id.rs +74 -0
- data/vendor/automerge-rust/automerge/src/legacy/utility_impls/opid.rs +68 -0
- data/vendor/automerge-rust/automerge/src/lib.rs +315 -0
- data/vendor/automerge-rust/automerge/src/marks.rs +478 -0
- data/vendor/automerge-rust/automerge/src/op_set2/change/batch.rs +2002 -0
- data/vendor/automerge-rust/automerge/src/op_set2/change/collector.rs +974 -0
- data/vendor/automerge-rust/automerge/src/op_set2/change.rs +332 -0
- data/vendor/automerge-rust/automerge/src/op_set2/columns.rs +714 -0
- data/vendor/automerge-rust/automerge/src/op_set2/meta.rs +174 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op.rs +1363 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/elems.rs +43 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/found_op.rs +60 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/index.rs +197 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/insert.rs +179 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/mark_index.rs +292 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/marks.rs +86 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/op_iter.rs +1295 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/op_query.rs +82 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/top_op.rs +50 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set/visible.rs +290 -0
- data/vendor/automerge-rust/automerge/src/op_set2/op_set.rs +1793 -0
- data/vendor/automerge-rust/automerge/src/op_set2/parents.rs +133 -0
- data/vendor/automerge-rust/automerge/src/op_set2/skip_list.rs +714 -0
- data/vendor/automerge-rust/automerge/src/op_set2/types.rs +769 -0
- data/vendor/automerge-rust/automerge/src/op_set2.rs +18 -0
- data/vendor/automerge-rust/automerge/src/patches/patch.rs +95 -0
- data/vendor/automerge-rust/automerge/src/patches/patch_builder.rs +382 -0
- data/vendor/automerge-rust/automerge/src/patches/patch_log.rs +584 -0
- data/vendor/automerge-rust/automerge/src/patches.rs +7 -0
- data/vendor/automerge-rust/automerge/src/query/list_state.rs +230 -0
- data/vendor/automerge-rust/automerge/src/query/seek_mark.rs +142 -0
- data/vendor/automerge-rust/automerge/src/read.rs +326 -0
- data/vendor/automerge-rust/automerge/src/sequence_tree.rs +662 -0
- data/vendor/automerge-rust/automerge/src/storage/bundle/builder.rs +942 -0
- data/vendor/automerge-rust/automerge/src/storage/bundle/error.rs +42 -0
- data/vendor/automerge-rust/automerge/src/storage/bundle/meta.rs +23 -0
- data/vendor/automerge-rust/automerge/src/storage/bundle/storage.rs +146 -0
- data/vendor/automerge-rust/automerge/src/storage/bundle.rs +210 -0
- data/vendor/automerge-rust/automerge/src/storage/change/change_actors.rs +311 -0
- data/vendor/automerge-rust/automerge/src/storage/change/change_op_columns.rs +621 -0
- data/vendor/automerge-rust/automerge/src/storage/change/compressed.rs +51 -0
- data/vendor/automerge-rust/automerge/src/storage/change/op_with_change_actors.rs +1 -0
- data/vendor/automerge-rust/automerge/src/storage/change.rs +523 -0
- data/vendor/automerge-rust/automerge/src/storage/chunk.rs +312 -0
- data/vendor/automerge-rust/automerge/src/storage/columns/column.rs +42 -0
- data/vendor/automerge-rust/automerge/src/storage/columns/column_builder.rs +199 -0
- data/vendor/automerge-rust/automerge/src/storage/columns/column_specification.rs +340 -0
- data/vendor/automerge-rust/automerge/src/storage/columns/raw_column.rs +286 -0
- data/vendor/automerge-rust/automerge/src/storage/columns.rs +355 -0
- data/vendor/automerge-rust/automerge/src/storage/document/compression.rs +362 -0
- data/vendor/automerge-rust/automerge/src/storage/document.rs +411 -0
- data/vendor/automerge-rust/automerge/src/storage/load/change_collector.rs +15 -0
- data/vendor/automerge-rust/automerge/src/storage/load.rs +136 -0
- data/vendor/automerge-rust/automerge/src/storage/parse/leb128.rs +302 -0
- data/vendor/automerge-rust/automerge/src/storage/parse.rs +619 -0
- data/vendor/automerge-rust/automerge/src/storage/save/document.rs +27 -0
- data/vendor/automerge-rust/automerge/src/storage.rs +26 -0
- data/vendor/automerge-rust/automerge/src/sync/bloom.rs +161 -0
- data/vendor/automerge-rust/automerge/src/sync/message_builder.rs +118 -0
- data/vendor/automerge-rust/automerge/src/sync/state.rs +214 -0
- data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/bloom.rs +162 -0
- data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/mod.rs +625 -0
- data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/state.rs +120 -0
- data/vendor/automerge-rust/automerge/src/sync.rs +2482 -0
- data/vendor/automerge-rust/automerge/src/text_diff/LICENSE +201 -0
- data/vendor/automerge-rust/automerge/src/text_diff/myers.rs +332 -0
- data/vendor/automerge-rust/automerge/src/text_diff/replace.rs +139 -0
- data/vendor/automerge-rust/automerge/src/text_diff/utils.rs +119 -0
- data/vendor/automerge-rust/automerge/src/text_diff.rs +515 -0
- data/vendor/automerge-rust/automerge/src/text_value.rs +276 -0
- data/vendor/automerge-rust/automerge/src/transaction/commit.rs +34 -0
- data/vendor/automerge-rust/automerge/src/transaction/inner.rs +1403 -0
- data/vendor/automerge-rust/automerge/src/transaction/manual_transaction.rs +147 -0
- data/vendor/automerge-rust/automerge/src/transaction/owned_transaction.rs +266 -0
- data/vendor/automerge-rust/automerge/src/transaction/result.rs +21 -0
- data/vendor/automerge-rust/automerge/src/transaction/transactable.rs +203 -0
- data/vendor/automerge-rust/automerge/src/transaction.rs +513 -0
- data/vendor/automerge-rust/automerge/src/types.rs +749 -0
- data/vendor/automerge-rust/automerge/src/validation.rs +29 -0
- data/vendor/automerge-rust/automerge/src/value.rs +763 -0
- data/vendor/automerge-rust/automerge/tests/batch_insert.rs +1034 -0
- data/vendor/automerge-rust/automerge/tests/block_tests.rs +887 -0
- data/vendor/automerge-rust/automerge/tests/convert_string_to_text.rs +72 -0
- data/vendor/automerge-rust/automerge/tests/diff_marks.rs +1508 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/64bit_obj_id_change.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/64bit_obj_id_doc.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_has_incorrect_meta.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_is_ok.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_is_overlong.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks_compressed.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks_out_of_order.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/action-is-48.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/incorrect_max_op.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/invalid_deflate_stream.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/missing_actor.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/overflow_in_length.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/too_many_deps.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/fuzz-crashers/too_many_ops.automerge +0 -0
- data/vendor/automerge-rust/automerge/tests/test.rs +2668 -0
- data/vendor/automerge-rust/automerge/tests/test_mark_patches.rs +41 -0
- data/vendor/automerge-rust/automerge/tests/test_save_load_orphans.rs +84 -0
- data/vendor/automerge-rust/automerge/tests/text.rs +1098 -0
- data/vendor/automerge-rust/automerge/tests/text_encoding.rs +640 -0
- data/vendor/automerge-rust/automerge-c/CMakeLists.txt +439 -0
- data/vendor/automerge-rust/automerge-c/Cargo.toml +22 -0
- data/vendor/automerge-rust/automerge-c/README.md +233 -0
- data/vendor/automerge-rust/automerge-c/build.rs +21 -0
- data/vendor/automerge-rust/automerge-c/cbindgen.toml +48 -0
- data/vendor/automerge-rust/automerge-c/cmake/Cargo.toml.in +22 -0
- data/vendor/automerge-rust/automerge-c/cmake/automerge-c-config.cmake.in +99 -0
- data/vendor/automerge-rust/automerge-c/cmake/cbindgen.toml.in +48 -0
- data/vendor/automerge-rust/automerge-c/cmake/config.h.in +58 -0
- data/vendor/automerge-rust/automerge-c/cmake/enum-string-functions-gen.cmake +183 -0
- data/vendor/automerge-rust/automerge-c/cmake/file-regex-replace.cmake +33 -0
- data/vendor/automerge-rust/automerge-c/cmake/file-touch.cmake +35 -0
- data/vendor/automerge-rust/automerge-c/docs/CMakeLists.txt +39 -0
- data/vendor/automerge-rust/automerge-c/docs/img/brandmark.png +0 -0
- data/vendor/automerge-rust/automerge-c/examples/CMakeLists.txt +42 -0
- data/vendor/automerge-rust/automerge-c/examples/README.md +9 -0
- data/vendor/automerge-rust/automerge-c/examples/quickstart.c +131 -0
- data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/result.h +30 -0
- data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/stack.h +130 -0
- data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/stack_callback_data.h +53 -0
- data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/string.h +29 -0
- data/vendor/automerge-rust/automerge-c/src/actor_id.rs +193 -0
- data/vendor/automerge-rust/automerge-c/src/byte_span.rs +227 -0
- data/vendor/automerge-rust/automerge-c/src/change.rs +356 -0
- data/vendor/automerge-rust/automerge-c/src/cursor.rs +168 -0
- data/vendor/automerge-rust/automerge-c/src/doc/list.rs +636 -0
- data/vendor/automerge-rust/automerge-c/src/doc/map.rs +556 -0
- data/vendor/automerge-rust/automerge-c/src/doc/mark.rs +296 -0
- data/vendor/automerge-rust/automerge-c/src/doc/utils.rs +46 -0
- data/vendor/automerge-rust/automerge-c/src/doc.rs +1009 -0
- data/vendor/automerge-rust/automerge-c/src/index.rs +84 -0
- data/vendor/automerge-rust/automerge-c/src/item.rs +2177 -0
- data/vendor/automerge-rust/automerge-c/src/items.rs +401 -0
- data/vendor/automerge-rust/automerge-c/src/lib.rs +11 -0
- data/vendor/automerge-rust/automerge-c/src/obj.rs +216 -0
- data/vendor/automerge-rust/automerge-c/src/result.rs +653 -0
- data/vendor/automerge-rust/automerge-c/src/sync/have.rs +42 -0
- data/vendor/automerge-rust/automerge-c/src/sync/message.rs +146 -0
- data/vendor/automerge-rust/automerge-c/src/sync/state.rs +262 -0
- data/vendor/automerge-rust/automerge-c/src/sync.rs +7 -0
- data/vendor/automerge-rust/automerge-c/src/utils/result.c +33 -0
- data/vendor/automerge-rust/automerge-c/src/utils/stack.c +106 -0
- data/vendor/automerge-rust/automerge-c/src/utils/stack_callback_data.c +9 -0
- data/vendor/automerge-rust/automerge-c/src/utils/string.c +46 -0
- data/vendor/automerge-rust/automerge-c/test/CMakeLists.txt +67 -0
- data/vendor/automerge-rust/automerge-c/test/actor_id_tests.c +140 -0
- data/vendor/automerge-rust/automerge-c/test/base_state.c +17 -0
- data/vendor/automerge-rust/automerge-c/test/base_state.h +39 -0
- data/vendor/automerge-rust/automerge-c/test/byte_span_tests.c +119 -0
- data/vendor/automerge-rust/automerge-c/test/cmocka_utils.c +91 -0
- data/vendor/automerge-rust/automerge-c/test/cmocka_utils.h +42 -0
- data/vendor/automerge-rust/automerge-c/test/cursor_tests.c +263 -0
- data/vendor/automerge-rust/automerge-c/test/doc_state.c +27 -0
- data/vendor/automerge-rust/automerge-c/test/doc_state.h +17 -0
- data/vendor/automerge-rust/automerge-c/test/doc_tests.c +335 -0
- data/vendor/automerge-rust/automerge-c/test/enum_string_tests.c +148 -0
- data/vendor/automerge-rust/automerge-c/test/files/brave-ape-49.automerge +0 -0
- data/vendor/automerge-rust/automerge-c/test/item_tests.c +313 -0
- data/vendor/automerge-rust/automerge-c/test/list_tests.c +544 -0
- data/vendor/automerge-rust/automerge-c/test/macro_utils.c +38 -0
- data/vendor/automerge-rust/automerge-c/test/macro_utils.h +23 -0
- data/vendor/automerge-rust/automerge-c/test/main.c +33 -0
- data/vendor/automerge-rust/automerge-c/test/map_tests.c +1610 -0
- data/vendor/automerge-rust/automerge-c/test/mark_tests.c +124 -0
- data/vendor/automerge-rust/automerge-c/test/ported_wasm/basic_tests.c +1642 -0
- data/vendor/automerge-rust/automerge-c/test/ported_wasm/cursor_tests.c +108 -0
- data/vendor/automerge-rust/automerge-c/test/ported_wasm/suite.c +17 -0
- data/vendor/automerge-rust/automerge-c/test/ported_wasm/sync_tests.c +1280 -0
- data/vendor/automerge-rust/automerge-c/test/str_utils.c +15 -0
- data/vendor/automerge-rust/automerge-c/test/str_utils.h +17 -0
- data/vendor/automerge-rust/automerge-test/Cargo.toml +17 -0
- data/vendor/automerge-rust/automerge-test/README.md +3 -0
- data/vendor/automerge-rust/automerge-test/src/lib.rs +487 -0
- data/vendor/automerge-rust/hexane/CHANGELOG.md +34 -0
- data/vendor/automerge-rust/hexane/Cargo.toml +47 -0
- data/vendor/automerge-rust/hexane/README.md +292 -0
- data/vendor/automerge-rust/hexane/RESULTS.txt +20 -0
- data/vendor/automerge-rust/hexane/TODO +18 -0
- data/vendor/automerge-rust/hexane/benches/insert.rs +82 -0
- data/vendor/automerge-rust/hexane/benches/seek.rs +77 -0
- data/vendor/automerge-rust/hexane/benches/splice.rs +82 -0
- data/vendor/automerge-rust/hexane/src/aggregate.rs +288 -0
- data/vendor/automerge-rust/hexane/src/boolean.rs +478 -0
- data/vendor/automerge-rust/hexane/src/columndata.rs +2540 -0
- data/vendor/automerge-rust/hexane/src/cursor.rs +756 -0
- data/vendor/automerge-rust/hexane/src/delta.rs +793 -0
- data/vendor/automerge-rust/hexane/src/encoder.rs +639 -0
- data/vendor/automerge-rust/hexane/src/leb128.rs +82 -0
- data/vendor/automerge-rust/hexane/src/lib.rs +95 -0
- data/vendor/automerge-rust/hexane/src/pack.rs +325 -0
- data/vendor/automerge-rust/hexane/src/raw.rs +314 -0
- data/vendor/automerge-rust/hexane/src/rle.rs +928 -0
- data/vendor/automerge-rust/hexane/src/slab/tree.rs +1373 -0
- data/vendor/automerge-rust/hexane/src/slab/writer.rs +535 -0
- data/vendor/automerge-rust/hexane/src/slab.rs +224 -0
- data/vendor/automerge-rust/hexane/src/test.rs +108 -0
- data/vendor/bundle/ruby/3.3.0/bin/rake +29 -0
- data/vendor/bundle/ruby/3.3.0/bin/rake-compiler +29 -0
- data/vendor/bundle/ruby/3.3.0/bin/rake-compiler-dock +29 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/History.rdoc +1732 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/Manifest.txt +32 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/README.rdoc +845 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/Rakefile +97 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/design_rationale.rb +54 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/hoe/minitest.rb +30 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/assertions.rb +850 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/autorun.rb +6 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/benchmark.rb +452 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/compress.rb +94 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/error_on_warning.rb +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/expectations.rb +321 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/hell.rb +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/manual_plugins.rb +16 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/mock.rb +327 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/parallel.rb +72 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/pride.rb +4 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/pride_plugin.rb +135 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/spec.rb +353 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/test.rb +238 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/test_task.rb +324 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/unit.rb +42 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest.rb +1250 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/metametameta.rb +150 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_assertions.rb +1677 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_benchmark.rb +137 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_mock.rb +1213 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_reporter.rb +437 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_spec.rb +1159 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_test.rb +1374 -0
- data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_test_task.rb +57 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/History.rdoc +2454 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/MIT-LICENSE +21 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/README.rdoc +155 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/command_line_usage.rdoc +171 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/Rakefile1 +38 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/Rakefile2 +35 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/a.c +6 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/b.c +6 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/main.c +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/glossary.rdoc +42 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/jamis.rb +592 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/proto_rake.rdoc +127 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rake.1 +156 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rakefile.rdoc +635 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rational.rdoc +151 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/exe/rake +27 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/application.rb +847 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/backtrace.rb +25 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/clean.rb +78 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/cloneable.rb +17 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/cpu_counter.rb +122 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/default_loader.rb +15 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/dsl_definition.rb +196 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/early_time.rb +22 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/ext/core.rb +26 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/ext/string.rb +176 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_creation_task.rb +25 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_list.rb +435 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_task.rb +58 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_utils.rb +137 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_utils_ext.rb +135 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/invocation_chain.rb +57 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/invocation_exception_mixin.rb +17 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/late_time.rb +18 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/linked_list.rb +112 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/loaders/makefile.rb +54 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/multi_task.rb +14 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/name_space.rb +38 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/options.rb +31 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/packagetask.rb +222 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/phony.rb +16 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/private_reader.rb +21 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/promise.rb +100 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/pseudo_status.rb +30 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rake_module.rb +67 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rake_test_loader.rb +27 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rule_recursion_overflow_error.rb +20 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/scope.rb +43 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task.rb +434 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_argument_error.rb +8 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_arguments.rb +113 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_manager.rb +333 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/tasklib.rb +12 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/testtask.rb +192 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/thread_history_display.rb +49 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/thread_pool.rb +157 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/trace_output.rb +23 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/version.rb +10 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/win32.rb +17 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake.rb +69 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/rake.gemspec +102 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/Gemfile +8 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/History.md +695 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/LICENSE.txt +20 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/README.md +476 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/Rakefile +15 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/appveyor.yml +22 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/bin/rake-compiler +24 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/cucumber.yml +4 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/compile.feature +79 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-compile.feature +23 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-package-multi.feature +15 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-package.feature +14 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-compile.feature +22 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-no-native-compile.feature +33 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-package.feature +24 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/package.feature +40 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/compilation.rb +70 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/cross_compilation.rb +27 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/execution.rb +52 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/folders.rb +32 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/gem.rb +46 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/java_compilation.rb +7 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/env.rb +10 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/file_template_helpers.rb +137 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/generator_helpers.rb +123 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/platform_extension_helpers.rb +27 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/baseextensiontask.rb +90 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/compiler_config.rb +38 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/extensioncompiler.rb +51 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/extensiontask.rb +589 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/javaextensiontask.rb +321 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/bin/cross-ruby.rake +189 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/bootstrap.rake +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/common.rake +10 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/cucumber.rake +23 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/gem.rake +15 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/rspec.rake +9 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/CHANGELOG.md +446 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/CONTRIBUTING.md +109 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Dockerfile.jruby +79 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Dockerfile.mri.erb +282 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Gemfile +8 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/LICENSE.txt +22 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/README.md +447 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Rakefile +246 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/bin/rake-compiler-dock +18 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/buildkitd.toml +2 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/gem_helper.rb +54 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_i686.rb +18 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_musl_cross.sh +37 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_osxcross.sh +45 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_pkg_config.sh +24 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/parallel_docker_build.rb +169 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0004-Enable-build-of-static-libruby.patch +38 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0005-build-miniruby-first.patch +16 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0006-ruby-4-rubyspec-capiext.patch +16 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/rcd-env.sh +6 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/runas +7 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/sigfw.c +45 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_codesign +17 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_vbox +30 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/sudoers +1 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/colors.rb +43 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/docker_check.rb +356 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/predefined_user_group.rb +5 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/starter.rb +206 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/version.rb +4 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock.rb +151 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/Dockerfile +66 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/README.md +14 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/binutils-mingw-w64-ignore-check-errors.patch +13 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/gcc-mingw-w64-only-c-c++.patch +13 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/mingw-w64-enable-ucrt.patch +22 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/rake-compiler-dock.gemspec +34 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.alpine +17 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.debian +24 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/fixtures/mig_test_rpc.defs +8 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Gemfile +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Rakefile +97 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RcdTestExtService.java +19 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RubyRcdTest.java +16 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/extconf.rb +111 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.c +65 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.h +11 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/lib/rcd_test.rb +6 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/rcd_test.gemspec +28 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/test/test_basic.rb +49 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_environment_variables.rb +108 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_mig.rb +18 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_parallel_docker_build.rb +95 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_rubygems_plugins.rb +12 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_starter.rb +158 -0
- data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_versions.rb +82 -0
- data/vendor/bundle/ruby/3.3.0/specifications/minitest-5.27.0.gemspec +32 -0
- data/vendor/bundle/ruby/3.3.0/specifications/rake-13.4.2.gemspec +26 -0
- data/vendor/bundle/ruby/3.3.0/specifications/rake-compiler-1.3.1.gemspec +33 -0
- data/vendor/bundle/ruby/3.3.0/specifications/rake-compiler-dock-1.12.0.gemspec +28 -0
- metadata +584 -0
|
@@ -0,0 +1,2668 @@
|
|
|
1
|
+
use automerge::marks::{ExpandMark, Mark};
|
|
2
|
+
//use automerge::op_tree::B;
|
|
3
|
+
use automerge::transaction::{CommitOptions, Transactable};
|
|
4
|
+
use automerge::{
|
|
5
|
+
sync::SyncDoc, ActorId, AutoCommit, Automerge, AutomergeError, Change, ExpandedChange, ObjId,
|
|
6
|
+
ObjType, Patch, PatchAction, PatchLog, Prop, ReadDoc, ScalarValue, SequenceTree, Value, ROOT,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const B: usize = 16;
|
|
10
|
+
|
|
11
|
+
fn patch_log_from_actor(actor: &[u8]) -> PatchLog {
|
|
12
|
+
let mut source = AutoCommit::new();
|
|
13
|
+
source.set_actor(ActorId::from(actor));
|
|
14
|
+
source.put(ROOT, "source", "value").unwrap();
|
|
15
|
+
let source_changes = source.get_changes(&[]);
|
|
16
|
+
|
|
17
|
+
let mut patch_log = PatchLog::active();
|
|
18
|
+
let mut doc = Automerge::new();
|
|
19
|
+
doc.apply_changes_log_patches(source_changes, &mut patch_log)
|
|
20
|
+
.unwrap();
|
|
21
|
+
patch_log
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn doc_with_actor(actor: &[u8]) -> Automerge {
|
|
25
|
+
let mut source = AutoCommit::new();
|
|
26
|
+
source.set_actor(ActorId::from(actor));
|
|
27
|
+
source.put(ROOT, "key", "value").unwrap();
|
|
28
|
+
let changes = source.get_changes(&[]);
|
|
29
|
+
|
|
30
|
+
let mut doc = Automerge::new();
|
|
31
|
+
doc.apply_changes(changes).unwrap();
|
|
32
|
+
doc
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[test]
|
|
36
|
+
fn applying_changes_with_patch_log_from_another_document_returns_error_not_panic() {
|
|
37
|
+
let mut patch_log = patch_log_from_actor(b"bbbbbb");
|
|
38
|
+
let mut source = AutoCommit::new();
|
|
39
|
+
source.set_actor(ActorId::from(b"cccccc" as &[u8]));
|
|
40
|
+
source.put(ROOT, "source", "value").unwrap();
|
|
41
|
+
let changes = source.get_changes(&[]);
|
|
42
|
+
|
|
43
|
+
let mut doc = Automerge::new();
|
|
44
|
+
let result = doc.apply_changes_log_patches(changes, &mut patch_log);
|
|
45
|
+
|
|
46
|
+
assert!(matches!(result, Err(AutomergeError::PatchLogMismatch(_))));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#[test]
|
|
50
|
+
fn transaction_with_patch_log_from_another_document_does_not_panic() {
|
|
51
|
+
let patch_log = patch_log_from_actor(b"bbbbbb");
|
|
52
|
+
let mut doc = doc_with_actor(b"cccccc");
|
|
53
|
+
|
|
54
|
+
let result = doc.transaction_log_patches(patch_log);
|
|
55
|
+
|
|
56
|
+
assert!(matches!(result, Err(automerge::PatchLogMismatch)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#[test]
|
|
60
|
+
fn transaction_at_with_patch_log_from_another_document_does_not_panic() {
|
|
61
|
+
let patch_log = patch_log_from_actor(b"bbbbbb");
|
|
62
|
+
let mut doc = doc_with_actor(b"cccccc");
|
|
63
|
+
let heads = doc.get_heads();
|
|
64
|
+
|
|
65
|
+
let result = doc.transaction_at(patch_log, &heads);
|
|
66
|
+
|
|
67
|
+
assert!(matches!(result, Err(automerge::PatchLogMismatch)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[test]
|
|
71
|
+
fn owned_transaction_with_patch_log_from_another_document_does_not_panic() {
|
|
72
|
+
let patch_log = patch_log_from_actor(b"bbbbbb");
|
|
73
|
+
let doc = doc_with_actor(b"cccccc");
|
|
74
|
+
|
|
75
|
+
let result = doc.into_transaction(Some(patch_log), None);
|
|
76
|
+
|
|
77
|
+
assert!(matches!(result, Err(automerge::PatchLogMismatch)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
use std::fs;
|
|
81
|
+
|
|
82
|
+
// set up logging for all the tests
|
|
83
|
+
use test_log::test;
|
|
84
|
+
|
|
85
|
+
#[allow(unused_imports)]
|
|
86
|
+
use automerge_test::{
|
|
87
|
+
assert_doc, assert_obj, list, map, mk_counter, new_doc, new_doc_with_actor, pretty_print,
|
|
88
|
+
realize, realize_obj, sorted_actors, RealizedObject,
|
|
89
|
+
};
|
|
90
|
+
use pretty_assertions::assert_eq;
|
|
91
|
+
|
|
92
|
+
#[test]
|
|
93
|
+
fn no_conflict_on_repeated_assignment() {
|
|
94
|
+
let mut doc = AutoCommit::new();
|
|
95
|
+
doc.put(&automerge::ROOT, "foo", 1).unwrap();
|
|
96
|
+
doc.put(&automerge::ROOT, "foo", 2).unwrap();
|
|
97
|
+
assert_doc!(
|
|
98
|
+
&doc,
|
|
99
|
+
map! {
|
|
100
|
+
"foo" => { 2 },
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[test]
|
|
106
|
+
fn repeated_map_assignment_which_resolves_conflict_not_ignored() {
|
|
107
|
+
let mut doc1 = new_doc();
|
|
108
|
+
let mut doc2 = new_doc();
|
|
109
|
+
doc1.put(&automerge::ROOT, "field", 123).unwrap();
|
|
110
|
+
doc2.merge(&mut doc1).unwrap();
|
|
111
|
+
doc2.put(&automerge::ROOT, "field", 456).unwrap();
|
|
112
|
+
doc1.put(&automerge::ROOT, "field", 789).unwrap();
|
|
113
|
+
doc1.merge(&mut doc2).unwrap();
|
|
114
|
+
assert_eq!(doc1.get_all(&automerge::ROOT, "field").unwrap().len(), 2);
|
|
115
|
+
|
|
116
|
+
doc1.put(&automerge::ROOT, "field", 123).unwrap();
|
|
117
|
+
assert_doc!(
|
|
118
|
+
&doc1,
|
|
119
|
+
map! {
|
|
120
|
+
"field" => { 123 }
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#[test]
|
|
126
|
+
fn repeated_list_assignment_which_resolves_conflict_not_ignored() {
|
|
127
|
+
let mut doc1 = new_doc();
|
|
128
|
+
let mut doc2 = new_doc();
|
|
129
|
+
let list_id = doc1
|
|
130
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
131
|
+
.unwrap();
|
|
132
|
+
doc1.insert(&list_id, 0, 123).unwrap();
|
|
133
|
+
doc2.merge(&mut doc1).unwrap();
|
|
134
|
+
doc2.put(&list_id, 0, 456).unwrap();
|
|
135
|
+
doc1.merge(&mut doc2).unwrap();
|
|
136
|
+
doc1.put(&list_id, 0, 789).unwrap();
|
|
137
|
+
|
|
138
|
+
assert_doc!(
|
|
139
|
+
&doc1,
|
|
140
|
+
map! {
|
|
141
|
+
"list" => {
|
|
142
|
+
list![
|
|
143
|
+
{ 789 },
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#[test]
|
|
151
|
+
fn list_deletion() {
|
|
152
|
+
let mut doc = new_doc();
|
|
153
|
+
let list_id = doc
|
|
154
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
155
|
+
.unwrap();
|
|
156
|
+
doc.insert(&list_id, 0, 123).unwrap();
|
|
157
|
+
doc.insert(&list_id, 1, 456).unwrap();
|
|
158
|
+
doc.insert(&list_id, 2, 789).unwrap();
|
|
159
|
+
doc.delete(&list_id, 1).unwrap();
|
|
160
|
+
assert_doc!(
|
|
161
|
+
&doc,
|
|
162
|
+
map! {
|
|
163
|
+
"list" => { list![
|
|
164
|
+
{ 123 },
|
|
165
|
+
{ 789 },
|
|
166
|
+
]}
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#[test]
|
|
172
|
+
fn merge_concurrent_map_prop_updates() {
|
|
173
|
+
let mut doc1 = new_doc();
|
|
174
|
+
let mut doc2 = new_doc();
|
|
175
|
+
doc1.put(&automerge::ROOT, "foo", "bar").unwrap();
|
|
176
|
+
doc2.put(&automerge::ROOT, "hello", "world").unwrap();
|
|
177
|
+
doc1.merge(&mut doc2).unwrap();
|
|
178
|
+
assert_eq!(
|
|
179
|
+
doc1.get(&automerge::ROOT, "foo").unwrap().unwrap().0,
|
|
180
|
+
"bar".into()
|
|
181
|
+
);
|
|
182
|
+
assert_doc!(
|
|
183
|
+
&doc1,
|
|
184
|
+
map! {
|
|
185
|
+
"foo" => { "bar" },
|
|
186
|
+
"hello" => { "world" },
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
doc2.merge(&mut doc1).unwrap();
|
|
190
|
+
assert_doc!(
|
|
191
|
+
&doc2,
|
|
192
|
+
map! {
|
|
193
|
+
"foo" => { "bar" },
|
|
194
|
+
"hello" => { "world" },
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
assert_eq!(realize(doc1.document()), realize(doc2.document()));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#[test]
|
|
201
|
+
fn add_concurrent_increments_of_same_property() {
|
|
202
|
+
let mut doc1 = new_doc();
|
|
203
|
+
let mut doc2 = new_doc();
|
|
204
|
+
doc1.put(&automerge::ROOT, "counter", mk_counter(0))
|
|
205
|
+
.unwrap();
|
|
206
|
+
doc2.merge(&mut doc1).unwrap();
|
|
207
|
+
doc1.increment(&automerge::ROOT, "counter", 1).unwrap();
|
|
208
|
+
doc2.increment(&automerge::ROOT, "counter", 2).unwrap();
|
|
209
|
+
doc1.merge(&mut doc2).unwrap();
|
|
210
|
+
assert_doc!(
|
|
211
|
+
&doc1,
|
|
212
|
+
map! {
|
|
213
|
+
"counter" => {
|
|
214
|
+
mk_counter(3)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#[test]
|
|
221
|
+
fn add_increments_only_to_preceeded_values() {
|
|
222
|
+
let mut doc1 = new_doc();
|
|
223
|
+
let mut doc2 = new_doc();
|
|
224
|
+
|
|
225
|
+
doc1.put(&automerge::ROOT, "counter", mk_counter(0))
|
|
226
|
+
.unwrap();
|
|
227
|
+
doc1.increment(&automerge::ROOT, "counter", 1).unwrap();
|
|
228
|
+
|
|
229
|
+
// create a counter in doc2
|
|
230
|
+
doc2.put(&automerge::ROOT, "counter", mk_counter(0))
|
|
231
|
+
.unwrap();
|
|
232
|
+
doc2.increment(&automerge::ROOT, "counter", 3).unwrap();
|
|
233
|
+
|
|
234
|
+
// The two values should be conflicting rather than added
|
|
235
|
+
doc1.merge(&mut doc2).unwrap();
|
|
236
|
+
|
|
237
|
+
assert_doc!(
|
|
238
|
+
&doc1,
|
|
239
|
+
map! {
|
|
240
|
+
"counter" => {
|
|
241
|
+
mk_counter(1),
|
|
242
|
+
mk_counter(3),
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[test]
|
|
249
|
+
fn concurrent_updates_of_same_field() {
|
|
250
|
+
let mut doc1 = new_doc();
|
|
251
|
+
let mut doc2 = new_doc();
|
|
252
|
+
doc1.put(&automerge::ROOT, "field", "one").unwrap();
|
|
253
|
+
doc2.put(&automerge::ROOT, "field", "two").unwrap();
|
|
254
|
+
|
|
255
|
+
doc1.merge(&mut doc2).unwrap();
|
|
256
|
+
|
|
257
|
+
assert_doc!(
|
|
258
|
+
&doc1,
|
|
259
|
+
map! {
|
|
260
|
+
"field" => {
|
|
261
|
+
"one",
|
|
262
|
+
"two",
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
#[test]
|
|
269
|
+
fn concurrent_updates_of_same_list_element() {
|
|
270
|
+
let mut doc1 = new_doc();
|
|
271
|
+
let mut doc2 = new_doc();
|
|
272
|
+
let list_id = doc1
|
|
273
|
+
.put_object(&automerge::ROOT, "birds", ObjType::List)
|
|
274
|
+
.unwrap();
|
|
275
|
+
doc1.insert(&list_id, 0, "finch").unwrap();
|
|
276
|
+
doc2.merge(&mut doc1).unwrap();
|
|
277
|
+
doc1.put(&list_id, 0, "greenfinch").unwrap();
|
|
278
|
+
doc2.put(&list_id, 0, "goldfinch").unwrap();
|
|
279
|
+
|
|
280
|
+
doc1.merge(&mut doc2).unwrap();
|
|
281
|
+
|
|
282
|
+
assert_doc!(
|
|
283
|
+
&doc1,
|
|
284
|
+
map! {
|
|
285
|
+
"birds" => {
|
|
286
|
+
list![{
|
|
287
|
+
"greenfinch",
|
|
288
|
+
"goldfinch",
|
|
289
|
+
}]
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#[test]
|
|
296
|
+
fn assignment_conflicts_of_different_types() {
|
|
297
|
+
let mut doc1 = new_doc();
|
|
298
|
+
let mut doc2 = new_doc();
|
|
299
|
+
let mut doc3 = new_doc();
|
|
300
|
+
doc1.put(&automerge::ROOT, "field", "string").unwrap();
|
|
301
|
+
doc2.put_object(&automerge::ROOT, "field", ObjType::List)
|
|
302
|
+
.unwrap();
|
|
303
|
+
doc3.put_object(&automerge::ROOT, "field", ObjType::Map)
|
|
304
|
+
.unwrap();
|
|
305
|
+
doc1.merge(&mut doc2).unwrap();
|
|
306
|
+
doc1.merge(&mut doc3).unwrap();
|
|
307
|
+
|
|
308
|
+
assert_doc!(
|
|
309
|
+
&doc1,
|
|
310
|
+
map! {
|
|
311
|
+
"field" => {
|
|
312
|
+
"string",
|
|
313
|
+
list!{},
|
|
314
|
+
map!{},
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
#[test]
|
|
321
|
+
fn changes_within_conflicting_map_field() {
|
|
322
|
+
let mut doc1 = new_doc();
|
|
323
|
+
let mut doc2 = new_doc();
|
|
324
|
+
doc1.put(&automerge::ROOT, "field", "string").unwrap();
|
|
325
|
+
let map_id = doc2
|
|
326
|
+
.put_object(&automerge::ROOT, "field", ObjType::Map)
|
|
327
|
+
.unwrap();
|
|
328
|
+
doc2.put(&map_id, "innerKey", 42).unwrap();
|
|
329
|
+
doc1.merge(&mut doc2).unwrap();
|
|
330
|
+
|
|
331
|
+
assert_doc!(
|
|
332
|
+
&doc1,
|
|
333
|
+
map! {
|
|
334
|
+
"field" => {
|
|
335
|
+
"string",
|
|
336
|
+
map!{
|
|
337
|
+
"innerKey" => {
|
|
338
|
+
42,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
#[test]
|
|
347
|
+
fn changes_within_conflicting_list_element() {
|
|
348
|
+
let (actor1, actor2) = sorted_actors();
|
|
349
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
350
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
351
|
+
let list_id = doc1
|
|
352
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
353
|
+
.unwrap();
|
|
354
|
+
doc1.insert(&list_id, 0, "hello").unwrap();
|
|
355
|
+
doc2.merge(&mut doc1).unwrap();
|
|
356
|
+
|
|
357
|
+
let map_in_doc1 = doc1.put_object(&list_id, 0, ObjType::Map).unwrap();
|
|
358
|
+
doc1.put(&map_in_doc1, "map1", true).unwrap();
|
|
359
|
+
doc1.put(&map_in_doc1, "key", 1).unwrap();
|
|
360
|
+
|
|
361
|
+
let map_in_doc2 = doc2.put_object(&list_id, 0, ObjType::Map).unwrap();
|
|
362
|
+
doc1.merge(&mut doc2).unwrap();
|
|
363
|
+
doc2.put(&map_in_doc2, "map2", true).unwrap();
|
|
364
|
+
doc2.put(&map_in_doc2, "key", 2).unwrap();
|
|
365
|
+
|
|
366
|
+
doc1.merge(&mut doc2).unwrap();
|
|
367
|
+
|
|
368
|
+
assert_doc!(
|
|
369
|
+
&doc1,
|
|
370
|
+
map! {
|
|
371
|
+
"list" => {
|
|
372
|
+
list![
|
|
373
|
+
{
|
|
374
|
+
map!{
|
|
375
|
+
"map2" => { true },
|
|
376
|
+
"key" => { 2 },
|
|
377
|
+
},
|
|
378
|
+
map!{
|
|
379
|
+
"key" => { 1 },
|
|
380
|
+
"map1" => { true },
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
#[test]
|
|
390
|
+
fn concurrently_assigned_nested_maps_should_not_merge() {
|
|
391
|
+
let mut doc1 = new_doc();
|
|
392
|
+
let mut doc2 = new_doc();
|
|
393
|
+
|
|
394
|
+
let doc1_map_id = doc1
|
|
395
|
+
.put_object(&automerge::ROOT, "config", ObjType::Map)
|
|
396
|
+
.unwrap();
|
|
397
|
+
doc1.put(&doc1_map_id, "background", "blue").unwrap();
|
|
398
|
+
|
|
399
|
+
let doc2_map_id = doc2
|
|
400
|
+
.put_object(&automerge::ROOT, "config", ObjType::Map)
|
|
401
|
+
.unwrap();
|
|
402
|
+
doc2.put(&doc2_map_id, "logo_url", "logo.png").unwrap();
|
|
403
|
+
|
|
404
|
+
doc1.merge(&mut doc2).unwrap();
|
|
405
|
+
|
|
406
|
+
assert_doc!(
|
|
407
|
+
&doc1,
|
|
408
|
+
map! {
|
|
409
|
+
"config" => {
|
|
410
|
+
map!{
|
|
411
|
+
"background" => {"blue"}
|
|
412
|
+
},
|
|
413
|
+
map!{
|
|
414
|
+
"logo_url" => {"logo.png"}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#[test]
|
|
422
|
+
fn concurrent_insertions_at_different_list_positions() {
|
|
423
|
+
let (actor1, actor2) = sorted_actors();
|
|
424
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
425
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
426
|
+
assert!(doc1.get_actor() < doc2.get_actor());
|
|
427
|
+
|
|
428
|
+
let list_id = doc1
|
|
429
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
430
|
+
.unwrap();
|
|
431
|
+
|
|
432
|
+
doc1.insert(&list_id, 0, "one").unwrap();
|
|
433
|
+
doc1.insert(&list_id, 1, "three").unwrap();
|
|
434
|
+
doc2.merge(&mut doc1).unwrap();
|
|
435
|
+
doc1.splice(&list_id, 1, 0, vec![ScalarValue::from("two")])
|
|
436
|
+
.unwrap();
|
|
437
|
+
doc2.insert(&list_id, 2, "four").unwrap();
|
|
438
|
+
|
|
439
|
+
doc1.merge(&mut doc2).unwrap();
|
|
440
|
+
|
|
441
|
+
assert_doc!(
|
|
442
|
+
&doc1,
|
|
443
|
+
map! {
|
|
444
|
+
"list" => {
|
|
445
|
+
list![
|
|
446
|
+
{"one"},
|
|
447
|
+
{"two"},
|
|
448
|
+
{"three"},
|
|
449
|
+
{"four"},
|
|
450
|
+
]
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
#[test]
|
|
457
|
+
fn concurrent_insertions_at_same_list_position() {
|
|
458
|
+
let (actor1, actor2) = sorted_actors();
|
|
459
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
460
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
461
|
+
assert!(doc1.get_actor() < doc2.get_actor());
|
|
462
|
+
|
|
463
|
+
let list_id = doc1
|
|
464
|
+
.put_object(&automerge::ROOT, "birds", ObjType::List)
|
|
465
|
+
.unwrap();
|
|
466
|
+
doc1.insert(&list_id, 0, "parakeet").unwrap();
|
|
467
|
+
|
|
468
|
+
doc2.merge(&mut doc1).unwrap();
|
|
469
|
+
doc1.insert(&list_id, 1, "starling").unwrap();
|
|
470
|
+
doc2.insert(&list_id, 1, "chaffinch").unwrap();
|
|
471
|
+
doc1.merge(&mut doc2).unwrap();
|
|
472
|
+
|
|
473
|
+
assert_doc!(
|
|
474
|
+
&doc1,
|
|
475
|
+
map! {
|
|
476
|
+
"birds" => {
|
|
477
|
+
list![
|
|
478
|
+
{
|
|
479
|
+
"parakeet",
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"chaffinch",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"starling",
|
|
486
|
+
},
|
|
487
|
+
]
|
|
488
|
+
},
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
#[test]
|
|
494
|
+
fn concurrent_assignment_and_deletion_of_a_map_entry() {
|
|
495
|
+
let mut doc1 = new_doc();
|
|
496
|
+
let mut doc2 = new_doc();
|
|
497
|
+
doc1.put(&automerge::ROOT, "bestBird", "robin").unwrap();
|
|
498
|
+
doc2.merge(&mut doc1).unwrap();
|
|
499
|
+
doc1.delete(&automerge::ROOT, "bestBird").unwrap();
|
|
500
|
+
doc2.put(&automerge::ROOT, "bestBird", "magpie").unwrap();
|
|
501
|
+
|
|
502
|
+
doc1.merge(&mut doc2).unwrap();
|
|
503
|
+
|
|
504
|
+
assert_doc!(
|
|
505
|
+
&doc1,
|
|
506
|
+
map! {
|
|
507
|
+
"bestBird" => {
|
|
508
|
+
"magpie",
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
#[test]
|
|
515
|
+
fn concurrent_assignment_and_deletion_of_list_entry() {
|
|
516
|
+
let mut doc1 = new_doc();
|
|
517
|
+
let mut doc2 = new_doc();
|
|
518
|
+
let list_id = doc1
|
|
519
|
+
.put_object(&automerge::ROOT, "birds", ObjType::List)
|
|
520
|
+
.unwrap();
|
|
521
|
+
doc1.insert(&list_id, 0, "blackbird").unwrap();
|
|
522
|
+
doc1.insert(&list_id, 1, "thrush").unwrap();
|
|
523
|
+
doc1.insert(&list_id, 2, "goldfinch").unwrap();
|
|
524
|
+
doc2.merge(&mut doc1).unwrap();
|
|
525
|
+
doc1.put(&list_id, 1, "starling").unwrap();
|
|
526
|
+
doc2.delete(&list_id, 1).unwrap();
|
|
527
|
+
|
|
528
|
+
assert_doc!(
|
|
529
|
+
&doc2,
|
|
530
|
+
map! {
|
|
531
|
+
"birds" => {list![
|
|
532
|
+
{"blackbird"},
|
|
533
|
+
{"goldfinch"},
|
|
534
|
+
]}
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
assert_doc!(
|
|
539
|
+
&doc1,
|
|
540
|
+
map! {
|
|
541
|
+
"birds" => {list![
|
|
542
|
+
{ "blackbird" },
|
|
543
|
+
{ "starling" },
|
|
544
|
+
{ "goldfinch" },
|
|
545
|
+
]}
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
doc1.merge(&mut doc2).unwrap();
|
|
550
|
+
|
|
551
|
+
assert_doc!(
|
|
552
|
+
&doc1,
|
|
553
|
+
map! {
|
|
554
|
+
"birds" => {list![
|
|
555
|
+
{ "blackbird" },
|
|
556
|
+
{ "starling" },
|
|
557
|
+
{ "goldfinch" },
|
|
558
|
+
]}
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
#[test]
|
|
564
|
+
fn insertion_after_a_deleted_list_element() {
|
|
565
|
+
let mut doc1 = new_doc();
|
|
566
|
+
let mut doc2 = new_doc();
|
|
567
|
+
let list_id = doc1
|
|
568
|
+
.put_object(&automerge::ROOT, "birds", ObjType::List)
|
|
569
|
+
.unwrap();
|
|
570
|
+
|
|
571
|
+
doc1.insert(&list_id, 0, "blackbird").unwrap();
|
|
572
|
+
doc1.insert(&list_id, 1, "thrush").unwrap();
|
|
573
|
+
doc1.insert(&list_id, 2, "goldfinch").unwrap();
|
|
574
|
+
|
|
575
|
+
doc2.merge(&mut doc1).unwrap();
|
|
576
|
+
|
|
577
|
+
doc1.splice(&list_id, 1, 2, Vec::<ScalarValue>::new())
|
|
578
|
+
.unwrap();
|
|
579
|
+
|
|
580
|
+
doc2.splice(&list_id, 2, 0, vec![ScalarValue::from("starling")])
|
|
581
|
+
.unwrap();
|
|
582
|
+
|
|
583
|
+
doc1.merge(&mut doc2).unwrap();
|
|
584
|
+
|
|
585
|
+
assert_doc!(
|
|
586
|
+
&doc1,
|
|
587
|
+
map! {
|
|
588
|
+
"birds" => {list![
|
|
589
|
+
{ "blackbird" },
|
|
590
|
+
{ "starling" }
|
|
591
|
+
]}
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
doc2.merge(&mut doc1).unwrap();
|
|
596
|
+
assert_doc!(
|
|
597
|
+
&doc2,
|
|
598
|
+
map! {
|
|
599
|
+
"birds" => {list![
|
|
600
|
+
{ "blackbird" },
|
|
601
|
+
{ "starling" }
|
|
602
|
+
]}
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#[test]
|
|
608
|
+
fn concurrent_deletion_of_same_list_element() {
|
|
609
|
+
let mut doc1 = new_doc();
|
|
610
|
+
let mut doc2 = new_doc();
|
|
611
|
+
let list_id = doc1
|
|
612
|
+
.put_object(&automerge::ROOT, "birds", ObjType::List)
|
|
613
|
+
.unwrap();
|
|
614
|
+
|
|
615
|
+
doc1.insert(&list_id, 0, "albatross").unwrap();
|
|
616
|
+
doc1.insert(&list_id, 1, "buzzard").unwrap();
|
|
617
|
+
doc1.insert(&list_id, 2, "cormorant").unwrap();
|
|
618
|
+
|
|
619
|
+
doc2.merge(&mut doc1).unwrap();
|
|
620
|
+
|
|
621
|
+
doc1.delete(&list_id, 1).unwrap();
|
|
622
|
+
|
|
623
|
+
doc2.delete(&list_id, 1).unwrap();
|
|
624
|
+
|
|
625
|
+
doc1.merge(&mut doc2).unwrap();
|
|
626
|
+
|
|
627
|
+
assert_doc!(
|
|
628
|
+
&doc1,
|
|
629
|
+
map! {
|
|
630
|
+
"birds" => {list![
|
|
631
|
+
{ "albatross" },
|
|
632
|
+
{ "cormorant" }
|
|
633
|
+
]}
|
|
634
|
+
}
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
doc2.merge(&mut doc1).unwrap();
|
|
638
|
+
assert_doc!(
|
|
639
|
+
&doc2,
|
|
640
|
+
map! {
|
|
641
|
+
"birds" => {list![
|
|
642
|
+
{ "albatross" },
|
|
643
|
+
{ "cormorant" }
|
|
644
|
+
]}
|
|
645
|
+
}
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
#[test]
|
|
650
|
+
fn concurrent_updates_at_different_levels() {
|
|
651
|
+
let mut doc1 = new_doc();
|
|
652
|
+
let mut doc2 = new_doc();
|
|
653
|
+
|
|
654
|
+
let animals = doc1
|
|
655
|
+
.put_object(&automerge::ROOT, "animals", ObjType::Map)
|
|
656
|
+
.unwrap();
|
|
657
|
+
let birds = doc1.put_object(&animals, "birds", ObjType::Map).unwrap();
|
|
658
|
+
doc1.put(&birds, "pink", "flamingo").unwrap();
|
|
659
|
+
doc1.put(&birds, "black", "starling").unwrap();
|
|
660
|
+
|
|
661
|
+
let mammals = doc1.put_object(&animals, "mammals", ObjType::List).unwrap();
|
|
662
|
+
doc1.insert(&mammals, 0, "badger").unwrap();
|
|
663
|
+
|
|
664
|
+
doc2.merge(&mut doc1).unwrap();
|
|
665
|
+
|
|
666
|
+
doc1.put(&birds, "brown", "sparrow").unwrap();
|
|
667
|
+
|
|
668
|
+
doc2.delete(&animals, "birds").unwrap();
|
|
669
|
+
doc1.merge(&mut doc2).unwrap();
|
|
670
|
+
|
|
671
|
+
assert_obj!(
|
|
672
|
+
&doc1,
|
|
673
|
+
&automerge::ROOT,
|
|
674
|
+
"animals",
|
|
675
|
+
map! {
|
|
676
|
+
"mammals" => {
|
|
677
|
+
list![{ "badger" }],
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
assert_obj!(
|
|
683
|
+
doc2.document(),
|
|
684
|
+
&automerge::ROOT,
|
|
685
|
+
"animals",
|
|
686
|
+
map! {
|
|
687
|
+
"mammals" => {
|
|
688
|
+
list![{ "badger" }],
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
#[test]
|
|
695
|
+
fn concurrent_updates_of_concurrently_deleted_objects() {
|
|
696
|
+
let mut doc1 = new_doc();
|
|
697
|
+
let mut doc2 = new_doc();
|
|
698
|
+
|
|
699
|
+
let birds = doc1
|
|
700
|
+
.put_object(&automerge::ROOT, "birds", ObjType::Map)
|
|
701
|
+
.unwrap();
|
|
702
|
+
let blackbird = doc1.put_object(&birds, "blackbird", ObjType::Map).unwrap();
|
|
703
|
+
doc1.put(&blackbird, "feathers", "black").unwrap();
|
|
704
|
+
|
|
705
|
+
doc2.merge(&mut doc1).unwrap();
|
|
706
|
+
|
|
707
|
+
doc1.delete(&birds, "blackbird").unwrap();
|
|
708
|
+
|
|
709
|
+
doc2.put(&blackbird, "beak", "orange").unwrap();
|
|
710
|
+
|
|
711
|
+
doc1.merge(&mut doc2).unwrap();
|
|
712
|
+
|
|
713
|
+
assert_doc!(
|
|
714
|
+
&doc1,
|
|
715
|
+
map! {
|
|
716
|
+
"birds" => {
|
|
717
|
+
map!{},
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
#[test]
|
|
724
|
+
fn does_not_interleave_sequence_insertions_at_same_position() {
|
|
725
|
+
let (actor1, actor2) = sorted_actors();
|
|
726
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
727
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
728
|
+
|
|
729
|
+
let wisdom = doc1
|
|
730
|
+
.put_object(&automerge::ROOT, "wisdom", ObjType::List)
|
|
731
|
+
.unwrap();
|
|
732
|
+
doc2.merge(&mut doc1).unwrap();
|
|
733
|
+
|
|
734
|
+
doc1.splice(
|
|
735
|
+
&wisdom,
|
|
736
|
+
0,
|
|
737
|
+
0,
|
|
738
|
+
vec![
|
|
739
|
+
ScalarValue::from("to"),
|
|
740
|
+
ScalarValue::from("be"),
|
|
741
|
+
ScalarValue::from("is"),
|
|
742
|
+
ScalarValue::from("to"),
|
|
743
|
+
ScalarValue::from("do"),
|
|
744
|
+
],
|
|
745
|
+
)
|
|
746
|
+
.unwrap();
|
|
747
|
+
|
|
748
|
+
doc2.splice(
|
|
749
|
+
&wisdom,
|
|
750
|
+
0,
|
|
751
|
+
0,
|
|
752
|
+
vec![
|
|
753
|
+
ScalarValue::from("to"),
|
|
754
|
+
ScalarValue::from("do"),
|
|
755
|
+
ScalarValue::from("is"),
|
|
756
|
+
ScalarValue::from("to"),
|
|
757
|
+
ScalarValue::from("be"),
|
|
758
|
+
],
|
|
759
|
+
)
|
|
760
|
+
.unwrap();
|
|
761
|
+
|
|
762
|
+
doc1.merge(&mut doc2).unwrap();
|
|
763
|
+
|
|
764
|
+
assert_doc!(
|
|
765
|
+
&doc1,
|
|
766
|
+
map! {
|
|
767
|
+
"wisdom" => {list![
|
|
768
|
+
{"to"},
|
|
769
|
+
{"do"},
|
|
770
|
+
{"is"},
|
|
771
|
+
{"to"},
|
|
772
|
+
{"be"},
|
|
773
|
+
{"to"},
|
|
774
|
+
{"be"},
|
|
775
|
+
{"is"},
|
|
776
|
+
{"to"},
|
|
777
|
+
{"do"},
|
|
778
|
+
]}
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
#[test]
|
|
784
|
+
fn mutliple_insertions_at_same_list_position_with_insertion_by_greater_actor_id() {
|
|
785
|
+
let (actor1, actor2) = sorted_actors();
|
|
786
|
+
assert!(actor2 > actor1);
|
|
787
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
788
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
789
|
+
|
|
790
|
+
let list = doc1
|
|
791
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
792
|
+
.unwrap();
|
|
793
|
+
doc1.insert(&list, 0, "two").unwrap();
|
|
794
|
+
doc2.merge(&mut doc1).unwrap();
|
|
795
|
+
|
|
796
|
+
doc2.insert(&list, 0, "one").unwrap();
|
|
797
|
+
assert_doc!(
|
|
798
|
+
&doc2,
|
|
799
|
+
map! {
|
|
800
|
+
"list" => { list![
|
|
801
|
+
{ "one" },
|
|
802
|
+
{ "two" },
|
|
803
|
+
]}
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
#[test]
|
|
809
|
+
fn mutliple_insertions_at_same_list_position_with_insertion_by_lesser_actor_id() {
|
|
810
|
+
let (actor2, actor1) = sorted_actors();
|
|
811
|
+
assert!(actor2 < actor1);
|
|
812
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
813
|
+
let mut doc2 = new_doc_with_actor(actor2);
|
|
814
|
+
|
|
815
|
+
let list = doc1
|
|
816
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
817
|
+
.unwrap();
|
|
818
|
+
doc1.insert(&list, 0, "two").unwrap();
|
|
819
|
+
doc2.merge(&mut doc1).unwrap();
|
|
820
|
+
|
|
821
|
+
doc2.insert(&list, 0, "one").unwrap();
|
|
822
|
+
assert_doc!(
|
|
823
|
+
&doc2,
|
|
824
|
+
map! {
|
|
825
|
+
"list" => { list![
|
|
826
|
+
{ "one" },
|
|
827
|
+
{ "two" },
|
|
828
|
+
]}
|
|
829
|
+
}
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
#[test]
|
|
834
|
+
fn insertion_consistent_with_causality() {
|
|
835
|
+
let mut doc1 = new_doc();
|
|
836
|
+
let mut doc2 = new_doc();
|
|
837
|
+
|
|
838
|
+
let list = doc1
|
|
839
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
840
|
+
.unwrap();
|
|
841
|
+
doc1.insert(&list, 0, "four").unwrap();
|
|
842
|
+
doc2.merge(&mut doc1).unwrap();
|
|
843
|
+
doc2.insert(&list, 0, "three").unwrap();
|
|
844
|
+
doc1.merge(&mut doc2).unwrap();
|
|
845
|
+
doc1.insert(&list, 0, "two").unwrap();
|
|
846
|
+
doc2.merge(&mut doc1).unwrap();
|
|
847
|
+
doc2.insert(&list, 0, "one").unwrap();
|
|
848
|
+
|
|
849
|
+
assert_doc!(
|
|
850
|
+
&doc2,
|
|
851
|
+
map! {
|
|
852
|
+
"list" => { list![
|
|
853
|
+
{"one"},
|
|
854
|
+
{"two"},
|
|
855
|
+
{"three" },
|
|
856
|
+
{"four"},
|
|
857
|
+
]}
|
|
858
|
+
}
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
#[test]
|
|
863
|
+
fn save_and_restore_empty() {
|
|
864
|
+
let mut doc = new_doc();
|
|
865
|
+
let loaded = Automerge::load(&doc.save()).unwrap();
|
|
866
|
+
|
|
867
|
+
assert_doc!(&loaded, map! {});
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
#[test]
|
|
871
|
+
fn save_restore_complex1() {
|
|
872
|
+
let mut doc1 = new_doc();
|
|
873
|
+
let todos = doc1
|
|
874
|
+
.put_object(&automerge::ROOT, "todos", ObjType::List)
|
|
875
|
+
.unwrap();
|
|
876
|
+
|
|
877
|
+
let first_todo = doc1.insert_object(&todos, 0, ObjType::Map).unwrap();
|
|
878
|
+
doc1.put(&first_todo, "title", "water plants").unwrap();
|
|
879
|
+
doc1.put(&first_todo, "done", false).unwrap();
|
|
880
|
+
|
|
881
|
+
let mut doc2 = new_doc();
|
|
882
|
+
doc2.merge(&mut doc1).unwrap();
|
|
883
|
+
doc2.put(&first_todo, "title", "weed plants").unwrap();
|
|
884
|
+
|
|
885
|
+
doc1.put(&first_todo, "title", "kill plants").unwrap();
|
|
886
|
+
doc1.merge(&mut doc2).unwrap();
|
|
887
|
+
|
|
888
|
+
let reloaded = Automerge::load(&doc1.save()).unwrap();
|
|
889
|
+
|
|
890
|
+
assert_doc!(
|
|
891
|
+
&reloaded,
|
|
892
|
+
map! {
|
|
893
|
+
"todos" => {list![
|
|
894
|
+
{map!{
|
|
895
|
+
"title" => {
|
|
896
|
+
"weed plants",
|
|
897
|
+
"kill plants",
|
|
898
|
+
},
|
|
899
|
+
"done" => {false},
|
|
900
|
+
}}
|
|
901
|
+
]}
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
#[test]
|
|
907
|
+
fn handle_repeated_out_of_order_changes() -> Result<(), automerge::AutomergeError> {
|
|
908
|
+
let mut doc1 = new_doc();
|
|
909
|
+
let list = doc1.put_object(ROOT, "list", ObjType::List)?;
|
|
910
|
+
doc1.insert(&list, 0, "a")?;
|
|
911
|
+
let mut doc2 = doc1.fork();
|
|
912
|
+
doc1.insert(&list, 1, "b")?;
|
|
913
|
+
doc1.commit();
|
|
914
|
+
doc1.insert(&list, 2, "c")?;
|
|
915
|
+
doc1.commit();
|
|
916
|
+
doc1.insert(&list, 3, "d")?;
|
|
917
|
+
doc1.commit();
|
|
918
|
+
let changes = doc1.get_changes(&[]).into_iter().collect::<Vec<_>>();
|
|
919
|
+
doc2.apply_changes(changes[2..].to_vec())?;
|
|
920
|
+
doc2.apply_changes(changes[2..].to_vec())?;
|
|
921
|
+
doc2.apply_changes(changes)?;
|
|
922
|
+
assert_eq!(doc1.save(), doc2.save());
|
|
923
|
+
Ok(())
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
#[test]
|
|
927
|
+
fn save_restore_complex_transactional() {
|
|
928
|
+
let mut doc1 = Automerge::new();
|
|
929
|
+
let first_todo = doc1
|
|
930
|
+
.transact::<_, _, automerge::AutomergeError>(|d| {
|
|
931
|
+
let todos = d.put_object(&automerge::ROOT, "todos", ObjType::List)?;
|
|
932
|
+
let first_todo = d.insert_object(&todos, 0, ObjType::Map)?;
|
|
933
|
+
d.put(&first_todo, "title", "water plants")?;
|
|
934
|
+
d.put(&first_todo, "done", false)?;
|
|
935
|
+
Ok(first_todo)
|
|
936
|
+
})
|
|
937
|
+
.unwrap()
|
|
938
|
+
.result;
|
|
939
|
+
|
|
940
|
+
let mut doc2 = Automerge::new();
|
|
941
|
+
doc2.merge(&mut doc1).unwrap();
|
|
942
|
+
doc2.transact::<_, _, automerge::AutomergeError>(|tx| {
|
|
943
|
+
tx.put(&first_todo, "title", "weed plants")?;
|
|
944
|
+
Ok(())
|
|
945
|
+
})
|
|
946
|
+
.unwrap();
|
|
947
|
+
|
|
948
|
+
doc1.transact::<_, _, automerge::AutomergeError>(|tx| {
|
|
949
|
+
tx.put(&first_todo, "title", "kill plants")?;
|
|
950
|
+
Ok(())
|
|
951
|
+
})
|
|
952
|
+
.unwrap();
|
|
953
|
+
doc1.merge(&mut doc2).unwrap();
|
|
954
|
+
|
|
955
|
+
let reloaded = Automerge::load(&doc1.save()).unwrap();
|
|
956
|
+
|
|
957
|
+
assert_doc!(
|
|
958
|
+
&reloaded,
|
|
959
|
+
map! {
|
|
960
|
+
"todos" => {list![
|
|
961
|
+
{map!{
|
|
962
|
+
"title" => {
|
|
963
|
+
"weed plants",
|
|
964
|
+
"kill plants",
|
|
965
|
+
},
|
|
966
|
+
"done" => {false},
|
|
967
|
+
}}
|
|
968
|
+
]}
|
|
969
|
+
}
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
#[test]
|
|
974
|
+
fn list_counter_del() -> Result<(), automerge::AutomergeError> {
|
|
975
|
+
let mut v = [ActorId::random(), ActorId::random(), ActorId::random()];
|
|
976
|
+
v.sort();
|
|
977
|
+
let actor1 = v[0].clone();
|
|
978
|
+
let actor2 = v[1].clone();
|
|
979
|
+
let actor3 = v[2].clone();
|
|
980
|
+
|
|
981
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
982
|
+
|
|
983
|
+
let list = doc1.put_object(ROOT, "list", ObjType::List)?;
|
|
984
|
+
doc1.insert(&list, 0, "a")?;
|
|
985
|
+
doc1.insert(&list, 1, "b")?;
|
|
986
|
+
doc1.insert(&list, 2, "c")?;
|
|
987
|
+
|
|
988
|
+
let mut doc2 = AutoCommit::load(&doc1.save())?;
|
|
989
|
+
doc2.set_actor(actor2);
|
|
990
|
+
|
|
991
|
+
let mut doc3 = AutoCommit::load(&doc1.save())?;
|
|
992
|
+
doc3.set_actor(actor3);
|
|
993
|
+
|
|
994
|
+
doc1.put(&list, 1, ScalarValue::counter(0))?;
|
|
995
|
+
doc2.put(&list, 1, ScalarValue::counter(10))?;
|
|
996
|
+
doc3.put(&list, 1, ScalarValue::counter(100))?;
|
|
997
|
+
|
|
998
|
+
doc1.put(&list, 2, ScalarValue::counter(0))?;
|
|
999
|
+
doc2.put(&list, 2, ScalarValue::counter(10))?;
|
|
1000
|
+
doc3.put(&list, 2, 100)?;
|
|
1001
|
+
|
|
1002
|
+
doc1.increment(&list, 1, 1)?;
|
|
1003
|
+
doc1.increment(&list, 2, 1)?;
|
|
1004
|
+
|
|
1005
|
+
doc1.merge(&mut doc2).unwrap();
|
|
1006
|
+
doc1.merge(&mut doc3).unwrap();
|
|
1007
|
+
|
|
1008
|
+
assert_obj!(
|
|
1009
|
+
doc1.document(),
|
|
1010
|
+
&automerge::ROOT,
|
|
1011
|
+
"list",
|
|
1012
|
+
list![
|
|
1013
|
+
{
|
|
1014
|
+
"a",
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
ScalarValue::counter(1),
|
|
1018
|
+
ScalarValue::counter(10),
|
|
1019
|
+
ScalarValue::counter(100)
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
ScalarValue::Int(100),
|
|
1023
|
+
ScalarValue::counter(1),
|
|
1024
|
+
ScalarValue::counter(10),
|
|
1025
|
+
}
|
|
1026
|
+
]
|
|
1027
|
+
);
|
|
1028
|
+
|
|
1029
|
+
doc1.increment(&list, 1, 1)?;
|
|
1030
|
+
doc1.increment(&list, 2, 1)?;
|
|
1031
|
+
|
|
1032
|
+
assert_obj!(
|
|
1033
|
+
doc1.document(),
|
|
1034
|
+
&automerge::ROOT,
|
|
1035
|
+
"list",
|
|
1036
|
+
list![
|
|
1037
|
+
{
|
|
1038
|
+
"a",
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
ScalarValue::counter(2),
|
|
1042
|
+
ScalarValue::counter(11),
|
|
1043
|
+
ScalarValue::counter(101)
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
ScalarValue::counter(2),
|
|
1047
|
+
ScalarValue::counter(11),
|
|
1048
|
+
}
|
|
1049
|
+
]
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
doc1.delete(&list, 2)?;
|
|
1053
|
+
|
|
1054
|
+
assert_eq!(doc1.length(&list), 2);
|
|
1055
|
+
|
|
1056
|
+
let doc4 = AutoCommit::load(&doc1.save())?;
|
|
1057
|
+
|
|
1058
|
+
assert_eq!(doc4.length(&list), 2);
|
|
1059
|
+
|
|
1060
|
+
doc1.delete(&list, 1)?;
|
|
1061
|
+
|
|
1062
|
+
assert_eq!(doc1.length(&list), 1);
|
|
1063
|
+
|
|
1064
|
+
doc1.dump();
|
|
1065
|
+
let doc5 = AutoCommit::load(&doc1.save())?;
|
|
1066
|
+
|
|
1067
|
+
assert_eq!(doc5.length(&list), 1);
|
|
1068
|
+
|
|
1069
|
+
Ok(())
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
#[test]
|
|
1073
|
+
fn observe_counter_change_application() {
|
|
1074
|
+
let mut doc = AutoCommit::new();
|
|
1075
|
+
doc.put(ROOT, "counter", ScalarValue::counter(1)).unwrap();
|
|
1076
|
+
doc.increment(ROOT, "counter", 2).unwrap();
|
|
1077
|
+
doc.increment(ROOT, "counter", 5).unwrap();
|
|
1078
|
+
let changes = doc.get_changes(&[]).into_iter();
|
|
1079
|
+
|
|
1080
|
+
let mut doc = AutoCommit::new();
|
|
1081
|
+
doc.apply_changes(changes).unwrap();
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
#[test]
|
|
1085
|
+
fn increment_non_counter_map() {
|
|
1086
|
+
let mut doc = AutoCommit::new();
|
|
1087
|
+
// can't increment nothing
|
|
1088
|
+
assert!(matches!(
|
|
1089
|
+
doc.increment(ROOT, "nothing", 2),
|
|
1090
|
+
Err(AutomergeError::MissingCounter)
|
|
1091
|
+
));
|
|
1092
|
+
|
|
1093
|
+
// can't increment a non-counter
|
|
1094
|
+
doc.put(ROOT, "non-counter", "mystring").unwrap();
|
|
1095
|
+
assert!(matches!(
|
|
1096
|
+
doc.increment(ROOT, "non-counter", 2),
|
|
1097
|
+
Err(AutomergeError::MissingCounter)
|
|
1098
|
+
));
|
|
1099
|
+
|
|
1100
|
+
// can increment a counter still
|
|
1101
|
+
doc.put(ROOT, "counter", ScalarValue::counter(1)).unwrap();
|
|
1102
|
+
assert!(matches!(doc.increment(ROOT, "counter", 2), Ok(())));
|
|
1103
|
+
|
|
1104
|
+
// can increment a counter that is part of a conflict
|
|
1105
|
+
let mut doc1 = AutoCommit::new();
|
|
1106
|
+
doc1.set_actor(ActorId::from([1]));
|
|
1107
|
+
let mut doc2 = AutoCommit::new();
|
|
1108
|
+
doc2.set_actor(ActorId::from([2]));
|
|
1109
|
+
|
|
1110
|
+
doc1.put(ROOT, "key", ScalarValue::counter(1)).unwrap();
|
|
1111
|
+
doc2.put(ROOT, "key", "mystring").unwrap();
|
|
1112
|
+
doc1.merge(&mut doc2).unwrap();
|
|
1113
|
+
|
|
1114
|
+
assert!(matches!(doc1.increment(ROOT, "key", 2), Ok(())));
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
#[test]
|
|
1118
|
+
fn increment_non_counter_list() {
|
|
1119
|
+
let mut doc = AutoCommit::new();
|
|
1120
|
+
let list = doc.put_object(ROOT, "list", ObjType::List).unwrap();
|
|
1121
|
+
|
|
1122
|
+
// can't increment a non-counter
|
|
1123
|
+
doc.insert(&list, 0, "mystring").unwrap();
|
|
1124
|
+
assert!(matches!(
|
|
1125
|
+
doc.increment(&list, 0, 2),
|
|
1126
|
+
Err(AutomergeError::MissingCounter)
|
|
1127
|
+
));
|
|
1128
|
+
|
|
1129
|
+
// can increment a counter
|
|
1130
|
+
doc.insert(&list, 0, ScalarValue::counter(1)).unwrap();
|
|
1131
|
+
assert!(matches!(doc.increment(&list, 0, 2), Ok(())));
|
|
1132
|
+
|
|
1133
|
+
// can increment a counter that is part of a conflict
|
|
1134
|
+
let mut doc1 = AutoCommit::new();
|
|
1135
|
+
doc1.set_actor(ActorId::from([1]));
|
|
1136
|
+
let list = doc1.put_object(ROOT, "list", ObjType::List).unwrap();
|
|
1137
|
+
doc1.insert(&list, 0, ()).unwrap();
|
|
1138
|
+
let mut doc2 = doc1.fork();
|
|
1139
|
+
doc2.set_actor(ActorId::from([2]));
|
|
1140
|
+
|
|
1141
|
+
doc1.put(&list, 0, ScalarValue::counter(1)).unwrap();
|
|
1142
|
+
doc2.put(&list, 0, "mystring").unwrap();
|
|
1143
|
+
doc1.merge(&mut doc2).unwrap();
|
|
1144
|
+
|
|
1145
|
+
assert!(matches!(doc1.increment(&list, 0, 2), Ok(())));
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
#[test]
|
|
1149
|
+
fn test_local_inc_in_map() {
|
|
1150
|
+
let mut v = [ActorId::random(), ActorId::random(), ActorId::random()];
|
|
1151
|
+
v.sort();
|
|
1152
|
+
let actor1 = v[0].clone();
|
|
1153
|
+
let actor2 = v[1].clone();
|
|
1154
|
+
let actor3 = v[2].clone();
|
|
1155
|
+
|
|
1156
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
1157
|
+
doc1.put(&automerge::ROOT, "hello", "world").unwrap();
|
|
1158
|
+
|
|
1159
|
+
let mut doc2 = AutoCommit::load(&doc1.save()).unwrap();
|
|
1160
|
+
doc2.set_actor(actor2);
|
|
1161
|
+
|
|
1162
|
+
let mut doc3 = AutoCommit::load(&doc1.save()).unwrap();
|
|
1163
|
+
doc3.set_actor(actor3);
|
|
1164
|
+
|
|
1165
|
+
doc1.put(ROOT, "cnt", 20_u64).unwrap();
|
|
1166
|
+
doc2.put(ROOT, "cnt", ScalarValue::counter(0)).unwrap();
|
|
1167
|
+
doc3.put(ROOT, "cnt", ScalarValue::counter(10)).unwrap();
|
|
1168
|
+
doc1.merge(&mut doc2).unwrap();
|
|
1169
|
+
doc1.merge(&mut doc3).unwrap();
|
|
1170
|
+
|
|
1171
|
+
assert_doc! {doc1.document(), map!{
|
|
1172
|
+
"cnt" => {
|
|
1173
|
+
20_u64,
|
|
1174
|
+
ScalarValue::counter(0),
|
|
1175
|
+
ScalarValue::counter(10),
|
|
1176
|
+
},
|
|
1177
|
+
"hello" => {"world"},
|
|
1178
|
+
}};
|
|
1179
|
+
|
|
1180
|
+
doc1.increment(ROOT, "cnt", 5).unwrap();
|
|
1181
|
+
|
|
1182
|
+
assert_doc! {doc1.document(), map!{
|
|
1183
|
+
"cnt" => {
|
|
1184
|
+
ScalarValue::counter(5),
|
|
1185
|
+
ScalarValue::counter(15),
|
|
1186
|
+
},
|
|
1187
|
+
"hello" => {"world"},
|
|
1188
|
+
}};
|
|
1189
|
+
let mut doc4 = AutoCommit::load(&doc1.save()).unwrap();
|
|
1190
|
+
assert_eq!(doc4.save(), doc1.save());
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
#[test]
|
|
1194
|
+
fn test_merging_test_conflicts_then_saving_and_loading() {
|
|
1195
|
+
let (actor1, actor2) = sorted_actors();
|
|
1196
|
+
|
|
1197
|
+
let mut doc1 = new_doc_with_actor(actor1);
|
|
1198
|
+
let text = doc1.put_object(ROOT, "text", ObjType::Text).unwrap();
|
|
1199
|
+
doc1.splice_text(&text, 0, 0, "hello").unwrap();
|
|
1200
|
+
|
|
1201
|
+
let mut doc2 = AutoCommit::load(&doc1.save()).unwrap();
|
|
1202
|
+
doc2.set_actor(actor2);
|
|
1203
|
+
|
|
1204
|
+
assert_doc! {&doc2, map!{
|
|
1205
|
+
"text" => { list![{"h"}, {"e"}, {"l"}, {"l"}, {"o"}]},
|
|
1206
|
+
}};
|
|
1207
|
+
|
|
1208
|
+
doc2.splice_text(&text, 4, 1, "").unwrap();
|
|
1209
|
+
doc2.splice_text(&text, 4, 0, "!").unwrap();
|
|
1210
|
+
doc2.splice_text(&text, 5, 0, " ").unwrap();
|
|
1211
|
+
doc2.splice_text(&text, 6, 0, "world").unwrap();
|
|
1212
|
+
|
|
1213
|
+
assert_doc!(
|
|
1214
|
+
&doc2,
|
|
1215
|
+
map! {
|
|
1216
|
+
"text" => { list![{"h"}, {"e"}, {"l"}, {"l"}, {"!"}, {" "}, {"w"} , {"o"}, {"r"}, {"l"}, {"d"}]}
|
|
1217
|
+
}
|
|
1218
|
+
);
|
|
1219
|
+
|
|
1220
|
+
let doc3 = AutoCommit::load(&doc2.save()).unwrap();
|
|
1221
|
+
|
|
1222
|
+
assert_doc!(
|
|
1223
|
+
&doc3,
|
|
1224
|
+
map! {
|
|
1225
|
+
"text" => { list![{"h"}, {"e"}, {"l"}, {"l"}, {"!"}, {" "}, {"w"} , {"o"}, {"r"}, {"l"}, {"d"}]}
|
|
1226
|
+
}
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/// Surfaces an error which occurs when loading a document with a change which only contains a
|
|
1231
|
+
/// delete operation. In this case the delete operation doesn't appear in the encoded document
|
|
1232
|
+
/// operations except as a succ, so the max_op was calculated incorectly.
|
|
1233
|
+
#[test]
|
|
1234
|
+
fn delete_only_change() {
|
|
1235
|
+
let actor = automerge::ActorId::random();
|
|
1236
|
+
let mut doc1 = automerge::Automerge::new().with_actor(actor.clone());
|
|
1237
|
+
let list = doc1
|
|
1238
|
+
.transact::<_, _, automerge::AutomergeError>(|d| {
|
|
1239
|
+
let l = d.put_object(&automerge::ROOT, "list", ObjType::List)?;
|
|
1240
|
+
d.insert(&l, 0, 'a')?;
|
|
1241
|
+
Ok(l)
|
|
1242
|
+
})
|
|
1243
|
+
.unwrap()
|
|
1244
|
+
.result;
|
|
1245
|
+
|
|
1246
|
+
let mut doc2 = automerge::Automerge::load(&doc1.save())
|
|
1247
|
+
.unwrap()
|
|
1248
|
+
.with_actor(actor.clone());
|
|
1249
|
+
doc2.transact::<_, _, automerge::AutomergeError>(|d| d.delete(&list, 0))
|
|
1250
|
+
.unwrap();
|
|
1251
|
+
|
|
1252
|
+
let mut doc3 = automerge::Automerge::load(&doc2.save())
|
|
1253
|
+
.unwrap()
|
|
1254
|
+
.with_actor(actor.clone());
|
|
1255
|
+
doc3.transact(|d| d.insert(&list, 0, "b")).unwrap();
|
|
1256
|
+
|
|
1257
|
+
let doc4 = automerge::Automerge::load(&doc3.save())
|
|
1258
|
+
.unwrap()
|
|
1259
|
+
.with_actor(actor);
|
|
1260
|
+
|
|
1261
|
+
let changes = doc4.get_changes(&[]);
|
|
1262
|
+
assert_eq!(changes.len(), 3);
|
|
1263
|
+
let c = &changes[2];
|
|
1264
|
+
assert_eq!(c.start_op().get(), 4);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/// Expose an error where a document which contained a create operation without any subsequent
|
|
1268
|
+
/// operations targeting the created object did not load the object correctly.
|
|
1269
|
+
#[test]
|
|
1270
|
+
fn save_and_reload_create_object() {
|
|
1271
|
+
let actor = automerge::ActorId::random();
|
|
1272
|
+
let mut doc = automerge::Automerge::new().with_actor(actor);
|
|
1273
|
+
|
|
1274
|
+
// Create a change containing an object but no other operations
|
|
1275
|
+
let list = doc
|
|
1276
|
+
.transact::<_, _, automerge::AutomergeError>(|d| {
|
|
1277
|
+
d.put_object(&automerge::ROOT, "foo", ObjType::List)
|
|
1278
|
+
})
|
|
1279
|
+
.unwrap()
|
|
1280
|
+
.result;
|
|
1281
|
+
|
|
1282
|
+
// Save and load the change
|
|
1283
|
+
let mut doc2 = automerge::Automerge::load(&doc.save()).unwrap();
|
|
1284
|
+
doc2.transact::<_, _, automerge::AutomergeError>(|d| {
|
|
1285
|
+
d.insert(&list, 0, 1_u64)?;
|
|
1286
|
+
Ok(())
|
|
1287
|
+
})
|
|
1288
|
+
.unwrap();
|
|
1289
|
+
|
|
1290
|
+
assert_doc!(&doc2, map! {"foo" => { list! [{1_u64}]}});
|
|
1291
|
+
|
|
1292
|
+
let _doc3 = automerge::Automerge::load(&doc2.save()).unwrap();
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
#[test]
|
|
1296
|
+
fn test_compressed_changes() {
|
|
1297
|
+
let mut doc = new_doc();
|
|
1298
|
+
// crate::storage::DEFLATE_MIN_SIZE is 250, so this should trigger compression
|
|
1299
|
+
doc.put(ROOT, "bytes", ScalarValue::Bytes(vec![10; 300]))
|
|
1300
|
+
.unwrap();
|
|
1301
|
+
let mut change = doc.get_last_local_change().unwrap().clone();
|
|
1302
|
+
let uncompressed = change.raw_bytes().to_vec();
|
|
1303
|
+
assert!(uncompressed.len() > 256);
|
|
1304
|
+
let compressed = change.bytes().to_vec();
|
|
1305
|
+
assert!(compressed.len() < uncompressed.len());
|
|
1306
|
+
|
|
1307
|
+
let reloaded = automerge::Change::try_from(&compressed[..]).unwrap();
|
|
1308
|
+
assert_eq!(change.raw_bytes(), reloaded.raw_bytes());
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
#[test]
|
|
1312
|
+
fn test_compressed_doc_cols() {
|
|
1313
|
+
// In this test, the keyCtr column is long enough for deflate compression to kick in, but the
|
|
1314
|
+
// keyStr column is short. Thus, the deflate bit gets set for keyCtr but not for keyStr.
|
|
1315
|
+
// When checking whether the columns appear in ascending order, we must ignore the deflate bit.
|
|
1316
|
+
let mut doc = new_doc();
|
|
1317
|
+
let list = doc.put_object(ROOT, "list", ObjType::List).unwrap();
|
|
1318
|
+
let mut expected = Vec::new();
|
|
1319
|
+
for i in 0..200 {
|
|
1320
|
+
doc.insert(&list, i, i as u64).unwrap();
|
|
1321
|
+
expected.push(i as u64);
|
|
1322
|
+
}
|
|
1323
|
+
let uncompressed = doc.save_nocompress();
|
|
1324
|
+
let compressed = doc.save();
|
|
1325
|
+
assert!(compressed.len() < uncompressed.len());
|
|
1326
|
+
let loaded = automerge::Automerge::load(&compressed).unwrap();
|
|
1327
|
+
assert_doc!(
|
|
1328
|
+
&loaded,
|
|
1329
|
+
map! {
|
|
1330
|
+
"list" => { expected}
|
|
1331
|
+
}
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
#[test]
|
|
1336
|
+
fn test_change_encoding_expanded_change_round_trip() {
|
|
1337
|
+
let change_bytes: Vec<u8> = vec![
|
|
1338
|
+
0x85, 0x6f, 0x4a, 0x83, // magic bytes
|
|
1339
|
+
0xb2, 0x98, 0x9e, 0xa9, // checksum
|
|
1340
|
+
1, 61, 0, 2, 0x12, 0x34, // chunkType: change, length, deps, actor '1234'
|
|
1341
|
+
1, 1, 252, 250, 220, 255, 5, // seq, startOp, time
|
|
1342
|
+
14, 73, 110, 105, 116, 105, 97, 108, 105, 122, 97, 116, 105, 111,
|
|
1343
|
+
110, // message: 'Initialization'
|
|
1344
|
+
0, 6, // actor list, column count
|
|
1345
|
+
0x15, 3, 0x34, 1, 0x42, 2, // keyStr, insert, action
|
|
1346
|
+
0x56, 2, 0x57, 1, 0x70, 2, // valLen, valRaw, predNum
|
|
1347
|
+
0x7f, 1, 0x78, // keyStr: 'x'
|
|
1348
|
+
1, // insert: false
|
|
1349
|
+
0x7f, 1, // action: set
|
|
1350
|
+
0x7f, 19, // valLen: 1 byte of type uint
|
|
1351
|
+
1, // valRaw: 1
|
|
1352
|
+
0x7f, 0, // predNum: 0
|
|
1353
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 10 trailing bytes
|
|
1354
|
+
];
|
|
1355
|
+
let change = automerge::Change::try_from(&change_bytes[..]).unwrap();
|
|
1356
|
+
assert_eq!(change.raw_bytes(), change_bytes);
|
|
1357
|
+
let expanded = automerge::ExpandedChange::from(&change);
|
|
1358
|
+
let unexpanded: automerge::Change = expanded.into();
|
|
1359
|
+
assert_eq!(unexpanded.raw_bytes(), change_bytes);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
#[test]
|
|
1363
|
+
fn save_and_load_incremented_counter() {
|
|
1364
|
+
let mut doc = AutoCommit::new();
|
|
1365
|
+
doc.put(ROOT, "counter", ScalarValue::counter(1)).unwrap();
|
|
1366
|
+
doc.commit();
|
|
1367
|
+
doc.increment(ROOT, "counter", 1).unwrap();
|
|
1368
|
+
doc.commit();
|
|
1369
|
+
let changes1: Vec<Change> = doc.get_changes(&[]).into_iter().collect();
|
|
1370
|
+
let json: Vec<_> = changes1
|
|
1371
|
+
.iter()
|
|
1372
|
+
.map(|c| serde_json::to_string(&c.decode()).unwrap())
|
|
1373
|
+
.collect();
|
|
1374
|
+
let changes2: Vec<Change> = json
|
|
1375
|
+
.iter()
|
|
1376
|
+
.map(|j| serde_json::from_str::<ExpandedChange>(j).unwrap().into())
|
|
1377
|
+
.collect();
|
|
1378
|
+
|
|
1379
|
+
assert_eq!(changes1, changes2);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
#[test]
|
|
1383
|
+
fn load_incremental_with_corrupted_tail() {
|
|
1384
|
+
let mut doc = AutoCommit::new();
|
|
1385
|
+
doc.put(ROOT, "key", ScalarValue::Str("value".into()))
|
|
1386
|
+
.unwrap();
|
|
1387
|
+
doc.commit();
|
|
1388
|
+
let mut bytes = doc.save();
|
|
1389
|
+
bytes.extend_from_slice(&[1, 2, 3, 4]);
|
|
1390
|
+
let mut loaded = Automerge::new();
|
|
1391
|
+
let loaded_len = loaded.load_incremental(&bytes).unwrap();
|
|
1392
|
+
assert_eq!(loaded_len, 1);
|
|
1393
|
+
assert_doc!(
|
|
1394
|
+
&loaded,
|
|
1395
|
+
map! {
|
|
1396
|
+
"key" => { "value" },
|
|
1397
|
+
}
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
#[test]
|
|
1402
|
+
fn load_doc_with_deleted_objects() {
|
|
1403
|
+
// Reproduces an issue where a document with deleted objects failed to load
|
|
1404
|
+
let mut doc = AutoCommit::new();
|
|
1405
|
+
doc.put_object(ROOT, "list", ObjType::List).unwrap();
|
|
1406
|
+
doc.put_object(ROOT, "text", ObjType::Text).unwrap();
|
|
1407
|
+
doc.put_object(ROOT, "map", ObjType::Map).unwrap();
|
|
1408
|
+
doc.put_object(ROOT, "table", ObjType::Table).unwrap();
|
|
1409
|
+
doc.delete(&ROOT, "list").unwrap();
|
|
1410
|
+
doc.delete(&ROOT, "text").unwrap();
|
|
1411
|
+
doc.delete(&ROOT, "map").unwrap();
|
|
1412
|
+
doc.delete(&ROOT, "table").unwrap();
|
|
1413
|
+
let saved = doc.save();
|
|
1414
|
+
Automerge::load(&saved).unwrap();
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
#[test]
|
|
1418
|
+
fn insert_after_many_deletes() {
|
|
1419
|
+
let mut doc = AutoCommit::new();
|
|
1420
|
+
let obj = doc.put_object(&ROOT, "object", ObjType::Map).unwrap();
|
|
1421
|
+
for i in 0..100 {
|
|
1422
|
+
doc.put(&obj, i.to_string(), i).unwrap();
|
|
1423
|
+
doc.delete(&obj, i.to_string()).unwrap();
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
#[test]
|
|
1428
|
+
fn simple_bad_saveload() {
|
|
1429
|
+
let mut doc = Automerge::new();
|
|
1430
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1431
|
+
d.put(ROOT, "count", 0)?;
|
|
1432
|
+
Ok(())
|
|
1433
|
+
})
|
|
1434
|
+
.unwrap();
|
|
1435
|
+
|
|
1436
|
+
doc.transact::<_, _, AutomergeError>(|_d| Ok(())).unwrap();
|
|
1437
|
+
|
|
1438
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1439
|
+
d.put(ROOT, "count", 0)?;
|
|
1440
|
+
Ok(())
|
|
1441
|
+
})
|
|
1442
|
+
.unwrap();
|
|
1443
|
+
|
|
1444
|
+
let bytes = doc.save();
|
|
1445
|
+
Automerge::load(&bytes).unwrap();
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
#[test]
|
|
1449
|
+
fn ops_on_wrong_objets() -> Result<(), AutomergeError> {
|
|
1450
|
+
let mut doc = AutoCommit::new();
|
|
1451
|
+
let list = doc.put_object(&automerge::ROOT, "list", ObjType::List)?;
|
|
1452
|
+
doc.insert(&list, 0, "a")?;
|
|
1453
|
+
doc.insert(&list, 1, "b")?;
|
|
1454
|
+
let e1 = doc.put(&list, "a", "AAA");
|
|
1455
|
+
assert_eq!(e1, Err(AutomergeError::InvalidOp(ObjType::List)));
|
|
1456
|
+
let e2 = doc.splice_text(&list, 0, 0, "hello world");
|
|
1457
|
+
assert_eq!(e2, Err(AutomergeError::InvalidOp(ObjType::List)));
|
|
1458
|
+
let map = doc.put_object(&automerge::ROOT, "map", ObjType::Map)?;
|
|
1459
|
+
doc.put(&map, "a", "AAA")?;
|
|
1460
|
+
doc.put(&map, "b", "BBB")?;
|
|
1461
|
+
let e3 = doc.insert(&map, 0, "b");
|
|
1462
|
+
assert_eq!(e3, Err(AutomergeError::InvalidOp(ObjType::Map)));
|
|
1463
|
+
let e4 = doc.splice_text(&map, 0, 0, "hello world");
|
|
1464
|
+
assert_eq!(e4, Err(AutomergeError::InvalidOp(ObjType::Map)));
|
|
1465
|
+
let text = doc.put_object(&automerge::ROOT, "text", ObjType::Text)?;
|
|
1466
|
+
doc.splice_text(&text, 0, 0, "hello world")?;
|
|
1467
|
+
let e5 = doc.put(&text, "a", "AAA");
|
|
1468
|
+
assert_eq!(e5, Err(AutomergeError::InvalidOp(ObjType::Text)));
|
|
1469
|
+
//let e6 = doc.insert(&text, 0, "b");
|
|
1470
|
+
//assert_eq!(e6, Err(AutomergeError::InvalidOp(ObjType::Text)));
|
|
1471
|
+
Ok(())
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
#[test]
|
|
1475
|
+
fn fuzz_crashers() {
|
|
1476
|
+
let paths = fs::read_dir("./tests/fuzz-crashers").unwrap();
|
|
1477
|
+
|
|
1478
|
+
for path in paths {
|
|
1479
|
+
// uncomment this line to figure out which fixture is crashing:
|
|
1480
|
+
println!("{:?}", path.as_ref().unwrap().path().display());
|
|
1481
|
+
let bytes = fs::read(path.as_ref().unwrap().path());
|
|
1482
|
+
let res = Automerge::load(&bytes.unwrap());
|
|
1483
|
+
assert!(res.is_err());
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
fn fixture(name: &str) -> Vec<u8> {
|
|
1488
|
+
fs::read("./tests/fixtures/".to_owned() + name).unwrap()
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
#[test]
|
|
1492
|
+
fn overlong_leb() {
|
|
1493
|
+
// the value metadata says "2", but the LEB is only 1-byte long and there's an extra 0
|
|
1494
|
+
assert!(Automerge::load(&fixture("counter_value_has_incorrect_meta.automerge")).is_err());
|
|
1495
|
+
// the LEB is overlong (using 2 bytes where one would have sufficed)
|
|
1496
|
+
assert!(Automerge::load(&fixture("counter_value_is_overlong.automerge")).is_err());
|
|
1497
|
+
// the LEB is correct
|
|
1498
|
+
assert!(Automerge::load(&fixture("counter_value_is_ok.automerge")).is_ok());
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
#[test]
|
|
1502
|
+
fn load() {
|
|
1503
|
+
fn check_fixture(name: &str) {
|
|
1504
|
+
let doc = Automerge::load(&fixture(name)).unwrap();
|
|
1505
|
+
let map_id = doc.get(ROOT, "a").unwrap().unwrap().1;
|
|
1506
|
+
assert_eq!(doc.get(map_id, "a").unwrap().unwrap().0, "b".into());
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
check_fixture("two_change_chunks.automerge");
|
|
1510
|
+
check_fixture("two_change_chunks_compressed.automerge");
|
|
1511
|
+
check_fixture("two_change_chunks_out_of_order.automerge");
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
#[test]
|
|
1515
|
+
fn negative_64() {
|
|
1516
|
+
let mut doc = Automerge::new();
|
|
1517
|
+
assert!(doc.transact(|d| { d.put(ROOT, "a", -64_i64) }).is_ok())
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
#[test]
|
|
1521
|
+
fn obj_id_64bits() {
|
|
1522
|
+
// this change has an opId of 2**42, which when cast to a 32-bit int gives 0.
|
|
1523
|
+
// The file should either fail to load (a limit of ~4 billion ops per doc seems reasonable), or be handled correctly.
|
|
1524
|
+
if let Ok(doc) = Automerge::load(&fixture("64bit_obj_id_change.automerge")) {
|
|
1525
|
+
let map_id = doc.get(ROOT, "a").unwrap().unwrap().1;
|
|
1526
|
+
assert!(map_id != ROOT)
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// this fixture is the same as the above, but as a document chunk.
|
|
1530
|
+
if let Ok(doc) = Automerge::load(&fixture("64bit_obj_id_doc.automerge")) {
|
|
1531
|
+
let map_id = doc.get(ROOT, "a").unwrap().unwrap().1;
|
|
1532
|
+
assert!(map_id != ROOT)
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
#[test]
|
|
1537
|
+
fn bad_change_on_optree_node_boundary() {
|
|
1538
|
+
let mut doc = Automerge::new();
|
|
1539
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1540
|
+
d.put(ROOT, "a", "z")?;
|
|
1541
|
+
d.put(ROOT, "b", 0)?;
|
|
1542
|
+
d.put(ROOT, "c", 0)?;
|
|
1543
|
+
Ok(())
|
|
1544
|
+
})
|
|
1545
|
+
.unwrap();
|
|
1546
|
+
let iterations = 15_u64;
|
|
1547
|
+
for i in 0_u64..iterations {
|
|
1548
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1549
|
+
let s = "a".repeat(i as usize);
|
|
1550
|
+
d.put(ROOT, "a", s)?;
|
|
1551
|
+
d.put(ROOT, "b", i + 1)?;
|
|
1552
|
+
d.put(ROOT, "c", i + 1)?;
|
|
1553
|
+
Ok(())
|
|
1554
|
+
})
|
|
1555
|
+
.unwrap();
|
|
1556
|
+
}
|
|
1557
|
+
let mut doc2 = Automerge::load(doc.save().as_slice()).unwrap();
|
|
1558
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1559
|
+
let i = iterations + 2;
|
|
1560
|
+
let s = "a".repeat(i as usize);
|
|
1561
|
+
d.put(ROOT, "a", s)?;
|
|
1562
|
+
d.put(ROOT, "b", i)?;
|
|
1563
|
+
d.put(ROOT, "c", i)?;
|
|
1564
|
+
Ok(())
|
|
1565
|
+
})
|
|
1566
|
+
.unwrap();
|
|
1567
|
+
let change = doc.get_changes(&doc2.get_heads());
|
|
1568
|
+
doc2.apply_changes(change.into_iter().collect::<Vec<_>>())
|
|
1569
|
+
.unwrap();
|
|
1570
|
+
Automerge::load(doc2.save().as_slice()).unwrap();
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
#[test]
|
|
1574
|
+
fn regression_nth_miscount() {
|
|
1575
|
+
let mut doc = Automerge::new();
|
|
1576
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1577
|
+
let list_id = d.put_object(ROOT, "listval", ObjType::List).unwrap();
|
|
1578
|
+
for i in 0..30 {
|
|
1579
|
+
d.insert(&list_id, i, ScalarValue::Null).unwrap();
|
|
1580
|
+
let map = d.put_object(&list_id, i, ObjType::Map).unwrap();
|
|
1581
|
+
d.put(map, "test", ScalarValue::Int(i.try_into().unwrap()))
|
|
1582
|
+
.unwrap();
|
|
1583
|
+
}
|
|
1584
|
+
Ok(())
|
|
1585
|
+
})
|
|
1586
|
+
.unwrap();
|
|
1587
|
+
for i in 0..30 {
|
|
1588
|
+
let (obj_type, list_id) = doc.get(ROOT, "listval").unwrap().unwrap();
|
|
1589
|
+
assert_eq!(obj_type, Value::Object(ObjType::List));
|
|
1590
|
+
let (obj_type, map_id) = doc.get(&list_id, i).unwrap().unwrap();
|
|
1591
|
+
assert_eq!(obj_type, Value::Object(ObjType::Map));
|
|
1592
|
+
let (obj_type, _) = doc.get(map_id, "test").unwrap().unwrap();
|
|
1593
|
+
assert_eq!(
|
|
1594
|
+
obj_type,
|
|
1595
|
+
Value::Scalar(std::borrow::Cow::Borrowed(&ScalarValue::Int(
|
|
1596
|
+
i.try_into().unwrap()
|
|
1597
|
+
)))
|
|
1598
|
+
)
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
#[test]
|
|
1603
|
+
fn regression_nth_miscount_smaller() {
|
|
1604
|
+
let mut doc = Automerge::new();
|
|
1605
|
+
doc.transact::<_, _, AutomergeError>(|d| {
|
|
1606
|
+
let list_id = d.put_object(ROOT, "listval", ObjType::List).unwrap();
|
|
1607
|
+
for i in 0..B * 4 {
|
|
1608
|
+
d.insert(&list_id, i, ScalarValue::Null).unwrap();
|
|
1609
|
+
d.put(&list_id, i, ScalarValue::Int(i.try_into().unwrap()))
|
|
1610
|
+
.unwrap();
|
|
1611
|
+
}
|
|
1612
|
+
Ok(())
|
|
1613
|
+
})
|
|
1614
|
+
.unwrap();
|
|
1615
|
+
for i in 0..B * 4 {
|
|
1616
|
+
let (obj_type, list_id) = doc.get(ROOT, "listval").unwrap().unwrap();
|
|
1617
|
+
assert_eq!(obj_type, Value::Object(ObjType::List));
|
|
1618
|
+
let (obj_type, _) = doc.get(list_id, i).unwrap().unwrap();
|
|
1619
|
+
assert_eq!(
|
|
1620
|
+
obj_type,
|
|
1621
|
+
Value::Scalar(std::borrow::Cow::Borrowed(&ScalarValue::Int(
|
|
1622
|
+
i.try_into().unwrap()
|
|
1623
|
+
)))
|
|
1624
|
+
)
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
#[test]
|
|
1629
|
+
fn regression_insert_opid() {
|
|
1630
|
+
let mut doc = Automerge::new();
|
|
1631
|
+
let mut tx = doc.transaction();
|
|
1632
|
+
let list_id = tx
|
|
1633
|
+
.put_object(&automerge::ROOT, "list", ObjType::List)
|
|
1634
|
+
.unwrap();
|
|
1635
|
+
tx.commit();
|
|
1636
|
+
|
|
1637
|
+
let change1 = doc.get_last_local_change().unwrap().clone();
|
|
1638
|
+
let mut tx = doc.transaction();
|
|
1639
|
+
|
|
1640
|
+
const N: usize = 30;
|
|
1641
|
+
for i in 0..=N {
|
|
1642
|
+
tx.insert(&list_id, i, ScalarValue::Null).unwrap();
|
|
1643
|
+
tx.put(&list_id, i, ScalarValue::Int(i as i64)).unwrap();
|
|
1644
|
+
}
|
|
1645
|
+
tx.commit();
|
|
1646
|
+
|
|
1647
|
+
let change2 = doc.get_last_local_change().unwrap().clone();
|
|
1648
|
+
let mut new_doc = Automerge::new();
|
|
1649
|
+
let mut patch_log = PatchLog::active();
|
|
1650
|
+
new_doc
|
|
1651
|
+
.apply_changes_log_patches(vec![change1], &mut patch_log)
|
|
1652
|
+
.unwrap();
|
|
1653
|
+
new_doc
|
|
1654
|
+
.apply_changes_log_patches(vec![change2], &mut patch_log)
|
|
1655
|
+
.unwrap();
|
|
1656
|
+
|
|
1657
|
+
for i in 0..=N {
|
|
1658
|
+
let (doc_val, _) = doc.get(&list_id, i).unwrap().unwrap();
|
|
1659
|
+
let (new_doc_val, _) = new_doc.get(&list_id, i).unwrap().unwrap();
|
|
1660
|
+
|
|
1661
|
+
assert_eq!(
|
|
1662
|
+
doc_val,
|
|
1663
|
+
Value::Scalar(std::borrow::Cow::Owned(ScalarValue::Int(i as i64)))
|
|
1664
|
+
);
|
|
1665
|
+
assert_eq!(
|
|
1666
|
+
new_doc_val,
|
|
1667
|
+
Value::Scalar(std::borrow::Cow::Owned(ScalarValue::Int(i as i64)))
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
let patches = new_doc.make_patches(&mut patch_log);
|
|
1672
|
+
let mut values = SequenceTree::new();
|
|
1673
|
+
for i in 0..=N {
|
|
1674
|
+
values.push((
|
|
1675
|
+
Value::Scalar(std::borrow::Cow::Owned(ScalarValue::Int(i as i64))),
|
|
1676
|
+
ObjId::Id((2 * (i + 1) + 1) as u64, doc.get_actor().clone(), 0),
|
|
1677
|
+
false,
|
|
1678
|
+
));
|
|
1679
|
+
}
|
|
1680
|
+
let expected_patches = vec![
|
|
1681
|
+
Patch {
|
|
1682
|
+
obj: ROOT,
|
|
1683
|
+
path: vec![],
|
|
1684
|
+
action: PatchAction::PutMap {
|
|
1685
|
+
key: "list".to_string(),
|
|
1686
|
+
value: (
|
|
1687
|
+
Value::Object(ObjType::List),
|
|
1688
|
+
ObjId::Id(1, doc.get_actor().clone(), 0),
|
|
1689
|
+
),
|
|
1690
|
+
conflict: false,
|
|
1691
|
+
},
|
|
1692
|
+
},
|
|
1693
|
+
Patch {
|
|
1694
|
+
obj: ObjId::Id(1, doc.get_actor().clone(), 0),
|
|
1695
|
+
path: vec![(ROOT, Prop::Map("list".into()))],
|
|
1696
|
+
action: PatchAction::Insert { index: 0, values },
|
|
1697
|
+
},
|
|
1698
|
+
];
|
|
1699
|
+
assert_eq!(patches, expected_patches);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
#[test]
|
|
1703
|
+
fn big_list() {
|
|
1704
|
+
let mut doc = Automerge::new();
|
|
1705
|
+
let mut tx = doc.transaction();
|
|
1706
|
+
let list_id = tx.put_object(&ROOT, "list", ObjType::List).unwrap();
|
|
1707
|
+
tx.commit();
|
|
1708
|
+
|
|
1709
|
+
let change1 = doc.get_last_local_change().unwrap().clone();
|
|
1710
|
+
let mut tx = doc.transaction();
|
|
1711
|
+
|
|
1712
|
+
const N: usize = B;
|
|
1713
|
+
for i in 0..=N {
|
|
1714
|
+
tx.insert(&list_id, i, ScalarValue::Null).unwrap();
|
|
1715
|
+
}
|
|
1716
|
+
for i in 0..=N {
|
|
1717
|
+
tx.put_object(&list_id, i, ObjType::Map).unwrap();
|
|
1718
|
+
}
|
|
1719
|
+
tx.commit();
|
|
1720
|
+
|
|
1721
|
+
let change2 = doc.get_last_local_change().unwrap().clone();
|
|
1722
|
+
let mut new_doc = Automerge::new();
|
|
1723
|
+
let mut patch_log = PatchLog::active();
|
|
1724
|
+
new_doc
|
|
1725
|
+
.apply_changes_log_patches(vec![change1], &mut patch_log)
|
|
1726
|
+
.unwrap();
|
|
1727
|
+
new_doc
|
|
1728
|
+
.apply_changes_log_patches(vec![change2], &mut patch_log)
|
|
1729
|
+
.unwrap();
|
|
1730
|
+
|
|
1731
|
+
let patches = new_doc.make_patches(&mut patch_log);
|
|
1732
|
+
println!("PATCH = {:?}", patches.last());
|
|
1733
|
+
let matches = match &patches.last().unwrap().action {
|
|
1734
|
+
PatchAction::PutSeq { index: N, .. } => true,
|
|
1735
|
+
PatchAction::Insert { index: 0, values } if values.len() == N + 1 => true,
|
|
1736
|
+
_ => false,
|
|
1737
|
+
};
|
|
1738
|
+
assert!(matches);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
#[test]
|
|
1742
|
+
fn marks() {
|
|
1743
|
+
let mut doc = Automerge::new();
|
|
1744
|
+
let mut tx = doc.transaction();
|
|
1745
|
+
|
|
1746
|
+
let text_id = tx.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1747
|
+
|
|
1748
|
+
tx.splice_text(&text_id, 0, 0, "hello world").unwrap();
|
|
1749
|
+
|
|
1750
|
+
let mark = Mark::new("bold".to_string(), true, 0, "hello".len());
|
|
1751
|
+
tx.mark(&text_id, mark, ExpandMark::Both).unwrap();
|
|
1752
|
+
|
|
1753
|
+
// add " cool" (it will be bold because ExpandMark::Both)
|
|
1754
|
+
tx.splice_text(&text_id, "hello".len(), 0, " cool").unwrap();
|
|
1755
|
+
|
|
1756
|
+
// unbold "hello"
|
|
1757
|
+
tx.unmark(&text_id, "bold", 0, "hello".len(), ExpandMark::Before)
|
|
1758
|
+
.unwrap();
|
|
1759
|
+
|
|
1760
|
+
// insert "why " before hello.
|
|
1761
|
+
tx.splice_text(&text_id, 0, 0, "why ").unwrap();
|
|
1762
|
+
|
|
1763
|
+
let marks = tx.marks(&text_id).unwrap();
|
|
1764
|
+
|
|
1765
|
+
assert_eq!(marks[0].start, 9);
|
|
1766
|
+
assert_eq!(marks[0].end, 14);
|
|
1767
|
+
assert_eq!(marks[0].name(), "bold");
|
|
1768
|
+
assert_eq!(marks[0].value(), &ScalarValue::from(true));
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
#[test]
|
|
1772
|
+
fn can_transaction_at() -> Result<(), AutomergeError> {
|
|
1773
|
+
let mut doc1 = Automerge::new();
|
|
1774
|
+
let mut tx = doc1.transaction();
|
|
1775
|
+
let txt = tx.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1776
|
+
tx.put(&ROOT, "size", 100).unwrap();
|
|
1777
|
+
tx.splice_text(&txt, 0, 0, "aaabbbccc")?;
|
|
1778
|
+
tx.commit();
|
|
1779
|
+
let heads1 = doc1.get_heads();
|
|
1780
|
+
let mut tx = doc1.transaction();
|
|
1781
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaabbbccc");
|
|
1782
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1783
|
+
tx.splice_text(&txt, 3, 3, "QQQ")?;
|
|
1784
|
+
tx.put(&ROOT, "size", 200)?;
|
|
1785
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaaQQQccc");
|
|
1786
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(200));
|
|
1787
|
+
tx.commit();
|
|
1788
|
+
|
|
1789
|
+
let mut tx = doc1.transaction_at(PatchLog::null(), &heads1)?;
|
|
1790
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaabbbccc");
|
|
1791
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1792
|
+
tx.splice_text(&txt, 3, 3, "ZZZ")?;
|
|
1793
|
+
tx.put(&ROOT, "size", 300)?;
|
|
1794
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaaZZZccc");
|
|
1795
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(300));
|
|
1796
|
+
tx.commit();
|
|
1797
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaZZZQQQccc");
|
|
1798
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(300));
|
|
1799
|
+
|
|
1800
|
+
let mut tx = doc1.transaction_at(PatchLog::null(), &heads1)?;
|
|
1801
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaabbbccc");
|
|
1802
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1803
|
+
tx.splice_text(&txt, 3, 3, "TTT")?;
|
|
1804
|
+
tx.put(&ROOT, "size", 400)?;
|
|
1805
|
+
assert_eq!(tx.text(&txt).unwrap(), "aaaTTTccc");
|
|
1806
|
+
assert_eq!(tx.get(&ROOT, "size").unwrap().unwrap().0, Value::int(400));
|
|
1807
|
+
tx.commit();
|
|
1808
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaTTTZZZQQQccc");
|
|
1809
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(400));
|
|
1810
|
+
Ok(())
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
#[test]
|
|
1814
|
+
fn can_isolate() -> Result<(), AutomergeError> {
|
|
1815
|
+
let mut doc1 = AutoCommit::new();
|
|
1816
|
+
let txt = doc1.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1817
|
+
doc1.put(&ROOT, "size", 100).unwrap();
|
|
1818
|
+
doc1.splice_text(&txt, 0, 0, "aaabbbccc")?;
|
|
1819
|
+
let heads1 = doc1.get_heads();
|
|
1820
|
+
doc1.put(&ROOT, "size", 150)?;
|
|
1821
|
+
|
|
1822
|
+
doc1.isolate(&heads1);
|
|
1823
|
+
|
|
1824
|
+
let mut doc2 = doc1.fork();
|
|
1825
|
+
doc2.put(&ROOT, "other", 999)?;
|
|
1826
|
+
doc2.splice_text(&txt, 9, 0, "111")?;
|
|
1827
|
+
|
|
1828
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaabbbccc");
|
|
1829
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1830
|
+
doc1.splice_text(&txt, 3, 3, "QQQ")?;
|
|
1831
|
+
println!("---- A ");
|
|
1832
|
+
doc1.put(&ROOT, "size", 200)?;
|
|
1833
|
+
println!("---- N ");
|
|
1834
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaQQQccc");
|
|
1835
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(200));
|
|
1836
|
+
|
|
1837
|
+
let heads2 = doc1.get_heads();
|
|
1838
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaQQQccc");
|
|
1839
|
+
|
|
1840
|
+
doc1.merge(&mut doc2)?;
|
|
1841
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(200));
|
|
1842
|
+
assert_eq!(doc1.get(&ROOT, "other").unwrap(), None);
|
|
1843
|
+
|
|
1844
|
+
doc1.isolate(&heads1);
|
|
1845
|
+
|
|
1846
|
+
assert_ne!(heads1, heads2);
|
|
1847
|
+
|
|
1848
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaabbbccc");
|
|
1849
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1850
|
+
doc1.splice_text(&txt, 3, 3, "ZZZ")?;
|
|
1851
|
+
doc1.put(&ROOT, "size", 300)?;
|
|
1852
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaZZZccc");
|
|
1853
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(300));
|
|
1854
|
+
|
|
1855
|
+
let _heads3 = doc1.get_heads(); // commit
|
|
1856
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaZZZccc");
|
|
1857
|
+
|
|
1858
|
+
doc1.integrate();
|
|
1859
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaZZZQQQccc111");
|
|
1860
|
+
assert_eq!(
|
|
1861
|
+
doc1.get(&ROOT, "other").unwrap().unwrap().0,
|
|
1862
|
+
Value::int(999)
|
|
1863
|
+
);
|
|
1864
|
+
|
|
1865
|
+
doc1.isolate(&heads1);
|
|
1866
|
+
|
|
1867
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaabbbccc");
|
|
1868
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(100));
|
|
1869
|
+
doc1.splice_text(&txt, 3, 3, "TTT")?;
|
|
1870
|
+
doc1.put(&ROOT, "size", 400)?;
|
|
1871
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaTTTccc");
|
|
1872
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(400));
|
|
1873
|
+
|
|
1874
|
+
let _heads4 = doc1.get_heads(); // commit
|
|
1875
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaTTTccc");
|
|
1876
|
+
doc1.integrate();
|
|
1877
|
+
|
|
1878
|
+
assert_eq!(doc1.text(&txt).unwrap(), "aaaTTTZZZQQQccc111");
|
|
1879
|
+
assert_eq!(doc1.get(&ROOT, "size").unwrap().unwrap().0, Value::int(400));
|
|
1880
|
+
Ok(())
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
#[test]
|
|
1884
|
+
fn inserting_text_near_deleted_marks() {
|
|
1885
|
+
let mut doc = Automerge::new();
|
|
1886
|
+
let mut tx = doc.transaction();
|
|
1887
|
+
let text_id = tx.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1888
|
+
tx.splice_text(&text_id, 0, 0, "hello world").unwrap();
|
|
1889
|
+
let mark = Mark::new("bold".to_string(), true, 2, 8);
|
|
1890
|
+
tx.mark(&text_id, mark, ExpandMark::After).unwrap();
|
|
1891
|
+
let mark = Mark::new("link".to_string(), true, 3, 6);
|
|
1892
|
+
tx.mark(&text_id, mark, ExpandMark::None).unwrap();
|
|
1893
|
+
|
|
1894
|
+
tx.splice_text(&text_id, 1, 10, "").unwrap(); // 'h'
|
|
1895
|
+
dbg!(tx.text(&text_id).unwrap(), tx.marks(&text_id).unwrap());
|
|
1896
|
+
tx.splice_text(&text_id, 0, 0, "a").unwrap(); // 'ah'
|
|
1897
|
+
dbg!(tx.text(&text_id).unwrap(), tx.marks(&text_id).unwrap());
|
|
1898
|
+
tx.splice_text(&text_id, 2, 0, "a").unwrap(); // 'ah<bold>a</bold>'
|
|
1899
|
+
dbg!(tx.text(&text_id).unwrap(), tx.marks(&text_id).unwrap());
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
#[test]
|
|
1903
|
+
fn test_load_incremental_partial_load() {
|
|
1904
|
+
let mut doc = Automerge::new();
|
|
1905
|
+
|
|
1906
|
+
let mut tx = doc.transaction();
|
|
1907
|
+
tx.put(&ROOT, "a", 1).unwrap();
|
|
1908
|
+
tx.commit();
|
|
1909
|
+
|
|
1910
|
+
let start_heads = doc.get_heads();
|
|
1911
|
+
let mut tx = doc.transaction();
|
|
1912
|
+
tx.put(&ROOT, "b", 2).unwrap();
|
|
1913
|
+
tx.commit();
|
|
1914
|
+
|
|
1915
|
+
let changes = doc.get_changes(&start_heads);
|
|
1916
|
+
|
|
1917
|
+
let encoded = changes.into_iter().fold(Vec::new(), |mut acc, mut change| {
|
|
1918
|
+
acc.extend_from_slice(change.bytes().as_ref());
|
|
1919
|
+
acc
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
let mut doc2 = Automerge::new();
|
|
1923
|
+
doc2.load_incremental(&encoded).unwrap();
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
#[test]
|
|
1927
|
+
fn test_get_change_meta() {
|
|
1928
|
+
let mut doc = Automerge::new();
|
|
1929
|
+
|
|
1930
|
+
let mut tx = doc.transaction();
|
|
1931
|
+
tx.put(&ROOT, "a", 1).unwrap();
|
|
1932
|
+
tx.commit();
|
|
1933
|
+
|
|
1934
|
+
let start_heads = doc.get_heads();
|
|
1935
|
+
let mut tx = doc.transaction();
|
|
1936
|
+
tx.put(&ROOT, "b", 2).unwrap();
|
|
1937
|
+
tx.commit();
|
|
1938
|
+
|
|
1939
|
+
let changes = doc.get_changes_meta(&start_heads);
|
|
1940
|
+
|
|
1941
|
+
assert_eq!(changes.len(), 1);
|
|
1942
|
+
assert_eq!(*changes[0].actor, *doc.get_actor());
|
|
1943
|
+
assert_eq!(changes[0].seq, 2);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
#[test]
|
|
1947
|
+
fn get_marks_at_heads() {
|
|
1948
|
+
let mut doc = Automerge::new();
|
|
1949
|
+
let mut tx = doc.transaction();
|
|
1950
|
+
let text_id = tx.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1951
|
+
tx.splice_text(&text_id, 0, 0, "hello world").unwrap();
|
|
1952
|
+
let mark = Mark::new("bold".to_string(), true, 0, 10);
|
|
1953
|
+
tx.mark(&text_id, mark, ExpandMark::After).unwrap();
|
|
1954
|
+
tx.commit();
|
|
1955
|
+
|
|
1956
|
+
let heads = doc.get_heads();
|
|
1957
|
+
|
|
1958
|
+
let mut tx = doc.transaction();
|
|
1959
|
+
tx.mark(
|
|
1960
|
+
&text_id,
|
|
1961
|
+
Mark::new("bold".to_string(), ScalarValue::Null, 0, 10),
|
|
1962
|
+
ExpandMark::None,
|
|
1963
|
+
)
|
|
1964
|
+
.unwrap();
|
|
1965
|
+
let mark_map = tx.get_marks(&text_id, 1, Some(&heads)).unwrap();
|
|
1966
|
+
assert_eq!(mark_map.len(), 1);
|
|
1967
|
+
let (mark_name, mark_value) = mark_map.iter().next().unwrap();
|
|
1968
|
+
assert_eq!(mark_name, "bold");
|
|
1969
|
+
assert_eq!(mark_value, &ScalarValue::Boolean(true));
|
|
1970
|
+
|
|
1971
|
+
tx.commit();
|
|
1972
|
+
|
|
1973
|
+
let mark_map = doc.get_marks(&text_id, 1, Some(&heads)).unwrap();
|
|
1974
|
+
assert_eq!(mark_map.len(), 1);
|
|
1975
|
+
let (mark_name, mark_value) = mark_map.iter().next().unwrap();
|
|
1976
|
+
assert_eq!(mark_name, "bold");
|
|
1977
|
+
assert_eq!(mark_value, &ScalarValue::Boolean(true));
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/*
|
|
1981
|
+
#[test]
|
|
1982
|
+
fn conflicting_unicode_text_with_different_widths() -> Result<(), AutomergeError> {
|
|
1983
|
+
let mut doc1 = AutoCommit::new();
|
|
1984
|
+
let txt = doc1.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
1985
|
+
doc1.splice_text(&txt, 0, 0, "abc")?;
|
|
1986
|
+
|
|
1987
|
+
let mut doc2 = doc1.fork();
|
|
1988
|
+
|
|
1989
|
+
doc1.put(&txt, 1, "B")?;
|
|
1990
|
+
doc2.put(&txt, 1, "🐻")?;
|
|
1991
|
+
|
|
1992
|
+
assert_eq!(doc1.length(&txt), 3);
|
|
1993
|
+
assert_eq!(doc2.length(&txt), 4);
|
|
1994
|
+
|
|
1995
|
+
doc1.merge(&mut doc2)?;
|
|
1996
|
+
doc2.merge(&mut doc1)?;
|
|
1997
|
+
|
|
1998
|
+
let length = doc1.length(&txt);
|
|
1999
|
+
let last_value = doc1.get(&txt, length - 1)?;
|
|
2000
|
+
for n in 0..length {
|
|
2001
|
+
assert_eq!(doc1.get(&txt, n), doc2.get(&txt, n));
|
|
2002
|
+
}
|
|
2003
|
+
assert_eq!(last_value.unwrap().0, Value::from("c"));
|
|
2004
|
+
|
|
2005
|
+
println!("list.len() == {:?}", length);
|
|
2006
|
+
assert_eq!(doc1.length(&txt), doc2.length(&txt));
|
|
2007
|
+
Ok(())
|
|
2008
|
+
}
|
|
2009
|
+
*/
|
|
2010
|
+
|
|
2011
|
+
#[test]
|
|
2012
|
+
fn rollback_with_no_ops() {
|
|
2013
|
+
let mut doc = Automerge::new();
|
|
2014
|
+
|
|
2015
|
+
doc.transact::<_, _, AutomergeError>(|tx| {
|
|
2016
|
+
tx.put(ROOT, "a", 1)?;
|
|
2017
|
+
Ok::<_, AutomergeError>(())
|
|
2018
|
+
})
|
|
2019
|
+
.unwrap();
|
|
2020
|
+
|
|
2021
|
+
let mut doc2 = doc.fork();
|
|
2022
|
+
|
|
2023
|
+
let tx = doc2.transaction();
|
|
2024
|
+
tx.commit();
|
|
2025
|
+
|
|
2026
|
+
let mut doc3 = doc.fork();
|
|
2027
|
+
doc3.transact::<_, _, AutomergeError>(|tx| {
|
|
2028
|
+
tx.put(ROOT, "b", 2)?;
|
|
2029
|
+
Ok::<_, AutomergeError>(())
|
|
2030
|
+
})
|
|
2031
|
+
.unwrap();
|
|
2032
|
+
|
|
2033
|
+
doc2.merge(&mut doc3).unwrap();
|
|
2034
|
+
|
|
2035
|
+
let tx = doc2.transaction();
|
|
2036
|
+
tx.rollback();
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
#[test]
|
|
2040
|
+
fn rollback_with_several_actors() {
|
|
2041
|
+
let mut doc1 = AutoCommit::new().with_actor("aaaaaa".try_into().unwrap());
|
|
2042
|
+
let text = doc1.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
2043
|
+
doc1.splice_text(&text, 0, 0, "the sly fox jumped over the lazy dog")
|
|
2044
|
+
.unwrap();
|
|
2045
|
+
let map_a = doc1.put_object(&ROOT, "map_a", ObjType::Map).unwrap();
|
|
2046
|
+
doc1.put(&map_a, "key1", "value1a").unwrap();
|
|
2047
|
+
doc1.put(&map_a, "key2", "value2a").unwrap();
|
|
2048
|
+
|
|
2049
|
+
let mut doc2 = doc1.fork().with_actor("cccccc".try_into().unwrap());
|
|
2050
|
+
doc2.splice_text(&text, 8, 3, "monkey").unwrap();
|
|
2051
|
+
doc2.splice_text(&text, 36, 3, "pig").unwrap();
|
|
2052
|
+
let map_c = doc2.put_object(&ROOT, "map_c", ObjType::Map).unwrap();
|
|
2053
|
+
doc2.put(&map_a, "key2", "value2c").unwrap();
|
|
2054
|
+
doc2.put(&map_a, "key3", "value3c").unwrap();
|
|
2055
|
+
doc2.put(&map_c, "key1", "value").unwrap();
|
|
2056
|
+
|
|
2057
|
+
let mut doc3 = doc2.fork().with_actor("bbbbbb".try_into().unwrap());
|
|
2058
|
+
doc3.splice_text(&text, 8, 5, "zebra").unwrap();
|
|
2059
|
+
let map_b = doc3.put_object(&ROOT, "map_b", ObjType::Map).unwrap();
|
|
2060
|
+
doc3.put(&map_a, "key1", "value3b").unwrap();
|
|
2061
|
+
doc3.put(&map_a, "key3", "value3b").unwrap();
|
|
2062
|
+
doc3.put(&map_b, "key1", "value").unwrap();
|
|
2063
|
+
doc3.rollback();
|
|
2064
|
+
|
|
2065
|
+
assert_eq!(doc3.save(), doc2.save());
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
#[test]
|
|
2069
|
+
fn save_with_ops_which_reference_actors_only_via_delete() {
|
|
2070
|
+
let mut doc = Automerge::new();
|
|
2071
|
+
|
|
2072
|
+
doc.transact::<_, _, AutomergeError>(|tx| {
|
|
2073
|
+
tx.put(ROOT, "a", 1)?;
|
|
2074
|
+
Ok::<_, AutomergeError>(())
|
|
2075
|
+
})
|
|
2076
|
+
.unwrap();
|
|
2077
|
+
|
|
2078
|
+
let mut forked = doc.fork();
|
|
2079
|
+
forked
|
|
2080
|
+
.transact::<_, _, AutomergeError>(|tx| {
|
|
2081
|
+
tx.delete(ROOT, "a")?;
|
|
2082
|
+
Ok::<_, AutomergeError>(())
|
|
2083
|
+
})
|
|
2084
|
+
.unwrap();
|
|
2085
|
+
|
|
2086
|
+
doc.merge(&mut forked).unwrap();
|
|
2087
|
+
|
|
2088
|
+
// `doc` now contains a delete op which uses the actor of the fork. Delete
|
|
2089
|
+
// ops don't exist explicitly in the document ops, they are referenced in
|
|
2090
|
+
// the "successors" of the encoded ops. This means that when we're encoding
|
|
2091
|
+
// actor IDs into the document we need to check that any actor IDs which
|
|
2092
|
+
// are referenced in the `successors` of an op are encoded as well.
|
|
2093
|
+
|
|
2094
|
+
let saved = doc.save();
|
|
2095
|
+
// This will panic if we failed to encode the referenced actor ID
|
|
2096
|
+
let _ = Automerge::load(&saved).unwrap();
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
#[test]
|
|
2100
|
+
fn save_with_empty_commits() {
|
|
2101
|
+
let mut doc = Automerge::new();
|
|
2102
|
+
|
|
2103
|
+
doc.transact::<_, _, AutomergeError>(|tx| {
|
|
2104
|
+
tx.put(ROOT, "a", 1)?;
|
|
2105
|
+
Ok::<_, AutomergeError>(())
|
|
2106
|
+
})
|
|
2107
|
+
.unwrap();
|
|
2108
|
+
|
|
2109
|
+
let mut forked = doc.fork();
|
|
2110
|
+
forked.empty_commit(CommitOptions::default());
|
|
2111
|
+
|
|
2112
|
+
doc.merge(&mut forked).unwrap();
|
|
2113
|
+
|
|
2114
|
+
let saved = doc.save();
|
|
2115
|
+
// This will panic if we failed to encode the referenced actor ID
|
|
2116
|
+
let _ = Automerge::load(&saved).unwrap();
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
#[test]
|
|
2120
|
+
fn large_patches_in_lists_are_correct() {
|
|
2121
|
+
// Reproduces a bug caused by an incorrect use of ListEncoding in Automerge::live_obj_paths.
|
|
2122
|
+
// This is a function which precalculates the path of every visible object in the document.
|
|
2123
|
+
// The problem was that when calculating the index into a sequence it was using
|
|
2124
|
+
// ListEncoding::List to determine the index, which meant that when a string was inserted into
|
|
2125
|
+
// a list then the index of elements following the list was based on the number of elements in
|
|
2126
|
+
// the string, when it should just increase the index by one for the whole string.
|
|
2127
|
+
//
|
|
2128
|
+
// This bug was a little tricky to track down because it was only triggered by an optimization
|
|
2129
|
+
// which kicks in when there are > 100 patches to render.
|
|
2130
|
+
|
|
2131
|
+
let mut doc = Automerge::new();
|
|
2132
|
+
let heads_before = doc.get_heads();
|
|
2133
|
+
let list = doc
|
|
2134
|
+
.transact::<_, _, AutomergeError>(|tx| {
|
|
2135
|
+
let list = tx.put_object(ROOT, "list", ObjType::List)?;
|
|
2136
|
+
// This should just count as one
|
|
2137
|
+
tx.insert(&list, 0, "123456")?;
|
|
2138
|
+
for i in 1..501 {
|
|
2139
|
+
let inner = tx.insert_object(&list, i, ObjType::Map)?;
|
|
2140
|
+
tx.put(&inner, "a", i as i64)?;
|
|
2141
|
+
}
|
|
2142
|
+
Ok(list)
|
|
2143
|
+
})
|
|
2144
|
+
.unwrap()
|
|
2145
|
+
.result;
|
|
2146
|
+
let heads_after = doc.get_heads();
|
|
2147
|
+
let patches = doc.diff(&heads_before, &heads_after);
|
|
2148
|
+
let final_patch = patches.last().unwrap();
|
|
2149
|
+
assert_eq!(
|
|
2150
|
+
final_patch.path,
|
|
2151
|
+
vec![
|
|
2152
|
+
(ROOT, Prop::Map("list".into())),
|
|
2153
|
+
(list, Prop::Seq(500)) // In the buggy code this was incorrectly coming out as 505 due to
|
|
2154
|
+
// the counting of "123456" as 6 elements rather than 1
|
|
2155
|
+
]
|
|
2156
|
+
);
|
|
2157
|
+
let PatchAction::PutMap { .. } = &final_patch.action else {
|
|
2158
|
+
panic!("Expected PutMap, got {:?}", final_patch.action);
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
#[test]
|
|
2163
|
+
fn diff_should_reverse_deletion_of_object_in_list_correctly() {
|
|
2164
|
+
let mut doc = AutoCommit::new();
|
|
2165
|
+
let list = doc.put_object(ROOT, "list", ObjType::List).unwrap();
|
|
2166
|
+
doc.insert(&list, 0, "a").unwrap();
|
|
2167
|
+
let text = doc
|
|
2168
|
+
.insert_object(&list, 1, automerge::ObjType::Text)
|
|
2169
|
+
.unwrap();
|
|
2170
|
+
doc.splice_text(&text, 0, 0, "b").unwrap();
|
|
2171
|
+
doc.insert(&list, 2, "c").unwrap();
|
|
2172
|
+
|
|
2173
|
+
let heads_before = doc.get_heads();
|
|
2174
|
+
doc.delete(&list, 1).unwrap();
|
|
2175
|
+
let heads_after = doc.get_heads();
|
|
2176
|
+
|
|
2177
|
+
doc.update_diff_cursor();
|
|
2178
|
+
let patches = doc.diff(&heads_after, &heads_before);
|
|
2179
|
+
|
|
2180
|
+
assert_eq!(patches.len(), 2);
|
|
2181
|
+
let patch = patches[0].clone();
|
|
2182
|
+
let PatchAction::Insert { index, values } = &patch.action else {
|
|
2183
|
+
panic!("Expected Insert, got {:?}", patch.action);
|
|
2184
|
+
};
|
|
2185
|
+
assert_eq!(*index, 1);
|
|
2186
|
+
assert_eq!(values.len(), 1);
|
|
2187
|
+
let (value, _, _) = values.into_iter().next().unwrap();
|
|
2188
|
+
assert_eq!(value, &Value::Object(ObjType::Text));
|
|
2189
|
+
|
|
2190
|
+
let patch = patches[1].clone();
|
|
2191
|
+
let PatchAction::SpliceText { index, value, .. } = patch.action else {
|
|
2192
|
+
panic!("Expected SpliceText, got {:?}", patch.action);
|
|
2193
|
+
};
|
|
2194
|
+
assert_eq!(index, 0);
|
|
2195
|
+
assert_eq!(value.make_string(), "b");
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
#[test]
|
|
2199
|
+
fn diff_should_reverse_deletion_of_object_in_map_correctly() {
|
|
2200
|
+
let mut doc = AutoCommit::new();
|
|
2201
|
+
|
|
2202
|
+
let map = doc.put_object(ROOT, "map", ObjType::Map).unwrap();
|
|
2203
|
+
doc.put_object(&map, "text", ObjType::Text).unwrap();
|
|
2204
|
+
|
|
2205
|
+
doc.put(&map, "a", "a").unwrap();
|
|
2206
|
+
let text = doc.put_object(&map, "b", automerge::ObjType::Text).unwrap();
|
|
2207
|
+
doc.splice_text(&text, 0, 0, "b").unwrap();
|
|
2208
|
+
doc.put(&map, "c", "c").unwrap();
|
|
2209
|
+
|
|
2210
|
+
let heads_before = doc.get_heads();
|
|
2211
|
+
doc.delete(&map, "b").unwrap();
|
|
2212
|
+
let heads_after = doc.get_heads();
|
|
2213
|
+
|
|
2214
|
+
doc.update_diff_cursor();
|
|
2215
|
+
let patches = doc.diff(&heads_after, &heads_before);
|
|
2216
|
+
|
|
2217
|
+
assert_eq!(patches.len(), 2);
|
|
2218
|
+
let patch = patches[0].clone();
|
|
2219
|
+
let PatchAction::PutMap { key, value, .. } = &patch.action else {
|
|
2220
|
+
panic!("Expected putmap, got {:?}", patch.action);
|
|
2221
|
+
};
|
|
2222
|
+
assert_eq!(key, "b");
|
|
2223
|
+
assert_eq!(value.0, Value::Object(ObjType::Text));
|
|
2224
|
+
|
|
2225
|
+
let patch = patches[1].clone();
|
|
2226
|
+
let PatchAction::SpliceText { index, value, .. } = patch.action else {
|
|
2227
|
+
panic!("Expected SpliceText, got {:?}", patch.action);
|
|
2228
|
+
};
|
|
2229
|
+
assert_eq!(index, 0);
|
|
2230
|
+
assert_eq!(value.make_string(), "b");
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
#[test]
|
|
2234
|
+
fn diff_should_reverse_deletion_of_block_in_text_correctly() {
|
|
2235
|
+
let mut doc = AutoCommit::new();
|
|
2236
|
+
let text = doc.put_object(ROOT, "text", ObjType::Text).unwrap();
|
|
2237
|
+
doc.splice_text(&text, 0, 0, "a").unwrap();
|
|
2238
|
+
let block = doc.split_block(&text, 1).unwrap();
|
|
2239
|
+
doc.splice_text(&text, 2, 0, "b").unwrap();
|
|
2240
|
+
doc.put(&block, "key", "value").unwrap();
|
|
2241
|
+
|
|
2242
|
+
let heads_before = doc.get_heads();
|
|
2243
|
+
doc.delete(&text, 1).unwrap();
|
|
2244
|
+
let heads_after = doc.get_heads();
|
|
2245
|
+
|
|
2246
|
+
doc.update_diff_cursor();
|
|
2247
|
+
let patches = doc.diff(&heads_after, &heads_before);
|
|
2248
|
+
|
|
2249
|
+
assert_eq!(patches.len(), 2);
|
|
2250
|
+
let patch = patches[0].clone();
|
|
2251
|
+
let PatchAction::Insert { index, values } = &patch.action else {
|
|
2252
|
+
panic!("Expected Insert, got {:?}", patch.action);
|
|
2253
|
+
};
|
|
2254
|
+
assert_eq!(*index, 1);
|
|
2255
|
+
assert_eq!(values.len(), 1);
|
|
2256
|
+
let (value, _, _) = values.into_iter().next().unwrap();
|
|
2257
|
+
assert_eq!(value, &Value::Object(ObjType::Map));
|
|
2258
|
+
|
|
2259
|
+
let patch = patches[1].clone();
|
|
2260
|
+
let PatchAction::PutMap { key, value, .. } = patch.action else {
|
|
2261
|
+
panic!("Expected PutMap, got {:?}", patch.action);
|
|
2262
|
+
};
|
|
2263
|
+
assert_eq!(key, "key");
|
|
2264
|
+
let Value::Scalar(s) = value.0 else {
|
|
2265
|
+
panic!("Expected Scalar, got {:?}", value.0);
|
|
2266
|
+
};
|
|
2267
|
+
assert_eq!(s.as_ref(), &ScalarValue::Str("value".into()));
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
#[test]
|
|
2271
|
+
fn missing_actors_when_docs_are_forked() {
|
|
2272
|
+
// Reproduces https://github.com/automerge/automerge/issues/897
|
|
2273
|
+
//
|
|
2274
|
+
// The problem was a result of these things interacting:
|
|
2275
|
+
//
|
|
2276
|
+
// 1. When we create a transaction we add the actor ID of the document
|
|
2277
|
+
// creating the transaction to the IndexedCache of actor IDs that
|
|
2278
|
+
// document stores
|
|
2279
|
+
// 2. When we fork a document we copy the IndexedCache from the source
|
|
2280
|
+
// document to the forked document
|
|
2281
|
+
// 3. When we save a document we must encode all the actor IDs in the saved
|
|
2282
|
+
// document in lexicographic order. To do this we first enumerate all
|
|
2283
|
+
// the actor IDs in the change graph and then encode this in the
|
|
2284
|
+
// document
|
|
2285
|
+
// 4. We assume that the IndexedCache of actor IDs on the document only
|
|
2286
|
+
// contains actor IDs which are in the change graph
|
|
2287
|
+
//
|
|
2288
|
+
// What can happen is that we create a new actor ID somehow (by forking or
|
|
2289
|
+
// loading). Then we create a transaction with the new actor ID but never
|
|
2290
|
+
// actually make any changes. Then, we create another actor ID in the
|
|
2291
|
+
// same document - by forking it typically. This means that this last
|
|
2292
|
+
// document has an IndexedCache with an actor ID in it which will never
|
|
2293
|
+
// be saved to the document, but which is followed by an actor ID which
|
|
2294
|
+
// will be saved. This in turn means that the indexes we save to the
|
|
2295
|
+
// document are off by one and so we get load errors.
|
|
2296
|
+
//
|
|
2297
|
+
// The solution was to create the lookup table from actor index to actor
|
|
2298
|
+
// ID directly from the actor IDs in the change graph rather than from the
|
|
2299
|
+
// IndexedCache.
|
|
2300
|
+
let actor0 = ActorId::from(&[0]);
|
|
2301
|
+
let actor1 = ActorId::from(&[1]);
|
|
2302
|
+
let actor2 = ActorId::from(&[2]);
|
|
2303
|
+
|
|
2304
|
+
let mut doc0 = AutoCommit::new().with_actor(actor0);
|
|
2305
|
+
doc0.put(ROOT, "a", 1).unwrap();
|
|
2306
|
+
|
|
2307
|
+
// swap these actors and no error occurs
|
|
2308
|
+
let mut doc1 = doc0.fork().with_actor(actor2);
|
|
2309
|
+
let mut doc2 = doc0.fork().with_actor(actor1);
|
|
2310
|
+
|
|
2311
|
+
doc1.put(ROOT, "b", 2).unwrap();
|
|
2312
|
+
doc2.merge(&mut doc1).unwrap();
|
|
2313
|
+
|
|
2314
|
+
let s1 = doc2.save();
|
|
2315
|
+
|
|
2316
|
+
// This call creates a transaction which doesn't do anything (because the
|
|
2317
|
+
// "c" key doesn't exist) and so the actor ID (actor1) gets added to the
|
|
2318
|
+
// IndexedCache of doc2
|
|
2319
|
+
doc2.delete(ROOT, "c").unwrap();
|
|
2320
|
+
|
|
2321
|
+
// error occurs here
|
|
2322
|
+
let s2 = doc2.save_and_verify().unwrap();
|
|
2323
|
+
|
|
2324
|
+
assert_eq!(s1, s2);
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
#[test]
|
|
2328
|
+
fn allows_empty_keys_in_mappings() {
|
|
2329
|
+
let mut doc = AutoCommit::new();
|
|
2330
|
+
doc.put(&automerge::ROOT, "", 1).unwrap();
|
|
2331
|
+
assert_doc!(
|
|
2332
|
+
&doc,
|
|
2333
|
+
map! {
|
|
2334
|
+
"" => { 1 },
|
|
2335
|
+
}
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
#[test]
|
|
2340
|
+
fn has_our_changes() {
|
|
2341
|
+
let mut left = AutoCommit::new();
|
|
2342
|
+
left.put(&automerge::ROOT, "a", 1).unwrap();
|
|
2343
|
+
|
|
2344
|
+
let mut right = AutoCommit::new();
|
|
2345
|
+
right.put(&automerge::ROOT, "b", 2).unwrap();
|
|
2346
|
+
|
|
2347
|
+
let mut left_to_right = automerge::sync::State::new();
|
|
2348
|
+
let mut right_to_left = automerge::sync::State::new();
|
|
2349
|
+
|
|
2350
|
+
assert!(!left.has_our_changes(&left_to_right));
|
|
2351
|
+
assert!(!right.has_our_changes(&right_to_left));
|
|
2352
|
+
|
|
2353
|
+
while !left.has_our_changes(&left_to_right) || !right.has_our_changes(&right_to_left) {
|
|
2354
|
+
let mut quiet = true;
|
|
2355
|
+
if let Some(msg) = left.sync().generate_sync_message(&mut left_to_right) {
|
|
2356
|
+
quiet = false;
|
|
2357
|
+
right
|
|
2358
|
+
.sync()
|
|
2359
|
+
.receive_sync_message(&mut right_to_left, msg)
|
|
2360
|
+
.unwrap();
|
|
2361
|
+
}
|
|
2362
|
+
if let Some(msg) = right.sync().generate_sync_message(&mut right_to_left) {
|
|
2363
|
+
quiet = false;
|
|
2364
|
+
left.sync()
|
|
2365
|
+
.receive_sync_message(&mut left_to_right, msg)
|
|
2366
|
+
.unwrap();
|
|
2367
|
+
}
|
|
2368
|
+
if quiet {
|
|
2369
|
+
panic!("no messages sent but the sync state says we're not in sync");
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
assert!(right.has_our_changes(&right_to_left));
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
#[test]
|
|
2376
|
+
fn stats_smoke_test() {
|
|
2377
|
+
let mut doc = AutoCommit::new();
|
|
2378
|
+
doc.put(&automerge::ROOT, "a", 1).unwrap();
|
|
2379
|
+
doc.commit();
|
|
2380
|
+
doc.put(&automerge::ROOT, "b", 2).unwrap();
|
|
2381
|
+
doc.commit();
|
|
2382
|
+
let stats = doc.stats();
|
|
2383
|
+
assert_eq!(stats.num_changes, 2);
|
|
2384
|
+
assert_eq!(stats.num_ops, 2);
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
#[test]
|
|
2388
|
+
fn invalid_index() {
|
|
2389
|
+
let mut doc = AutoCommit::new();
|
|
2390
|
+
let obj = doc
|
|
2391
|
+
.put_object(&automerge::ROOT, "a", ObjType::List)
|
|
2392
|
+
.unwrap();
|
|
2393
|
+
doc.insert(&obj, 0, 1).unwrap();
|
|
2394
|
+
doc.put(&obj, 0, 2).unwrap();
|
|
2395
|
+
assert_eq!(doc.get(&obj, 0).unwrap().unwrap().0, 2.into());
|
|
2396
|
+
assert_eq!(doc.insert(&obj, 2, 1), Err(AutomergeError::InvalidIndex(2)));
|
|
2397
|
+
assert_eq!(doc.put(&obj, 2, 2), Err(AutomergeError::InvalidIndex(2)));
|
|
2398
|
+
assert_eq!(
|
|
2399
|
+
doc.insert(&obj, 100, 1),
|
|
2400
|
+
Err(AutomergeError::InvalidIndex(100))
|
|
2401
|
+
);
|
|
2402
|
+
assert_eq!(
|
|
2403
|
+
doc.put(&obj, 100, 2),
|
|
2404
|
+
Err(AutomergeError::InvalidIndex(100))
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
#[test]
|
|
2409
|
+
fn zero_length_data() {
|
|
2410
|
+
let mut doc = AutoCommit::new();
|
|
2411
|
+
doc.put(&ROOT, "string", "").unwrap();
|
|
2412
|
+
doc.put(&ROOT, "bytes", vec![]).unwrap();
|
|
2413
|
+
doc.commit();
|
|
2414
|
+
assert_eq!(
|
|
2415
|
+
doc.get(&ROOT, "string").unwrap().unwrap().0,
|
|
2416
|
+
Value::from("")
|
|
2417
|
+
);
|
|
2418
|
+
assert_eq!(
|
|
2419
|
+
doc.get(&ROOT, "bytes").unwrap().unwrap().0,
|
|
2420
|
+
Value::from(vec![])
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
#[test]
|
|
2425
|
+
fn make_sure_load_incremental_doesnt_skip_a_load_with_a_common_head() {
|
|
2426
|
+
let mut doc1 = AutoCommit::new();
|
|
2427
|
+
doc1.put(&ROOT, "string", "hello").unwrap();
|
|
2428
|
+
let mut doc2 = doc1.fork();
|
|
2429
|
+
let mut doc3 = doc1.fork();
|
|
2430
|
+
|
|
2431
|
+
assert!(doc1.get_heads().len() == 1);
|
|
2432
|
+
|
|
2433
|
+
doc1.put(&ROOT, "concurrent1", "123").unwrap();
|
|
2434
|
+
assert!(doc1.get_heads().len() == 1);
|
|
2435
|
+
let hash_b = doc1.get_heads()[0];
|
|
2436
|
+
|
|
2437
|
+
doc3.load_incremental(&doc1.save()).unwrap();
|
|
2438
|
+
assert!(doc3.get_heads().len() == 1);
|
|
2439
|
+
let hash_c = doc3.get_heads()[0];
|
|
2440
|
+
|
|
2441
|
+
assert_eq!(hash_b, hash_c);
|
|
2442
|
+
|
|
2443
|
+
doc2.put(&ROOT, "concurrent2", "abc").unwrap();
|
|
2444
|
+
assert!(doc2.get_heads().len() == 1);
|
|
2445
|
+
let hash_d = doc2.get_heads()[0];
|
|
2446
|
+
|
|
2447
|
+
doc2.merge(&mut doc1).unwrap();
|
|
2448
|
+
let heads = doc2.get_heads();
|
|
2449
|
+
|
|
2450
|
+
assert!(heads.len() == 2);
|
|
2451
|
+
assert!(heads.contains(&hash_d));
|
|
2452
|
+
assert!(heads.contains(&hash_b));
|
|
2453
|
+
|
|
2454
|
+
doc3.load_incremental(&doc2.save()).unwrap();
|
|
2455
|
+
|
|
2456
|
+
assert!(doc3.get_heads() == doc2.get_heads());
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
#[test]
|
|
2460
|
+
fn test_get_last_local_change_generation() {
|
|
2461
|
+
let mut doc = AutoCommit::new();
|
|
2462
|
+
let text = doc.put_object(&ROOT, "text", ObjType::Text).unwrap();
|
|
2463
|
+
doc.splice_text(&text, 0, 0, "hello world").unwrap();
|
|
2464
|
+
confirm_last_change(&mut doc);
|
|
2465
|
+
doc.splice_text(&text, 5, 1, "X").unwrap();
|
|
2466
|
+
confirm_last_change(&mut doc);
|
|
2467
|
+
doc.splice_text(&text, 6, 1, "").unwrap();
|
|
2468
|
+
confirm_last_change(&mut doc);
|
|
2469
|
+
doc.splice_text(&text, 0, 0, "ten thousand and five hundred")
|
|
2470
|
+
.unwrap();
|
|
2471
|
+
confirm_last_change(&mut doc);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
fn confirm_last_change(doc: &mut AutoCommit) {
|
|
2475
|
+
let heads = doc.get_heads();
|
|
2476
|
+
let change = doc.get_last_local_change().unwrap();
|
|
2477
|
+
assert_eq!(vec![change.hash()], heads);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
#[test]
|
|
2481
|
+
fn test_overwriting_a_conflict() {
|
|
2482
|
+
let mut doc1 = AutoCommit::new();
|
|
2483
|
+
let mut doc2 = doc1.fork();
|
|
2484
|
+
|
|
2485
|
+
// put the same values
|
|
2486
|
+
doc1.put(&ROOT, "key", "value").unwrap();
|
|
2487
|
+
doc2.put(&ROOT, "key", "value").unwrap();
|
|
2488
|
+
doc1.merge(&mut doc2).unwrap();
|
|
2489
|
+
doc2.merge(&mut doc1).unwrap();
|
|
2490
|
+
|
|
2491
|
+
assert_eq!(doc1.get_all(&ROOT, "key").unwrap().len(), 2);
|
|
2492
|
+
assert_eq!(doc2.get_all(&ROOT, "key").unwrap().len(), 2);
|
|
2493
|
+
|
|
2494
|
+
doc1.put(&ROOT, "key", "value").unwrap();
|
|
2495
|
+
doc2.put(&ROOT, "key", "value").unwrap();
|
|
2496
|
+
doc1.merge(&mut doc2).unwrap();
|
|
2497
|
+
doc2.merge(&mut doc1).unwrap();
|
|
2498
|
+
|
|
2499
|
+
assert_eq!(doc1.get_all(&ROOT, "key").unwrap().len(), 1);
|
|
2500
|
+
assert_eq!(doc2.get_all(&ROOT, "key").unwrap().len(), 1);
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
#[test]
|
|
2504
|
+
fn get_changes_with_hash_of_empty_change_produces_correct_result() {
|
|
2505
|
+
// This test reproduces an issue where if you create an empty change, then
|
|
2506
|
+
// call Automerge::get_changes(&[hash_of_empty_change]) the result would
|
|
2507
|
+
// include the hash of the change just created but it should be empty. The
|
|
2508
|
+
// reason this happend was that `get_changes` works by walking the change
|
|
2509
|
+
// graph and combining all the (actor, seq) pairs to form a vector clock
|
|
2510
|
+
// which represents the ancestors which should be filtered out. The logic
|
|
2511
|
+
// which performed this walking combined clocks by comparing the `max_op` of
|
|
2512
|
+
// two changes, but in the case of an empty change the `max_op` didn't
|
|
2513
|
+
// change and this meant that the clock was not updated with the sequence
|
|
2514
|
+
// number of the empty change. This meant that the clock did not include the
|
|
2515
|
+
// empty change seq and so the empty change was not filtered out.
|
|
2516
|
+
let mut doc = AutoCommit::new();
|
|
2517
|
+
let head = doc.empty_change(CommitOptions::default());
|
|
2518
|
+
let changes = doc.get_changes(&[head]);
|
|
2519
|
+
assert!(changes.is_empty());
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
#[test]
|
|
2523
|
+
fn reproduce_clock_cache_bug() {
|
|
2524
|
+
// This test exercises an issue with clock caching. The problem manifested
|
|
2525
|
+
// as two documents which have common history returning different results
|
|
2526
|
+
// for `Automerge::get_changes(&common_heads)` where `common_heads` is a
|
|
2527
|
+
// set of change hashes which are in both documents.
|
|
2528
|
+
// `Automerge::get_changes(&heads)` returns everything in a document which
|
|
2529
|
+
// is _not_ an ancestor of the heads specified. In two documents which
|
|
2530
|
+
// contain `heads`, `get_changes(&heads)` should therefore return the same
|
|
2531
|
+
// thing.
|
|
2532
|
+
//
|
|
2533
|
+
// In order to compute the ancestors of the common heads, automerge converts
|
|
2534
|
+
// the heads to a vector clock. Every commit in an Automerge document has an
|
|
2535
|
+
// (actor ID, sequence number) pair (a lamport timestamp) which identifies
|
|
2536
|
+
// the commit. We can walk the commit graph accumulating these pairs to form
|
|
2537
|
+
// a vector clock. This vector clock can then be used to determine if any
|
|
2538
|
+
// given commit is an ancestor of the given heads.
|
|
2539
|
+
//
|
|
2540
|
+
// E.g. imagine this graph where I denote the lamport timestamp as (actor
|
|
2541
|
+
// ID, sequence number):
|
|
2542
|
+
//
|
|
2543
|
+
// * (a, 1)
|
|
2544
|
+
// |
|
|
2545
|
+
// * (a, 2)
|
|
2546
|
+
// / \
|
|
2547
|
+
// / \
|
|
2548
|
+
// * (b, 1) * (c, 1)
|
|
2549
|
+
// \ /
|
|
2550
|
+
// \ /
|
|
2551
|
+
// * (d, 1)
|
|
2552
|
+
//
|
|
2553
|
+
// Then say we have the heads for (b, 1), we can walk from (b,1) to the root
|
|
2554
|
+
// and we will end up with a vector clock of [(a, 2), (b, 1)]. We can then
|
|
2555
|
+
// use this vector clock to determine that (b,1) is an ancestor of (a,1)
|
|
2556
|
+
// because the clock for (b,1) contains (a,2), which is greater than (a,1)
|
|
2557
|
+
//
|
|
2558
|
+
// Creating these clocks for large documents is expensive, so we cache
|
|
2559
|
+
// clocks for every 16 commits in the document. The problem this test
|
|
2560
|
+
// exposes is that the logic for the clock caching was incorrect. When
|
|
2561
|
+
// walking the graph, the cache logic could accidentally omit some
|
|
2562
|
+
// commits from the cached clock.
|
|
2563
|
+
//
|
|
2564
|
+
// The caching logic was expressed by starting with the end node and then
|
|
2565
|
+
// walking backwards in a depth first search until the cache limit is
|
|
2566
|
+
// reached, it looked something like this:
|
|
2567
|
+
//
|
|
2568
|
+
// let limit = CACHE_STEP * 2
|
|
2569
|
+
// let clock = new_clock()
|
|
2570
|
+
// let to_visit = [start node]
|
|
2571
|
+
// let visited = []
|
|
2572
|
+
// while let Some(node) = to_visit.pop() {
|
|
2573
|
+
// // Process node
|
|
2574
|
+
// if let Some(cached) = get_cache(node) {
|
|
2575
|
+
// merge(cached, clock)
|
|
2576
|
+
// } else if visited.len() <= limit {
|
|
2577
|
+
// to_visit.extend(parents_of(node))
|
|
2578
|
+
// } else {
|
|
2579
|
+
// break;
|
|
2580
|
+
// }
|
|
2581
|
+
// }
|
|
2582
|
+
//
|
|
2583
|
+
// The logic would then restart from the remaining nodes in the to_visit queue.
|
|
2584
|
+
//
|
|
2585
|
+
// The bug is that in some scenarios we would not add the parents of the
|
|
2586
|
+
// node which causes us to reach the cache limit to the to_visit queue.
|
|
2587
|
+
//
|
|
2588
|
+
// The easiest way to observe this bug is to create a document where there
|
|
2589
|
+
// is a commit which has a large number of parents (i.e. a merge commit
|
|
2590
|
+
// from many branches). E.g imagine a document like this:
|
|
2591
|
+
//
|
|
2592
|
+
// A
|
|
2593
|
+
// |
|
|
2594
|
+
// .--.--.--.--.--.--.--.--.
|
|
2595
|
+
// B C D E F G H I J
|
|
2596
|
+
// | | | | | | | | |
|
|
2597
|
+
// K L M N O P Q R S
|
|
2598
|
+
// '--'--'--'--'--'--'--'--'
|
|
2599
|
+
// |
|
|
2600
|
+
// T
|
|
2601
|
+
// |
|
|
2602
|
+
// U
|
|
2603
|
+
//
|
|
2604
|
+
// Now, let's say our CACHE_STEP is 3. Then every third change which is
|
|
2605
|
+
// applied is cached. U is the 21st change and so it will be cached. The
|
|
2606
|
+
// cache logic will now step backwards through the graph until 6 (CACHE_STEP
|
|
2607
|
+
// * 2) nodes have been visited, and then it will stop. In this case that
|
|
2608
|
+
// would mean that we add T to the list of nodes to visit, then pop it and
|
|
2609
|
+
// process it, adding all of T's parents to the list of nodes to visit.
|
|
2610
|
+
// Then we continue processing until we reach O so the final order is
|
|
2611
|
+
// T, K, L, M, N, O. Here's where the bug is, the cache logic now returns
|
|
2612
|
+
// and says "now keep going with the rest of the to_visit list", but it
|
|
2613
|
+
// didn't add the parents of O to the list and so F never gets processed.
|
|
2614
|
+
//
|
|
2615
|
+
// This is only a problem if F is a commit with a different actor ID then
|
|
2616
|
+
// O, otherwise the (actor_id, seq) if O covers F.
|
|
2617
|
+
//
|
|
2618
|
+
// So, to reproduce this error, we create a lot of branches, and on each
|
|
2619
|
+
// branch make a lot of commits - each commit with a different actor.
|
|
2620
|
+
// Then we merge the branches together and observe that `get_changes(&heads)`
|
|
2621
|
+
// returns empty. If the caching logic is incorrect the heads will be
|
|
2622
|
+
// converted into a clock which doesn't cover commits like F and then
|
|
2623
|
+
// `get_changes(&heads)` will be non empty.
|
|
2624
|
+
|
|
2625
|
+
let mut base = AutoCommit::new();
|
|
2626
|
+
|
|
2627
|
+
// Add some number of initial commits
|
|
2628
|
+
for i in 0..100 {
|
|
2629
|
+
base.put(ROOT, format!("initial_commit_{}", i), true)
|
|
2630
|
+
.unwrap();
|
|
2631
|
+
base.commit();
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
const NUM_BRANCHES: usize = 20;
|
|
2635
|
+
const COMMITS_PER_BRANCH: usize = 2;
|
|
2636
|
+
|
|
2637
|
+
let mut branches = (0..NUM_BRANCHES - 1)
|
|
2638
|
+
.map(|_| base.fork())
|
|
2639
|
+
.collect::<Vec<_>>();
|
|
2640
|
+
branches.push(base);
|
|
2641
|
+
|
|
2642
|
+
for (branch_no, branch) in branches.iter_mut().enumerate() {
|
|
2643
|
+
for commit_no in 0..COMMITS_PER_BRANCH {
|
|
2644
|
+
branch
|
|
2645
|
+
.put(ROOT, format!("branch_{}-{}", branch_no, commit_no), true)
|
|
2646
|
+
.unwrap();
|
|
2647
|
+
branch.commit();
|
|
2648
|
+
// Make a new actor for the next commit
|
|
2649
|
+
*branch = branch.fork();
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
let mut base = branches.pop().unwrap();
|
|
2654
|
+
|
|
2655
|
+
for branch in &mut branches {
|
|
2656
|
+
base.merge(branch).unwrap();
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// Create a bunch of commits after the merge to ensure a clock is cached
|
|
2660
|
+
// between the document heads and the branches
|
|
2661
|
+
for i in 0..100 {
|
|
2662
|
+
base.put(ROOT, format!("after-merge-{}", i), true).unwrap();
|
|
2663
|
+
base.commit();
|
|
2664
|
+
}
|
|
2665
|
+
let heads = base.get_heads();
|
|
2666
|
+
|
|
2667
|
+
assert!(base.get_changes(&heads).is_empty());
|
|
2668
|
+
}
|