oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1use std::collections::HashMap;
2use std::convert::TryInto;
3use std::fs::File;
4use std::io::BufReader;
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::Arc;
7
8use crate::block::{ClientID, Item, ItemContent};
9use crate::branch::Branch;
10use crate::encoding::read::Read;
11use crate::id_set::{DeleteSet, IdSet};
12use crate::store::Store;
13use crate::types::xml::XmlFragment;
14use crate::types::{ToJson, TypePtr, TypeRef};
15use crate::update::{BlockCarrier, Update};
16use crate::updates::decoder::{Decode, Decoder, DecoderV1};
17use crate::updates::encoder::Encode;
18use crate::{
19 Any, ArrayPrelim, Doc, GetString, Map, MapPrelim, MapRef, ReadTxn, StateVector, Transact, Xml,
20 XmlElementRef, XmlTextRef, ID,
21};
22
23#[test]
24fn text_insert_delete() {
25 /* Generated via:
26 ```js
27 const doc = new Y.Doc()
28 const ytext = doc.getText('type')
29 doc..transact_mut()(function () {
30 ytext.insert(0, 'def')
31 ytext.insert(0, 'abc')
32 ytext.insert(6, 'ghi')
33 ytext.delete(2, 5)
34 })
35 const update = Y.encodeStateAsUpdate(doc)
36 ytext.toString() // => 'abhi'
37 ```
38 This way we confirm that we can decode and apply:
39 1. blocks without left/right origin consisting of multiple characters
40 2. blocks with left/right origin consisting of multiple characters
41 3. delete sets
42 */
43 let update = &[
44 1, 5, 152, 234, 173, 126, 0, 1, 1, 4, 116, 121, 112, 101, 3, 68, 152, 234, 173, 126, 0, 2,
45 97, 98, 193, 152, 234, 173, 126, 4, 152, 234, 173, 126, 0, 1, 129, 152, 234, 173, 126, 2,
46 1, 132, 152, 234, 173, 126, 6, 2, 104, 105, 1, 152, 234, 173, 126, 2, 0, 3, 5, 2,
47 ];
48 const CLIENT_ID: ClientID = 264992024;
49 let expected_blocks = vec![
50 Item::new(
51 ID::new(CLIENT_ID, 0),
52 None,
53 None,
54 None,
55 None,
56 TypePtr::Named("type".into()),
57 None,
58 ItemContent::Deleted(3),
59 )
60 .unwrap(),
61 Item::new(
62 ID::new(CLIENT_ID, 3),
63 None,
64 None,
65 None,
66 Some(ID::new(CLIENT_ID, 0)),
67 TypePtr::Unknown,
68 None,
69 ItemContent::String("ab".into()),
70 )
71 .unwrap(),
72 Item::new(
73 ID::new(CLIENT_ID, 5),
74 None,
75 Some(ID::new(CLIENT_ID, 4)),
76 None,
77 Some(ID::new(CLIENT_ID, 0)),
78 TypePtr::Unknown,
79 None,
80 ItemContent::Deleted(1),
81 )
82 .unwrap(),
83 Item::new(
84 ID::new(CLIENT_ID, 6),
85 None,
86 Some(ID::new(CLIENT_ID, 2)),
87 None,
88 None,
89 TypePtr::Unknown,
90 None,
91 ItemContent::Deleted(1),
92 )
93 .unwrap(),
94 Item::new(
95 ID::new(CLIENT_ID, 7),
96 None,
97 Some(ID::new(CLIENT_ID, 6)),
98 None,
99 None,
100 TypePtr::Unknown,
101 None,
102 ItemContent::String("hi".into()),
103 )
104 .unwrap(),
105 ];
106 let expected_ds = {
107 let mut ds = IdSet::new();
108 ds.insert(ID::new(CLIENT_ID, 0), 3);
109 ds.insert(ID::new(CLIENT_ID, 5), 2);
110 DeleteSet::from(ds)
111 };
112 let visited = Arc::new(AtomicBool::new(false));
113 let setter = visited.clone();
114
115 let doc = Doc::new();
116 let txt = doc.get_or_insert_text("type");
117 let _sub = doc.observe_update_v1(move |_, e| {
118 let u = Update::decode_v1(&e.update).unwrap();
119 for (actual, expected) in u.blocks.blocks().zip(expected_blocks.as_slice()) {
120 if let BlockCarrier::Item(block) = actual {
121 assert_eq!(block, expected);
122 }
123 }
124 assert_eq!(u.delete_set, expected_ds);
125 setter.store(true, Ordering::Relaxed);
126 });
127 {
128 let mut txn = doc.transact_mut();
129 let u = Update::decode_v1(update).unwrap();
130 txn.apply_update(u).unwrap();
131 }
132 assert_eq!(txt.get_string(&doc.transact()), "abhi".to_string());
133 assert!(visited.load(Ordering::Relaxed));
134}
135
136#[test]
137fn map_set() {
138 /* Generated via:
139 ```js
140 const doc = new Y.Doc()
141 const x = doc.getMap('test')
142 x.set('k1', 'v1')
143 x.set('k2', 'v2')
144 const payload_v1 = Y.encodeStateAsUpdate(doc)
145 console.log(payload_v1);
146 const payload_v2 = Y.encodeStateAsUpdateV2(doc)
147 console.log(payload_v2);
148 ```
149 */
150 const CLIENT_ID: ClientID = 440166001;
151 let expected = vec![
152 Item::new(
153 ID::new(CLIENT_ID, 0),
154 None,
155 None,
156 None,
157 None,
158 TypePtr::Named("test".into()),
159 Some("k1".into()),
160 ItemContent::Any(vec![Any::from("v1")]),
161 )
162 .unwrap()
163 .into(),
164 Item::new(
165 ID::new(CLIENT_ID, 1),
166 None,
167 None,
168 None,
169 None,
170 TypePtr::Named("test".into()),
171 Some("k2".into()),
172 ItemContent::Any(vec![Any::from("v2")]),
173 )
174 .unwrap()
175 .into(),
176 ];
177
178 let payload = &[
179 1, 2, 241, 204, 241, 209, 1, 0, 40, 1, 4, 116, 101, 115, 116, 2, 107, 49, 1, 119, 2, 118,
180 49, 40, 1, 4, 116, 101, 115, 116, 2, 107, 50, 1, 119, 2, 118, 50, 0,
181 ];
182 roundtrip_v1(payload, &expected);
183
184 let payload = &[
185 0, 0, 5, 177, 153, 227, 163, 3, 0, 0, 1, 40, 17, 12, 116, 101, 115, 116, 107, 49, 116, 101,
186 115, 116, 107, 50, 4, 2, 4, 2, 1, 1, 0, 2, 65, 0, 1, 2, 0, 119, 2, 118, 49, 119, 2, 118,
187 50, 0,
188 ];
189 roundtrip_v2(payload, &expected);
190}
191
192#[test]
193fn array_insert() {
194 /* Generated via:
195 ```js
196 const doc = new Y.Doc()
197 const x = doc.getArray('test')
198 x.push(['a']);
199 x.push(['b']);
200 const payload_v1 = Y.encodeStateAsUpdate(doc)
201 console.log(payload_v1);
202 const payload_v2 = Y.encodeStateAsUpdateV2(doc)
203 console.log(payload_v2);
204 ```
205 */
206 const CLIENT_ID: ClientID = 2525665872;
207 let expected = vec![Item::new(
208 ID::new(CLIENT_ID, 0),
209 None,
210 None,
211 None,
212 None,
213 TypePtr::Named("test".into()),
214 None,
215 ItemContent::Any(vec![Any::String("a".into()), Any::String("b".into())]),
216 )
217 .unwrap()
218 .into()];
219
220 let payload = &[
221 1, 1, 208, 180, 170, 180, 9, 0, 8, 1, 4, 116, 101, 115, 116, 2, 119, 1, 97, 119, 1, 98, 0,
222 ];
223 roundtrip_v1(payload, &expected);
224
225 let payload = &[
226 0, 0, 5, 144, 233, 212, 232, 18, 0, 0, 1, 8, 6, 4, 116, 101, 115, 116, 4, 1, 1, 0, 1, 2, 1,
227 1, 0, 119, 1, 97, 119, 1, 98, 0,
228 ];
229 roundtrip_v2(payload, &expected);
230}
231
232#[test]
233fn xml_fragment_insert() {
234 /* Generated via:
235 ```js
236 const ydoc = new Y.Doc()
237 const yxmlFragment = ydoc.getXmlFragment('fragment-name')
238 const yxmlNested = new Y.XmlFragment('fragment-name')
239 const yxmlText = new Y.XmlText()
240 yxmlFragment.insert(0, [yxmlText])
241 yxmlFragment.firstChild === yxmlText
242 yxmlFragment.insertAfter(yxmlText, [new Y.XmlElement('node-name')])
243 const payload_v1 = Y.encodeStateAsUpdate(ydoc)
244 console.log(payload_v1);
245 const payload_v2 = Y.encodeStateAsUpdateV2(ydoc)
246 console.log(payload_v2);
247 ```
248 */
249 const CLIENT_ID: ClientID = 2459881872;
250 let expected = vec![
251 Item::new(
252 ID::new(CLIENT_ID, 0),
253 None,
254 None,
255 None,
256 None,
257 TypePtr::Named("fragment-name".into()),
258 None,
259 ItemContent::Type(Branch::new(TypeRef::XmlText)),
260 )
261 .unwrap()
262 .into(),
263 Item::new(
264 ID::new(CLIENT_ID, 1),
265 None,
266 Some(ID::new(CLIENT_ID, 0)),
267 None,
268 None,
269 TypePtr::Unknown,
270 None,
271 ItemContent::Type(Branch::new(TypeRef::XmlElement("node-name".into()))),
272 )
273 .unwrap()
274 .into(),
275 ];
276
277 let payload = &[
278 1, 2, 144, 163, 251, 148, 9, 0, 7, 1, 13, 102, 114, 97, 103, 109, 101, 110, 116, 45, 110,
279 97, 109, 101, 6, 135, 144, 163, 251, 148, 9, 0, 3, 9, 110, 111, 100, 101, 45, 110, 97, 109,
280 101, 0,
281 ];
282 roundtrip_v1(payload, &expected);
283
284 let payload = &[
285 0, 1, 0, 6, 208, 198, 246, 169, 18, 0, 1, 0, 0, 3, 7, 0, 135, 25, 22, 102, 114, 97, 103,
286 109, 101, 110, 116, 45, 110, 97, 109, 101, 110, 111, 100, 101, 45, 110, 97, 109, 101, 13,
287 9, 1, 1, 2, 6, 3, 0, 1, 2, 0, 0,
288 ];
289 roundtrip_v2(payload, &expected);
290}
291
292#[test]
293fn state_vector() {
294 /* Generated via:
295 ```js
296 const a = new Y.Doc()
297 const ta = a.getText('test')
298 ta.insert(0, 'abc')
299
300 const b = new Y.Doc()
301 const tb = b.getText('test')
302 tb.insert(0, 'de')
303
304 Y.applyUpdate(a, Y.encodeStateAsUpdate(b))
305 console.log(Y.encodeStateVector(a))
306 ```
307 */
308 let payload = &[2, 178, 219, 218, 44, 3, 190, 212, 225, 6, 2];
309 let mut expected = StateVector::default();
310 expected.inc_by(14182974, 2);
311 expected.inc_by(93760946, 3);
312
313 let sv = StateVector::decode_v1(payload).unwrap();
314 assert_eq!(sv, expected);
315
316 let serialized = sv.encode_v1();
317 assert_eq!(serialized.as_slice(), payload);
318}
319
320#[test]
321fn utf32_lib0_v2_decoding() {
322 let data = &[
323 0, 1, 0, 11, 144, 161, 211, 222, 18, 226, 133, 156, 142, 8, 25, 23, 1, 0, 4, 6, 0, 14, 0,
324 16, 14, 1, 2, 14, 4, 2, 4, 2, 20, 4, 10, 8, 10, 8, 10, 1, 56, 55, 40, 4, 39, 0, 4, 0, 161,
325 0, 0, 0, 167, 0, 4, 0, 167, 0, 4, 0, 167, 0, 4, 0, 7, 0, 1, 0, 0, 0, 40, 3, 71, 0, 1, 0,
326 132, 0, 129, 0, 132, 0, 129, 0, 132, 0, 129, 0, 132, 0, 129, 0, 132, 0, 129, 0, 132, 237,
327 1, 208, 1, 110, 111, 116, 101, 46, 103, 117, 105, 100, 110, 111, 116, 101, 71, 117, 105,
328 100, 110, 111, 116, 101, 46, 111, 119, 110, 101, 114, 111, 119, 110, 101, 114, 110, 111,
329 116, 101, 46, 116, 121, 112, 101, 110, 111, 116, 101, 84, 121, 112, 101, 110, 111, 116,
330 101, 46, 112, 114, 105, 118, 97, 116, 101, 105, 115, 80, 114, 105, 118, 97, 116, 101, 110,
331 111, 116, 101, 46, 99, 114, 101, 97, 116, 101, 84, 105, 109, 101, 99, 114, 101, 97, 116,
332 101, 84, 105, 109, 101, 110, 111, 116, 101, 46, 116, 105, 116, 108, 101, 116, 105, 116,
333 108, 101, 102, 102, 195, 188, 108, 108, 101, 110, 102, 195, 188, 108, 104, 108, 101, 110,
334 102, 195, 188, 104, 108, 101, 110, 112, 114, 111, 115, 101, 109, 105, 114, 114, 111, 114,
335 112, 105, 110, 100, 101, 110, 116, 116, 97, 103, 78, 97, 109, 101, 108, 105, 110, 101, 72,
336 101, 105, 103, 104, 116, 98, 95, 105, 100, 229, 156, 168, 227, 129, 174, 233, 159, 169,
337 229, 155, 189, 240, 159, 135, 176, 240, 159, 135, 183, 240, 159, 135, 168, 240, 159, 135,
338 179, 240, 159, 135, 175, 240, 159, 135, 181, 9, 8, 10, 5, 9, 8, 12, 9, 15, 74, 0, 5, 1, 6,
339 7, 6, 11, 1, 6, 7, 10, 4, 65, 0, 2, 68, 1, 7, 1, 5, 0, 3, 1, 0, 0, 4, 66, 2, 3, 6, 10, 65,
340 4, 2, 65, 4, 66, 0, 10, 69, 1, 2, 5, 0, 119, 22, 66, 71, 108, 122, 109, 85, 106, 50, 84,
341 82, 45, 108, 100, 106, 102, 113, 49, 90, 112, 82, 49, 81, 125, 34, 125, 0, 121, 119, 13,
342 49, 54, 53, 50, 57, 51, 51, 50, 50, 50, 56, 56, 50, 30, 0, 125, 0, 119, 3, 100, 105, 118,
343 119, 0, 119, 11, 74, 88, 98, 65, 83, 97, 45, 97, 57, 50, 106, 1, 226, 130, 142, 135, 4, 8,
344 0, 19, 8, 1, 5, 1, 1, 1, 1, 9, 2, 4, 4, 4, 4, 4,
345 ];
346 let doc = Doc::new();
347 let xml = doc.get_or_insert_xml_fragment("prosemirror");
348 let mut txn = doc.transact_mut();
349 let update = Update::decode_v2(data).unwrap();
350 txn.apply_update(update).unwrap();
351 let actual: XmlElementRef = xml.get(&txn, 0).unwrap().try_into().unwrap();
352
353 let expected_attrs = HashMap::from([
354 ("b_id", "JXbASa-a92j".to_string()),
355 ("indent", "0".to_string()),
356 ("tagName", "div".to_string()),
357 ("lineHeight", "".to_string()),
358 ]);
359 let actual_attrs: HashMap<&str, String> = actual.attributes(&txn).collect();
360 assert_eq!(actual_attrs, expected_attrs);
361
362 let txt: XmlTextRef = actual.get(&txn, 0).unwrap().try_into().unwrap();
363
364 assert_eq!(txt.get_string(&txn), "在の韩国🇰🇷🇨🇳🇯🇵");
365}
366
367/// Verify if given `payload` can be deserialized into series
368/// of `expected` blocks, then serialize them back and check
369/// if produced binary is equivalent to `payload`.
370fn roundtrip_v1(payload: &[u8], expected: &Vec<BlockCarrier>) {
371 let u = Update::decode_v1(payload).unwrap();
372 let expected: Vec<&BlockCarrier> = expected.iter().collect();
373 let blocks: Vec<&BlockCarrier> = u.blocks.blocks().collect();
374 assert_eq!(blocks, expected, "failed to decode V1");
375
376 let store: Store = u.into();
377 let serialized = store.encode_v1();
378 assert_eq!(serialized, payload, "failed to encode V1");
379}
380
381/// Same as [roundtrip_v2] but using lib0 v2 encoding.
382fn roundtrip_v2(payload: &[u8], expected: &Vec<BlockCarrier>) {
383 let u = Update::decode_v2(payload).unwrap();
384 let expected: Vec<&BlockCarrier> = expected.iter().collect();
385 let blocks: Vec<&BlockCarrier> = u.blocks.blocks().collect();
386 assert_eq!(blocks, expected, "failed to decode V2");
387
388 let store: Store = u.into();
389 let serialized = store.encode_v2();
390 assert_eq!(serialized, payload, "failed to encode V2");
391}
392
393#[test]
394fn negative_zero_decoding_v2() {
395 let doc = Doc::new();
396 let root = doc.get_or_insert_map("root");
397 let mut txn = doc.transact_mut();
398
399 root.insert(&mut txn, "sequence", MapPrelim::default()); //NOTE: This is how I put nested map.
400 let sequence = root
401 .get(&txn, "sequence")
402 .unwrap()
403 .cast::<MapRef>()
404 .unwrap();
405 sequence.insert(&mut txn, "id", "V9Uk9pxUKZIrW6cOkC0Rg".to_string());
406 sequence.insert(&mut txn, "cuts", ArrayPrelim::default());
407 sequence.insert(&mut txn, "name", "new sequence".to_string());
408
409 root.insert(&mut txn, "__version__", 1);
410 root.insert(&mut txn, "face_expressions", ArrayPrelim::default());
411 root.insert(&mut txn, "characters", ArrayPrelim::default());
412 let expected = root.to_json(&txn);
413
414 let buffer = txn.encode_state_as_update_v2(&StateVector::default());
415
416 let u = Update::decode_v2(&buffer).unwrap();
417
418 let doc2 = Doc::new();
419 let root = doc2.get_or_insert_map("root");
420 let mut txn = doc2.transact_mut();
421 txn.apply_update(u).unwrap();
422 let actual = root.to_json(&txn);
423
424 assert_eq!(actual, expected);
425}
426
427#[test]
428fn test_small_data_set() {
429 test_data_set("../assets/bench-input/small-test-dataset.bin")
430}
431
432#[test]
433fn test_medium_data_set() {
434 test_data_set("../assets/bench-input/medium-test-dataset.bin")
435}
436
437fn test_data_set<P: AsRef<std::path::Path>>(path: P) {
438 let data = {
439 let mut reader = BufReader::new(File::open(path).unwrap());
440 let mut buf = Vec::new();
441 std::io::Read::read_to_end(&mut reader, &mut buf).unwrap();
442 buf
443 };
444 let mut decoder = DecoderV1::from(data.as_slice());
445 let test_count: u32 = decoder.read_var().unwrap();
446 for test_num in 0..test_count {
447 let updates_len: u32 = decoder.read_var().unwrap();
448 let doc = Doc::new();
449 let txt = doc.get_or_insert_text("text");
450 let map = doc.get_or_insert_map("map");
451 let arr = doc.get_or_insert_array("array");
452 for _ in 0..updates_len {
453 let update = Update::decode_v1(decoder.read_buf().unwrap()).unwrap();
454 doc.transact_mut().apply_update(update).unwrap();
455 }
456 let expected = decoder.read_string().unwrap();
457 assert_eq!(
458 txt.get_string(&doc.transact()),
459 expected,
460 "failed at {} run",
461 test_num
462 );
463
464 let expected = decoder.read_any().unwrap();
465 let actual = map.to_json(&doc.transact());
466 assert_eq!(actual, expected, "failed at {} run", test_num);
467
468 let expected = decoder.read_any().unwrap();
469 assert_eq!(
470 arr.to_json(&doc.transact()),
471 expected,
472 "failed at {} run",
473 test_num
474 );
475 }
476}
477