1use onestore::{
2 CommitState, ExGuid, PageCreation, PageEdit, RevisionIndex, Store,
3 document::{Document, FieldValue, Kind},
4 op::{PageOp, SectionOp},
5};
6use std::{collections::BTreeMap, sync::LazyLock};
7
8#[path = "current.rs"]
9pub(crate) mod current;
10#[path = "disk.rs"]
11pub(crate) mod disk;
12#[path = "ops.rs"]
13pub(crate) mod ops;
14
15static SOURCE: LazyLock<Vec<u8>> = LazyLock::new(|| {
16 onestore::create_section("pages.one", "Original 🦀 é 東京", "Author").unwrap()
17});
18
19pub fn run(input: &[u8]) {
20 let source = match input.first().copied().unwrap_or(0) % 5 {
21 1 => include_bytes!("../../../../corpus/page-lifecycle/page-edits/optional-cache/source/Lifecycle.one")
22 .as_slice(),
23 2 => include_bytes!("../../../../corpus/page-lifecycle/page-edits/optional-cache/source-cold/notebook/Lifecycle.one")
24 .as_slice(),
25 3 => include_bytes!("../../../../corpus/page-lifecycle/removal/features/native/before/notebook/Lifecycle.one").as_slice(),
26 4 => include_bytes!("../../../../corpus/page-lifecycle/removal/ink/native/before/notebook/Lifecycle.one").as_slice(),
27 _ => SOURCE.as_slice(),
28 };
29 if let Ok(intent) = serde_json::from_slice::<PageCreation>(input)
30 && let Ok(prepared) = ops::section_op(source, SectionOp::Create(intent.clone()))
31 {
32 current::current(prepared.as_bytes());
33 }
34 if let Ok(edits) = serde_json::from_slice::<Vec<PageEdit>>(input)
35 && let Ok(prepared) = ops::section_op(source, SectionOp::Pages(edits.to_vec()))
36 {
37 current::current(prepared.as_bytes());
38 }
39 if let Ok(pages) = serde_json::from_slice::<Vec<ExGuid>>(input)
40 && let Ok(prepared) = ops::section_op(source, SectionOp::Delete(pages.to_vec()))
41 {
42 current::current(prepared.as_bytes());
43 }
44 let mut persisted = source.to_vec();
45 let mut caches = std::array::from_fn::<_, 12, _>(|_| source.to_vec());
46 for step in input.chunks_exact(8).take(24) {
47 let actor = usize::from(step[0]) % caches.len();
48 if step[1] % 3 == 0 {
49 caches[actor].clone_from(&persisted);
50 }
51 let source = &caches[actor];
52 let store = Store::parse(source).unwrap();
53 let index = RevisionIndex::parse(&store).unwrap();
54 let document = Document::parse(&index).unwrap();
55 let mut pages = document.pages().unwrap();
56 let mut levels: BTreeMap<_, _> = pages
57 .iter()
58 .map(|(sid, _)| {
59 let space = &document.spaces[sid];
60 let view = &space.revisions[&space.contexts[&ExGuid::default()]];
61 let Kind::Metadata { level, .. } = view.nodes[&view.roots[&2]].kind else {
62 panic!()
63 };
64 (*sid, level.unwrap_or(1))
65 })
66 .collect();
67 let selected = pages
68 .get(usize::from(step[2]) % pages.len().max(1))
69 .copied();
70 let text = ["", "Same title", "é 🦋 東京", " spaces "][usize::from(step[3]) % 4];
71 let existing = if let Some((sid, _)) = selected {
72 let space = &document.spaces[&sid];
73 let view = &space.revisions[&space.contexts[&ExGuid::default()]];
74 let titles: Vec<_> = view
75 .nodes
76 .iter()
77 .filter_map(|(id, node)| {
78 (matches!(
79 node.kind,
80 Kind::RichText {
81 boilerplate: false,
82 ..
83 }
84 ) && node.extra[0].iter().any(|field| field.id == 0x88001cb4))
85 .then_some(*id)
86 })
87 .collect();
88 if step[1] & 64 != 0 {
89 let count = if step[3] & 128 != 0 {
90 pages.len()
91 } else {
92 1 + usize::from(step[3] & 64 != 0 && pages.len() > 1)
93 };
94 let removed: Vec<_> = (0..count)
95 .map(|i| pages[(usize::from(step[2]) + i) % pages.len()].0)
96 .collect();
97 let prepared =
98 ops::section_op(source, SectionOp::Delete(removed.to_vec())).unwrap();
99 pages.retain(|page| !removed.contains(&page.0));
100 for sid in removed {
101 levels.remove(&sid);
102 }
103 if let Some((first, _)) = pages.first() {
104 levels.insert(*first, 1);
105 }
106 Some((prepared, None))
107 } else if step[1] & 32 != 0 {
108 let count = 1 + usize::from(step[3] & 128 != 0 && pages.len() > 1);
109 let selected: Vec<_> = (0..count)
110 .map(|i| pages[(usize::from(step[2]) + i) % pages.len()].0)
111 .collect();
112 let before = (step[3] & 4 != 0)
113 .then_some(pages[usize::from(step[7]) % pages.len()].0)
114 .filter(|id| !selected.contains(id));
115 let mut edits = Vec::new();
116 for (ordinal, sid) in selected.iter().enumerate() {
117 let level = u32::from(if ordinal == 0 { step[3] } else { step[6] }) % 3 + 1;
118 levels.insert(*sid, level);
119 let edit = if step[3] & 8 == 0 {
120 PageEdit::set_level(*sid, level).unwrap()
121 } else {
122 let at = pages.iter().position(|p| p.0 == *sid).unwrap();
123 let page = pages.remove(at);
124 let at = before.map_or(pages.len(), |id| {
125 pages.iter().position(|p| p.0 == id).unwrap()
126 });
127 pages.insert(at, page);
128 PageEdit::move_to(*sid, before, level).unwrap()
129 };
130 edits.push(edit);
131 }
132 let restored: Vec<PageEdit> =
133 serde_json::from_value(serde_json::to_value(&edits).unwrap()).unwrap();
134 assert_eq!(edits, restored);
135 let prepared = ops::section_op(source, SectionOp::Pages(restored.to_vec()));
136 if levels[&pages[0].0] != 1 {
137 assert!(prepared.is_err());
138 continue;
139 }
140 Some((prepared.unwrap(), None))
141 } else if step[1] & 8 != 0 && !titles.is_empty() {
142 let object = titles[0];
143 let Kind::RichText { text: original, .. } = &view.nodes[&object].kind else {
144 unreachable!()
145 };
146 Some((
147 ops::page_op(
148 source,
149 sid,
150 PageOp::Text {
151 text: object,
152 range: 0..u32::try_from(original.encode_utf16().count()).unwrap(),
153 with: text.into(),
154 },
155 )
156 .unwrap(),
157 Some((sid, object, text)),
158 ))
159 } else if step[1] & 16 != 0 {
160 let (add, _, inserted) = ops::new_outline(36.0, 36.0, text);
161 Some((
162 ops::page_op(source, sid, add).unwrap(),
163 Some((sid, inserted, text)),
164 ))
165 } else {
166 None
167 }
168 } else {
169 None
170 };
171 let (edit, title_update) = existing.unwrap_or_else(|| {
172 let before = if step[2] & 1 != 0 {
173 selected.map(|(sid, _)| {
174 let section = &document.spaces[&document.root];
175 let section = &section.revisions[&section.contexts[&ExGuid::default()]];
176 section.nodes[&section.roots[&1]]
177 .children
178 .iter()
179 .map(|id| &section.nodes[id])
180 .find(|series| series.spaces.contains(&sid))
181 .unwrap()
182 .spaces[0]
183 })
184 } else {
185 None
186 };
187 let title = (step[3] & 4 == 0).then_some(text);
188 let intent = PageCreation::new(before, title, "Page fuzz").unwrap();
189 let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap();
190 assert_eq!(intent, restored);
191 let edit = ops::section_op(source, SectionOp::Create(restored.clone())).unwrap();
192 let position = before.map_or(pages.len(), |sid| {
193 pages.iter().position(|p| p.0 == sid).unwrap()
194 });
195 pages.insert(position, (intent.space(), intent.object()));
196 levels.insert(intent.space(), 1);
197 (
198 edit,
199 intent
200 .title_object()
201 .map(|object| (intent.space(), object, text)),
202 )
203 });
204 let after_store = Store::parse(edit.as_bytes()).unwrap();
205 let after_index = RevisionIndex::parse(&after_store).unwrap();
206 let after_document = Document::parse(&after_index).unwrap();
207 assert_eq!(after_document.pages().unwrap(), pages);
208 let mut metadata_levels = BTreeMap::new();
209 for (sid, _) in &pages {
210 let space = &after_document.spaces[sid];
211 let view = &space.revisions[&space.contexts[&ExGuid::default()]];
212 let Kind::Metadata { level, .. } = view.nodes[&view.roots[&2]].kind else {
213 panic!()
214 };
215 assert_eq!(level.unwrap_or(1), levels[sid]);
216 let metadata = &view.nodes[&view.roots[&2]];
217 let FieldValue::Bytes(guid) = metadata.extra[0]
218 .iter()
219 .find(|field| field.id == 0x1c001c30)
220 .unwrap()
221 .value
222 else {
223 panic!()
224 };
225 assert!(metadata_levels.insert(guid, levels[sid]).is_none());
226 }
227 let section = &after_document.spaces[&after_document.root];
228 let view = &section.revisions[&section.contexts[&ExGuid::default()]];
229 for metadata in view.nodes.values() {
230 if let Kind::Metadata { level, .. } = metadata.kind {
231 let FieldValue::Bytes(guid) = metadata.extra[0]
232 .iter()
233 .find(|field| field.id == 0x1c001c30)
234 .unwrap()
235 .value
236 else {
237 panic!()
238 };
239 assert_eq!(metadata_levels.get(guid), Some(&level.unwrap_or(1)));
240 }
241 }
242 if let Some((sid, object, text)) = title_update {
243 let space = &after_document.spaces[&sid];
244 let view = &space.revisions[&space.contexts[&ExGuid::default()]];
245 assert!(
246 matches!(&view.nodes[&object].kind, Kind::RichText { text: actual, .. } if actual == text)
247 );
248 }
249 for (sid, space) in &index.spaces {
250 let rid = space.labels[&(ExGuid::default(), 1)];
251 assert_eq!(
252 format!("{:?}", index.resolve(*sid, rid).unwrap()),
253 format!("{:?}", after_index.resolve(*sid, rid).unwrap())
254 );
255 }
256 let before = current::current(&persisted);
257 let after = current::current(edit.as_bytes());
258 let mut disk = disk::Disk {
259 visible: persisted.clone(),
260 durable: persisted.clone(),
261 operation: 0,
262 fail_at: (step[4] != 0).then_some(usize::from(u16::from_le_bytes([step[4], step[5]]))),
263 write_limit: if step[6] & 1 == 0 { 17 } else { 4096 },
264 random: u64::from(step[7]) + 1,
265 };
266 let Some(transaction) = &edit.transaction else {
267 continue;
268 };
269 let result = transaction.commit(&mut disk);
270 let observed = current::current(&disk.durable);
271 match result {
272 Ok(()) => assert_eq!(observed, after),
273 Err(error) => {
274 assert!(observed == before || observed == after);
275 match error.state {
276 CommitState::NotCommitted => assert_eq!(observed, before),
277 CommitState::Committed => assert_eq!(observed, after),
278 CommitState::Unknown => {}
279 }
280 }
281 }
282 persisted = disk.durable;
283 }
284}