Skip to content

Commit 0bdb7d3

Browse files
committed
test(common): Add lazy_loader::from_all_chunks.
This patch adds the new `from_all_chunks` function in the `linked_chunk::lazy_loader` module. It is only used for testing purposes. It aims at replacing `LinkedChunkBuilderTest` (see next patches). Why? Because `from_all_chunks` uses `from_last_chunk` and `insert_new_first_chunk`: if `from_all_chunks` is able to find all errors that `LinkedChunkBuilderTest` finds, it's a bingo. Transitively, it proves that `from_last_chunk` and `insert_new_first_chunk` are correct!
1 parent 5c57631 commit 0bdb7d3

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

crates/matrix-sdk-common/src/linked_chunk/lazy_loader.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,65 @@ where
196196
Ok(())
197197
}
198198

199+
/// A pretty inefficient, test-only, function to rebuild a full `LinkedChunk`.
200+
#[doc(hidden)]
201+
pub fn from_all_chunks<const CAP: usize, Item, Gap>(
202+
mut chunks: Vec<RawChunk<Item, Gap>>,
203+
) -> Result<Option<LinkedChunk<CAP, Item, Gap>>, LazyLoaderError>
204+
where
205+
Item: Clone,
206+
Gap: Clone,
207+
{
208+
if chunks.is_empty() {
209+
return Ok(None);
210+
}
211+
212+
// Sort by `next` so that the search for the next chunk is faster (it should
213+
// come first). The chunk with the biggest next chunk identifier comes first.
214+
// Chunk with no next chunk comes last.
215+
chunks.sort_by(|a, b| b.next.cmp(&a.next));
216+
217+
let last_chunk = chunks
218+
.pop()
219+
// SAFETY: `chunks` is guaranteed to not be empty, `pop` cannot fail.
220+
.expect("`chunks` is supposed to not be empty, we must be able to `pop` an item");
221+
let last_chunk_identifier = last_chunk.identifier;
222+
let chunk_identifier_generator =
223+
ChunkIdentifierGenerator::new_from_previous_chunk_identifier(last_chunk_identifier);
224+
225+
let Some(mut linked_chunk) = from_last_chunk(Some(last_chunk), chunk_identifier_generator)?
226+
else {
227+
return Ok(None);
228+
};
229+
230+
let mut next_chunk = last_chunk_identifier;
231+
232+
while let Some(chunk) = chunks
233+
.iter()
234+
.position(|chunk| chunk.next == Some(next_chunk))
235+
.map(|index| chunks.remove(index))
236+
{
237+
next_chunk = chunk.identifier;
238+
insert_new_first_chunk(&mut linked_chunk, chunk)?;
239+
}
240+
241+
let first_chunk = linked_chunk.links.first_chunk();
242+
243+
// It is expected that **all chunks** are passed to this function. If there was
244+
// a previous chunk, `insert_new_first_chunk` has erased it and moved it to
245+
// `lazy_previous`. Hence, let's check both (the former condition isn't
246+
// necessary, but better be robust).
247+
if first_chunk.previous().is_some() || first_chunk.lazy_previous.is_some() {
248+
return Err(LazyLoaderError::ChunkIsNotFirst { id: first_chunk.identifier() });
249+
}
250+
251+
if !chunks.is_empty() {
252+
return Err(LazyLoaderError::MultipleConnectedComponents);
253+
}
254+
255+
Ok(Some(linked_chunk))
256+
}
257+
199258
#[derive(thiserror::Error, Debug)]
200259
pub enum LazyLoaderError {
201260
#[error("chunk with id {} has a next chunk, it is supposed to be the last chunk", id.index())]
@@ -216,6 +275,18 @@ pub enum LazyLoaderError {
216275

217276
#[error("chunk with id {} is too large", id.index())]
218277
ChunkTooLarge { id: ChunkIdentifier },
278+
279+
#[doc(hidden)]
280+
#[error("the last chunk is missing")]
281+
MissingLastChunk,
282+
283+
#[doc(hidden)]
284+
#[error("chunk with id {} has a previous chunk, it is supposed to be the first chunk", id.index())]
285+
ChunkIsNotFirst { id: ChunkIdentifier },
286+
287+
#[doc(hidden)]
288+
#[error("multiple connected components")]
289+
MultipleConnectedComponents,
219290
}
220291

221292
#[cfg(test)]

0 commit comments

Comments
 (0)