use std::time::Duration; use jmbl::{OpOutput, Value, JMBL}; use litl::Val; #[test] fn root_can_be_list_and_insert_works() { let mut jmbl = JMBL::new_from_root( |view| view.create_list(["hello", "world"]), OpOutput::new_dummy(), ); std::thread::sleep(Duration::from_millis(10)); let jmbl1 = jmbl.readable.clone(); assert_eq!( jmbl1.get_root().if_list().unwrap().at(0), Value::str("hello") ); assert_eq!( jmbl1.get_root().if_list().unwrap().at(1), Value::str("world") ); assert_eq!( jmbl1.get_root().if_list().unwrap().at(2), Value::plain(Val::null()) ); assert_eq!( jmbl1.get_root().if_list().unwrap().val(), vec![Value::str("hello"), Value::str("world")] ); assert_eq!( jmbl1.get_root().if_list().unwrap().val_to_litl(), Val::array([Val::str("hello"), Val::str("world")]) ); jmbl.change(|root, _| root.if_list_mut().unwrap().insert_at(1, "cruel")); let jmbl2 = jmbl.readable.clone(); assert_eq!( jmbl2.get_root().if_list().unwrap().val(), vec![ Value::str("hello"), Value::str("cruel"), Value::str("world") ] ); } #[test] fn delete_works() { let mut jmbl = JMBL::new_from_root( |view| view.create_list(["hello", "cruel", "world"]), OpOutput::new_dummy(), ); jmbl.change(|root, _| root.if_list_mut().unwrap().delete_at(1)); let jmbl2 = jmbl.readable.clone(); assert_eq!( jmbl2.get_root().if_list().unwrap().val(), vec![Value::str("hello"), Value::str("world")] ); } #[test] fn push_works() { let mut jmbl = JMBL::new_from_root( |view| view.create_list(["hello", "world"]), OpOutput::new_dummy(), ); jmbl.change(|root, _| root.if_list_mut().unwrap().push("beings")); let jmbl2 = jmbl.readable.clone(); assert_eq!( jmbl2.get_root().if_list().unwrap().val(), vec![ Value::str("hello"), Value::str("world"), Value::str("beings") ] ); } #[test] fn pop_works() { let mut jmbl = JMBL::new_from_root( |view| view.create_list(["hello", "cruel", "world"]), OpOutput::new_dummy(), ); jmbl.change(|root, _| assert_eq!(root.if_list_mut().unwrap().pop(), Some(Value::str("world")))); let jmbl2 = jmbl.readable.clone(); assert_eq!( jmbl2.get_root().if_list().unwrap().val(), vec![Value::str("hello"), Value::str("cruel")] ); } #[test] fn concurrent_inserts_are_ordered_correctly() { let mut jmbl = JMBL::new_from_root( |view| view.create_list(["hello", "world"]), OpOutput::new_dummy(), ); let jmbl1 = jmbl.readable.clone(); let mut jmbl2a = jmbl.switch_to_new_op_output(OpOutput::new_dummy()); let mut jmbl2b = jmbl.switch_to_new_op_output(OpOutput::new_dummy()); let mut jmbl2c = jmbl.switch_to_new_op_output(OpOutput::new_dummy()); jmbl2a.change(|root, _| root.if_list_mut().unwrap().insert_at(1, "bright")); std::thread::sleep(Duration::from_millis(10)); jmbl2b.change(|root, _| root.if_list_mut().unwrap().insert_at(1, "dark")); std::thread::sleep(Duration::from_millis(10)); jmbl2c.change(|root, _| root.if_list_mut().unwrap().insert_at(1, "cheesy")); let ops_2b_since_1 = jmbl2b.ops_since(&jmbl1).collect::>(); assert_eq!(ops_2b_since_1, jmbl2b.test_get_past_sent_ops().unwrap()); let ops_2c_since_1 = jmbl2c.ops_since(&jmbl1).collect::>(); assert_eq!(ops_2c_since_1, jmbl2c.test_get_past_sent_ops().unwrap()); let jmbl3 = jmbl2a .with_ops_applied(&ops_2b_since_1) .with_ops_applied(&ops_2c_since_1); assert_eq!( jmbl3.get_root().if_list().unwrap().val_to_litl(), Val::array([ Val::str("hello"), Val::str("cheesy"), Val::str("dark"), Val::str("bright"), Val::str("world") ]) ); }