fix: it's possible to enter the no sibling state

This commit is contained in:
Zixuan Chen 2022-11-15 15:53:03 +08:00
parent 114e12944d
commit 284f1e0862
3 changed files with 293 additions and 81 deletions

View file

@ -1,3 +1,5 @@
use std::{fmt::Debug, time::Instant};
use enum_as_inner::EnumAsInner;
use tabled::{TableIteratorExt, Tabled};
pub mod recursive;
@ -263,7 +265,119 @@ pub fn test_single_client(mut actions: Vec<Action>) {
}
}
pub fn test_multi_sites(site_num: u8, mut actions: Vec<Action>) {
pub fn minify_error<T, F, N>(site_num: u8, actions: Vec<T>, f: F, normalize: N)
where
F: Fn(u8, &mut [T]),
N: Fn(u8, &mut [T]) -> Vec<T>,
T: Clone + Debug,
{
std::panic::set_hook(Box::new(|_info| {
// ignore panic output
}));
let f_ref: *const _ = &f;
let f_ref: usize = f_ref as usize;
let actions_clone = actions.clone();
let action_ref: usize = (&actions_clone) as *const _ as usize;
if std::panic::catch_unwind(|| {
// SAFETY: test
let f = unsafe { &*(f_ref as *const F) };
// SAFETY: test
let actions_ref = unsafe { &mut *(action_ref as *mut Vec<T>) };
f(site_num, actions_ref);
})
.is_ok()
{
println!("No Error Found");
return;
}
let mut minified = actions.clone();
let mut candidates = Vec::new();
for i in 0..actions.len() {
let mut new = actions.clone();
new.remove(i);
candidates.push(new);
}
println!("Minifying...");
let start = Instant::now();
while let Some(candidate) = candidates.pop() {
let f_ref: *const _ = &f;
let f_ref: usize = f_ref as usize;
let actions_clone = candidate.clone();
let action_ref: usize = (&actions_clone) as *const _ as usize;
if std::panic::catch_unwind(|| {
// SAFETY: test
let f = unsafe { &*(f_ref as *const F) };
// SAFETY: test
let actions_ref = unsafe { &mut *(action_ref as *mut Vec<T>) };
f(site_num, actions_ref);
})
.is_err()
{
for i in 0..candidate.len() {
let mut new = candidate.clone();
new.remove(i);
candidates.push(new);
}
if candidate.len() < minified.len() {
minified = candidate;
println!("New min len={}", minified.len());
}
if candidates.len() > 40 {
candidates.drain(0..30);
}
}
if start.elapsed().as_secs() > 10 {
if minified.len() <= 4 {
break;
}
}
if start.elapsed().as_secs() > 60 {
break;
}
}
let minified = normalize(site_num, &mut minified);
println!(
"Old Length {}, New Length {}",
actions.len(),
minified.len()
);
dbg!(&minified);
if actions.len() > minified.len() {
minify_error(site_num, minified, f, normalize);
}
}
pub fn normalize(site_num: u8, actions: &mut [Action]) -> Vec<Action> {
let mut sites = Vec::new();
for i in 0..site_num {
sites.push(LoroCore::new(Default::default(), Some(i as ClientID)));
}
let mut applied = Vec::new();
for action in actions.iter_mut() {
sites.preprocess(action);
applied.push(action.clone());
let sites_ptr: *mut Vec<_> = &mut sites as *mut _;
if std::panic::catch_unwind(|| {
// SAFETY: Test
let sites = unsafe { &mut *sites_ptr };
sites.apply_action(&action.clone());
})
.is_err()
{
break;
}
}
println!("{}", applied.clone().table());
applied
}
pub fn test_multi_sites(site_num: u8, actions: &mut [Action]) {
let mut sites = Vec::new();
for i in 0..site_num {
sites.push(LoroCore::new(Default::default(), Some(i as ClientID)));
@ -290,31 +404,184 @@ mod test {
fn case1() {
test_multi_sites(
8,
vec![
&mut vec![
Ins {
content: 0,
pos: 260589272104960,
content: 35108,
pos: 0,
site: 2,
},
Ins {
content: 18218,
pos: 0,
site: 7,
},
Ins {
content: 35624,
pos: 0,
site: 0,
},
Ins {
content: 43540,
pos: 45579077389650432,
content: 38400,
pos: 0,
site: 6,
},
Ins {
content: 65280,
pos: 2,
site: 7,
},
Ins {
content: 4626,
pos: 5,
site: 0,
},
Ins {
content: 60672,
pos: 0,
site: 1,
},
Ins {
content: 426,
pos: 131156641054720,
content: 35072,
pos: 1,
site: 2,
},
Ins {
content: 15035,
pos: 3,
site: 0,
},
Ins {
content: 18708,
pos: 8667458957855066541,
site: 120,
content: 65280,
pos: 0,
site: 7,
},
Ins {
content: 4626,
pos: 0,
site: 0,
},
Ins {
content: 201,
pos: 2,
site: 2,
},
Ins {
content: 65377,
pos: 3,
site: 1,
},
Ins {
content: 9988,
pos: 0,
site: 0,
},
Ins {
content: 4626,
pos: 14,
site: 0,
},
Ins {
content: 4626,
pos: 11,
site: 7,
},
Ins {
content: 1070,
pos: 0,
site: 5,
},
Ins {
content: 27421,
pos: 7,
site: 1,
},
Ins {
content: 65121,
pos: 22,
site: 0,
},
Ins {
content: 65462,
pos: 1,
site: 0,
},
Ins {
content: 4626,
pos: 0,
site: 4,
},
Ins {
content: 4626,
pos: 16,
site: 0,
},
Ins {
content: 65462,
pos: 11,
site: 2,
},
Ins {
content: 48009,
pos: 10,
site: 0,
},
Ins {
content: 23277,
pos: 7,
site: 0,
},
Ins {
content: 60672,
pos: 13,
site: 1,
},
Ins {
content: 4626,
pos: 2,
site: 7,
},
Ins {
content: 4626,
pos: 2,
site: 0,
},
Ins {
content: 2606,
pos: 0,
site: 3,
},
Ins {
content: 65270,
pos: 10,
site: 0,
},
SyncAll,
Ins {
content: 65462,
pos: 107,
site: 4,
},
SyncAll,
Ins {
content: 4626,
pos: 98,
site: 0,
},
SyncAll,
Ins {
content: 0,
pos: 0,
site: 0,
},
Del {
pos: 8680820740569200760,
len: 8680820740569200760,
site: 120,
pos: 0,
len: 147,
site: 0,
},
Ins {
content: 0,
pos: 146,
site: 4,
},
],
)
@ -324,7 +591,7 @@ mod test {
fn case0() {
test_multi_sites(
4,
vec![
&mut vec![
Ins {
content: 31800,
pos: 723390690148040714,
@ -344,4 +611,9 @@ mod test {
],
)
}
#[test]
fn mini() {
minify_error(8, vec![], test_multi_sites, normalize)
}
}

View file

@ -1,4 +1,4 @@
use std::{collections::HashSet, time::Instant};
use std::{collections::HashSet, fmt::Debug, panic::UnwindSafe, time::Instant};
use arbitrary::Arbitrary;
use enum_as_inner::EnumAsInner;
@ -505,70 +505,12 @@ pub fn test_multi_sites(site_num: u8, actions: &mut [Action]) {
check_synced(&mut sites);
}
pub fn minify_error(site_num: u8, actions: Vec<Action>) {
std::panic::set_hook(Box::new(|_info| {
// ignore panic output
}));
if std::panic::catch_unwind(|| test_multi_sites(site_num, &mut actions.clone())).is_ok() {
println!("No Error Found");
return;
}
let mut minified = actions.clone();
let mut candidates = Vec::new();
for i in 0..actions.len() {
let mut new = actions.clone();
new.remove(i);
candidates.push(new);
}
println!("Minifying...");
let start = Instant::now();
while let Some(candidate) = candidates.pop() {
if std::panic::catch_unwind(|| test_multi_sites(site_num, &mut candidate.clone())).is_err()
{
for i in 0..candidate.len() {
let mut new = candidate.clone();
new.remove(i);
candidates.push(new);
}
if candidate.len() < minified.len() {
minified = candidate;
println!("New min len={}", minified.len());
}
if candidates.len() > 40 {
candidates.drain(0..30);
}
}
if start.elapsed().as_secs() > 10 {
if minified.len() <= 4 {
break;
}
}
if start.elapsed().as_secs() > 60 {
break;
}
}
let minified = normalize(site_num, &mut minified);
println!(
"Old Length {}, New Length {}",
actions.len(),
minified.len()
);
dbg!(&minified);
if actions.len() > minified.len() {
minify_error(site_num, minified);
}
}
#[cfg(test)]
mod failed_tests {
use crate::fuzz::minify_error;
use crate::tests::PROPTEST_FACTOR_10;
use crate::ContainerType;
use super::minify_error;
use super::normalize;
use super::test_multi_sites;
use super::Action;
use super::Action::*;
@ -759,6 +701,8 @@ mod failed_tests {
value: Container(C::List),
},
],
test_multi_sites,
normalize,
)
}

View file

@ -601,11 +601,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
node.borrow_from_sibling(sibling, either, notify);
}
} else {
if node.parent().unwrap().is_root() {
continue;
}
unreachable!();
continue;
}
if to_delete {