diff --git a/Cargo.lock b/Cargo.lock index 11e29e1f..080a6a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f35429a652765189c1c5092870d8360ee7b7769b09b06d89ebaefd34676446" +checksum = "c71738eb20c42c5fb149571e76536a0f309d142f3957c28791662b96baf77a3d" dependencies = [ "alloy-rlp", "arbitrary", @@ -282,7 +282,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -396,23 +396,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2395336745358cc47207442127c47c63801a7065ecc0aa928da844f8bb5576" +checksum = "b0900b83f4ee1f45c640ceee596afbc118051921b9438fdb5a3175c1a7e05f8b" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed5047c9a241df94327879c2b0729155b58b941eae7805a7ada2e19436e6b39" +checksum = "a41b1e78dde06b5e12e6702fa8c1d30621bf07728ba75b801fb801c9c6a0ba10" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -421,31 +421,31 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dee02a81f529c415082235129f0df8b8e60aa1601b9c9298ffe54d75f57210b" +checksum = "91dc311a561a306664393407b88d3e53ae58581624128afd8a15faa5de3627dc" dependencies = [ "const-hex", "dunce", "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "syn-solidity", ] [[package]] name = "alloy-sol-types" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2841af22d99e2c0f82a78fe107b6481be3dd20b89bfb067290092794734343a" +checksum = "086f41bc6ebcd8cb15f38ba20e47be38dd03692149681ce8061c35d960dbf850" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -733,7 +733,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -744,7 +744,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -771,7 +771,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -830,7 +830,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -944,7 +944,7 @@ checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1108,7 +1108,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1306,7 +1306,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1317,7 +1317,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1382,7 +1382,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1403,7 +1403,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "unicode-xid", ] @@ -1496,7 +1496,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1664,7 +1664,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2216,7 +2216,7 @@ dependencies = [ "kona-common", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2551,7 +2551,7 @@ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2730,7 +2730,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2848,7 +2848,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -2986,7 +2986,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3130,7 +3130,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3210,7 +3210,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3236,7 +3236,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3563,7 +3563,7 @@ checksum = "09cb82b74b4810f07e460852c32f522e979787691b0b7b7439fe473e49d49b2f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3867,14 +3867,14 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] name = "serde_json" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "indexmap 2.6.0", "itoa", @@ -3891,7 +3891,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3933,7 +3933,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4103,7 +4103,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4161,9 +4161,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2", "quote", @@ -4172,14 +4172,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfc1bfd06acc78f16d8fd3ef846bc222ee7002468d10a7dce8d703d6eab89a3" +checksum = "9d5e0c2ea8db64b2898b62ea2fbd60204ca95e0b2c6bdf53ff768bbe916fbe4d" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4262,7 +4262,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4272,7 +4272,7 @@ source = "git+https://github.com/quartiq/thiserror?branch=no-std#e779e1b70023cee dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4385,7 +4385,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4496,7 +4496,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4712,7 +4712,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-shared", ] @@ -4746,7 +4746,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4969,7 +4969,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4989,5 +4989,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.82", ] diff --git a/crates/derive/src/stages/channel/channel_assembler.rs b/crates/derive/src/stages/channel/channel_assembler.rs index 609ec7c7..2e48de48 100644 --- a/crates/derive/src/stages/channel/channel_assembler.rs +++ b/crates/derive/src/stages/channel/channel_assembler.rs @@ -200,7 +200,7 @@ mod test { use super::ChannelAssembler; use crate::{ prelude::PipelineError, - stages::{frame_queue::tests::new_test_frames, ChannelReaderProvider}, + stages::ChannelReaderProvider, test_utils::{CollectingLayer, TestNextFrameProvider, TraceStorage}, }; use alloc::sync::Arc; @@ -217,7 +217,10 @@ mod test { let layer = CollectingLayer::new(trace_store.clone()); tracing_subscriber::Registry::default().with(layer).init(); - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let mock = TestNextFrameProvider::new(frames.into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); let mut assembler = ChannelAssembler::new(cfg, mock); @@ -255,7 +258,10 @@ mod test { #[tokio::test] async fn test_assembler_non_starting_frame() { - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let mock = TestNextFrameProvider::new(frames.into_iter().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); let mut assembler = ChannelAssembler::new(cfg, mock); @@ -273,7 +279,10 @@ mod test { let layer = CollectingLayer::new(trace_store.clone()); tracing_subscriber::Registry::default().with(layer).init(); - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let mock = TestNextFrameProvider::new(frames.clone().into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); let mut assembler = ChannelAssembler::new(cfg, mock); @@ -308,7 +317,10 @@ mod test { let layer = CollectingLayer::new(trace_store.clone()); tracing_subscriber::Registry::default().with(layer).init(); - let mut frames = new_test_frames(2); + let mut frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; frames[1].data = vec![0; MAX_RLP_BYTES_PER_CHANNEL_BEDROCK as usize]; let mock = TestNextFrameProvider::new(frames.into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); @@ -339,7 +351,10 @@ mod test { let layer = CollectingLayer::new(trace_store.clone()); tracing_subscriber::Registry::default().with(layer).init(); - let mut frames = new_test_frames(2); + let mut frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; frames[1].data = vec![0; MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize]; let mock = TestNextFrameProvider::new(frames.into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig { fjord_time: Some(0), ..Default::default() }); diff --git a/crates/derive/src/stages/channel/channel_bank.rs b/crates/derive/src/stages/channel/channel_bank.rs index f2f4422f..7f2b05c6 100644 --- a/crates/derive/src/stages/channel/channel_bank.rs +++ b/crates/derive/src/stages/channel/channel_bank.rs @@ -276,7 +276,6 @@ where mod tests { use super::*; use crate::{ - stages::frame_queue::tests::new_test_frames, test_utils::{CollectingLayer, TestNextFrameProvider, TraceStorage}, traits::ResetSignal, }; @@ -287,7 +286,7 @@ mod tests { #[test] fn test_channel_bank_into_prev() { - let frames = new_test_frames(1); + let frames = [crate::frame!(0xFF, 0, vec![0xDD; 50], true)]; let mock = TestNextFrameProvider::new(frames.into_iter().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); let channel_bank = ChannelBank::new(cfg, mock); @@ -485,8 +484,7 @@ mod tests { #[test] fn test_ingest_and_prune_channel_bank() { - use alloc::vec::Vec; - let mut frames: Vec = new_test_frames(100000); + let mut frames = crate::frames!(0xFF, 0, vec![0xDD; 50], 100000); let mock = TestNextFrameProvider::new(vec![]); let cfg = Arc::new(RollupConfig::default()); let mut channel_bank = ChannelBank::new(cfg, mock); @@ -511,8 +509,7 @@ mod tests { #[test] fn test_ingest_and_prune_channel_bank_fjord() { - use alloc::vec::Vec; - let mut frames: Vec = new_test_frames(100000); + let mut frames = crate::frames!(0xFF, 0, vec![0xDD; 50], 100000); let mock = TestNextFrameProvider::new(vec![]); let cfg = Arc::new(RollupConfig { fjord_time: Some(0), ..Default::default() }); let mut channel_bank = ChannelBank::new(cfg, mock); @@ -537,7 +534,7 @@ mod tests { #[tokio::test] async fn test_read_empty_channel_bank() { - let frames = new_test_frames(1); + let frames = [crate::frame!(0xFF, 0, vec![0xDD; 50], true)]; let mock = TestNextFrameProvider::new(vec![Ok(frames[0].clone())]); let cfg = Arc::new(RollupConfig::default()); let mut channel_bank = ChannelBank::new(cfg, mock); @@ -556,7 +553,10 @@ mod tests { const ROLLUP_CONFIGS: [RollupConfig; 2] = [OP_MAINNET_CONFIG, BASE_MAINNET_CONFIG]; for cfg in ROLLUP_CONFIGS { - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let mock = TestNextFrameProvider::new(frames.into_iter().map(Ok).collect::>()); let cfg = Arc::new(cfg); let mut channel_bank = ChannelBank::new(cfg.clone(), mock); diff --git a/crates/derive/src/stages/channel/channel_provider.rs b/crates/derive/src/stages/channel/channel_provider.rs index c51f68d4..6d6ad94b 100644 --- a/crates/derive/src/stages/channel/channel_provider.rs +++ b/crates/derive/src/stages/channel/channel_provider.rs @@ -31,7 +31,7 @@ mod test { use super::{ActiveStage, ChannelProvider}; use crate::{ prelude::{OriginProvider, PipelineError}, - stages::{frame_queue::tests::new_test_frames, ChannelReaderProvider}, + stages::ChannelReaderProvider, test_utils::TestNextFrameProvider, traits::{ResetSignal, SignalReceiver}, }; @@ -153,7 +153,10 @@ mod test { #[tokio::test] async fn test_channel_provider_reset_bank() { - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let provider = TestNextFrameProvider::new(frames.into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig::default()); let mut channel_provider = ChannelProvider::new(cfg.clone(), provider); @@ -181,7 +184,10 @@ mod test { #[tokio::test] async fn test_channel_provider_reset_assembler() { - let frames = new_test_frames(2); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), + ]; let provider = TestNextFrameProvider::new(frames.into_iter().rev().map(Ok).collect()); let cfg = Arc::new(RollupConfig { holocene_time: Some(0), ..Default::default() }); let mut channel_provider = ChannelProvider::new(cfg.clone(), provider); diff --git a/crates/derive/src/stages/frame_queue.rs b/crates/derive/src/stages/frame_queue.rs index 23b358c7..945dedcd 100644 --- a/crates/derive/src/stages/frame_queue.rs +++ b/crates/derive/src/stages/frame_queue.rs @@ -199,33 +199,7 @@ where pub(crate) mod tests { use super::*; use crate::{test_utils::TestFrameQueueProvider, traits::ResetSignal}; - use alloc::{vec, vec::Vec}; - use op_alloy_protocol::DERIVATION_VERSION_0; - - pub(crate) fn new_test_frames(count: usize) -> Vec { - (0..count) - .map(|i| Frame { - id: [0xFF; 16], - number: i as u16, - data: vec![0xDD; 50], - is_last: i == count - 1, - }) - .collect() - } - - pub(crate) fn encode_frames(frames: Vec) -> Bytes { - let mut bytes = Vec::new(); - bytes.extend_from_slice(&[DERIVATION_VERSION_0]); - for frame in frames.iter() { - bytes.extend_from_slice(&frame.encode()); - } - Bytes::from(bytes) - } - - pub(crate) fn new_encoded_test_frames(count: usize) -> Bytes { - let frames = new_test_frames(count); - encode_frames(frames) - } + use alloc::vec; #[tokio::test] async fn test_frame_queue_reset() { @@ -261,321 +235,271 @@ pub(crate) mod tests { #[tokio::test] async fn test_frame_queue_wrong_derivation_version() { - let data = vec![Ok(Bytes::from(vec![0x01]))]; - let mut mock = TestFrameQueueProvider::new(data); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Default::default()); - assert!(!frame_queue.is_holocene_active(BlockInfo::default())); - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::NotEnoughData.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_origin(BlockInfo::default()) + .with_raw_frames(Bytes::from(vec![0x01])) + .with_expected_err(PipelineError::NotEnoughData.temp()) + .build(); + assert.holocene_active(false); + assert.next_frames().await; } #[tokio::test] async fn test_frame_queue_frame_too_short() { - let data = vec![Ok(Bytes::from(vec![0x00, 0x01]))]; - let mut mock = TestFrameQueueProvider::new(data); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Default::default()); - assert!(!frame_queue.is_holocene_active(BlockInfo::default())); - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::NotEnoughData.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_origin(BlockInfo::default()) + .with_raw_frames(Bytes::from(vec![0x00, 0x01])) + .with_expected_err(PipelineError::NotEnoughData.temp()) + .build(); + assert.holocene_active(false); + assert.next_frames().await; } #[tokio::test] async fn test_frame_queue_single_frame() { - let data = new_encoded_test_frames(1); - let mut mock = TestFrameQueueProvider::new(vec![Ok(data)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Default::default()); - assert!(!frame_queue.is_holocene_active(BlockInfo::default())); - let frame_decoded = frame_queue.next_frame().await.unwrap(); - let frame = new_test_frames(1); - assert_eq!(frame[0], frame_decoded); - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let frames = [crate::frame!(0xFF, 0, vec![0xDD; 50], true)]; + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_expected_frames(&frames) + .with_origin(BlockInfo::default()) + .with_frames(&frames) + .build(); + assert.holocene_active(false); + assert.next_frames().await; } #[tokio::test] async fn test_frame_queue_multiple_frames() { - let data = new_encoded_test_frames(3); - let mut mock = TestFrameQueueProvider::new(vec![Ok(data)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Default::default()); - assert!(!frame_queue.is_holocene_active(BlockInfo::default())); - for i in 0..3 { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded.number, i); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], false), + crate::frame!(0xFF, 2, vec![0xDD; 50], true), + ]; + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_expected_frames(&frames) + .with_origin(BlockInfo::default()) + .with_frames(&frames) + .build(); + assert.holocene_active(false); + assert.next_frames().await; } #[tokio::test] async fn test_frame_queue_missing_origin() { - let data = new_encoded_test_frames(1); - let mock = TestFrameQueueProvider::new(vec![Ok(data)]); - let mut frame_queue = FrameQueue::new(mock, Default::default()); - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::MissingOrigin.crit()); + let frames = [crate::frame!(0xFF, 0, vec![0xDD; 50], true)]; + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_expected_frames(&frames) + .with_frames(&frames) + .build(); + assert.holocene_active(false); + assert.missing_origin().await; } #[tokio::test] async fn test_holocene_valid_frames() { - let channel = new_encoded_test_frames(3); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(channel)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for i in 0..3 { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded.number, i); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let frames = [ + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], false), + crate::frame!(0xFF, 2, vec![0xDD; 50], true), + ]; + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] - async fn test_holocene_single_invalid_frame() { - let frames = vec![Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: true }]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - let decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(decoded, frames[0]); - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + async fn test_holocene_single_frame() { + let frames = [crate::frame!(0xFF, 1, vec![0xDD; 50], true)]; + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_unordered_frames() { - let frames = vec![ + let frames = [ // -- First Channel -- - Frame { id: [0xEE; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 2, data: vec![0xDD; 50], is_last: true }, - // Frame with the same channel id, but after is_last should be dropped. - Frame { id: [0xEE; 16], number: 3, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0xEE, 0, vec![0xDD; 50], false), + crate::frame!(0xEE, 1, vec![0xDD; 50], false), + crate::frame!(0xEE, 2, vec![0xDD; 50], true), + crate::frame!(0xEE, 3, vec![0xDD; 50], false), // Dropped // -- Next Channel -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames.iter().take(3) { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - for i in 0..2 { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, frames[i + 4]); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&[&frames[0..3], &frames[4..]].concat()) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_non_sequential_frames() { - let frames = vec![ + let frames = [ // -- First Channel -- - Frame { id: [0xEE; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - // Both this and the next frames should be dropped since neither will be - // interpreted as having the next sequential frame number after 1. - Frame { id: [0xEE; 16], number: 3, data: vec![0xDD; 50], is_last: true }, - Frame { id: [0xEE; 16], number: 4, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0xEE, 0, vec![0xDD; 50], false), + crate::frame!(0xEE, 1, vec![0xDD; 50], false), + crate::frame!(0xEE, 3, vec![0xDD; 50], true), // Dropped + crate::frame!(0xEE, 4, vec![0xDD; 50], false), // Dropped ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames.iter().take(2) { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames[0..2]) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_unclosed_channel() { - let frames = vec![ + let frames = [ // -- First Channel -- - // Since this channel isn't closed by a last frame it is entirely dropped - Frame { id: [0xEE; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 2, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 3, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0xEE, 0, vec![0xDD; 50], false), + crate::frame!(0xEE, 1, vec![0xDD; 50], false), + crate::frame!(0xEE, 2, vec![0xDD; 50], false), + crate::frame!(0xEE, 3, vec![0xDD; 50], false), // -- Next Channel -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for i in 0..2 { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, frames[i + 4]); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames[4..]) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_unstarted_channel() { - let frames = vec![ + let frames = [ // -- First Channel -- - Frame { id: [0xDD; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xDD; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xDD; 16], number: 2, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xDD; 16], number: 3, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xDD, 0, vec![0xDD; 50], false), + crate::frame!(0xDD, 1, vec![0xDD; 50], false), + crate::frame!(0xDD, 2, vec![0xDD; 50], false), + crate::frame!(0xDD, 3, vec![0xDD; 50], true), // -- Second Channel -- - // Since this channel doesn't have a starting frame where number == 0, - // it is entirely dropped. - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 2, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xEE, 1, vec![0xDD; 50], false), // Dropped + crate::frame!(0xEE, 2, vec![0xDD; 50], true), // Dropped // -- Third Channel -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames.iter().take(4) { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - for i in 0..2 { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, frames[i + 6]); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&[&frames[0..4], &frames[6..]].concat()) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } - // Notice: The first channel is **not** dropped here because there can still be - // frames that come in to successfully close the channel. #[tokio::test] async fn test_holocene_unclosed_channel_with_invalid_start() { - let frames = vec![ + let frames = [ // -- First Channel -- - Frame { id: [0xEE; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 2, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 3, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0xEE, 0, vec![0xDD; 50], false), + crate::frame!(0xEE, 1, vec![0xDD; 50], false), + crate::frame!(0xEE, 2, vec![0xDD; 50], false), + crate::frame!(0xEE, 3, vec![0xDD; 50], false), // -- Next Channel -- - // This is also an invalid channel because it is never started - // since there isn't a first frame with number == 0 - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 2, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 1, vec![0xDD; 50], false), // Dropped + crate::frame!(0xFF, 2, vec![0xDD; 50], true), // Dropped ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames.iter().take(4) { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames[0..4]) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_replace_channel() { - let frames = vec![ + let frames = [ // -- First Channel - VALID & CLOSED -- - Frame { id: [0xDD; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xDD; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xDD, 0, vec![0xDD; 50], false), + crate::frame!(0xDD, 1, vec![0xDD; 50], true), // -- Second Channel - VALID & NOT CLOSED / DROPPED -- - Frame { id: [0xEE; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xEE; 16], number: 1, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0xEE, 0, vec![0xDD; 50], false), + crate::frame!(0xEE, 1, vec![0xDD; 50], false), // -- Third Channel - VALID & CLOSED / REPLACES CHANNEL #2 -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames.iter().filter(|f| f.id != [0xEE; 16]) { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&[&frames[0..2], &frames[4..]].concat()) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_interleaved_invalid_channel() { - let frames = vec![ + let frames = [ // -- First channel is dropped since it is replaced by the second channel -- // -- Second channel is dropped since it isn't closed -- - Frame { id: [0x01; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0x02; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0x01; 16], number: 1, data: vec![0xDD; 50], is_last: true }, - Frame { id: [0x02; 16], number: 1, data: vec![0xDD; 50], is_last: false }, + crate::frame!(0x01, 0, vec![0xDD; 50], false), + crate::frame!(0x02, 0, vec![0xDD; 50], false), + crate::frame!(0x01, 1, vec![0xDD; 50], true), + crate::frame!(0x02, 1, vec![0xDD; 50], false), // -- Third Channel - VALID & CLOSED -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in frames[4..].iter() { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, *frame); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&frames[4..]) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } #[tokio::test] async fn test_holocene_interleaved_valid_channel() { - let frames = vec![ + let frames = [ // -- First channel is dropped since it is replaced by the second channel -- // -- Second channel is successfully closed so it's valid -- - Frame { id: [0x01; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0x02; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0x01; 16], number: 1, data: vec![0xDD; 50], is_last: true }, - Frame { id: [0x02; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0x01, 0, vec![0xDD; 50], false), + crate::frame!(0x02, 0, vec![0xDD; 50], false), + crate::frame!(0x01, 1, vec![0xDD; 50], true), + crate::frame!(0x02, 1, vec![0xDD; 50], true), // -- Third Channel - VALID & CLOSED -- - Frame { id: [0xFF; 16], number: 0, data: vec![0xDD; 50], is_last: false }, - Frame { id: [0xFF; 16], number: 1, data: vec![0xDD; 50], is_last: true }, + crate::frame!(0xFF, 0, vec![0xDD; 50], false), + crate::frame!(0xFF, 1, vec![0xDD; 50], true), ]; - let encoded = encode_frames(frames.clone()); - let config = RollupConfig { holocene_time: Some(0), ..Default::default() }; - let mut mock = TestFrameQueueProvider::new(vec![Ok(encoded)]); - mock.set_origin(BlockInfo::default()); - let mut frame_queue = FrameQueue::new(mock, Arc::new(config)); - assert!(frame_queue.is_holocene_active(BlockInfo::default())); - for frame in [&frames[1], &frames[3], &frames[4], &frames[5]].iter() { - let frame_decoded = frame_queue.next_frame().await.unwrap(); - assert_eq!(frame_decoded, **frame); - } - let err = frame_queue.next_frame().await.unwrap_err(); - assert_eq!(err, PipelineError::Eof.temp()); + let assert = crate::test_utils::FrameQueueBuilder::new() + .with_rollup_config(&RollupConfig { holocene_time: Some(0), ..Default::default() }) + .with_origin(BlockInfo::default()) + .with_expected_frames(&[&frames[1..2], &frames[3..]].concat()) + .with_frames(&frames) + .build(); + assert.holocene_active(true); + assert.next_frames().await; } } diff --git a/crates/derive/src/test_utils/frames.rs b/crates/derive/src/test_utils/frames.rs new file mode 100644 index 00000000..9c4a8eb0 --- /dev/null +++ b/crates/derive/src/test_utils/frames.rs @@ -0,0 +1,134 @@ +//! Frames + +use crate::{ + errors::{PipelineError, PipelineErrorKind}, + stages::{FrameQueue, NextFrameProvider}, + test_utils::TestFrameQueueProvider, + traits::OriginProvider, +}; +use alloc::{sync::Arc, vec, vec::Vec}; +use alloy_primitives::Bytes; +use op_alloy_genesis::RollupConfig; +use op_alloy_protocol::{BlockInfo, Frame, DERIVATION_VERSION_0}; + +/// A [FrameQueue] builder. +#[derive(Debug, Default)] +pub struct FrameQueueBuilder { + origin: Option, + config: Option, + mock: Option, + expected_frames: Vec, + expected_err: Option, +} + +fn encode_frames(frames: &[Frame]) -> Bytes { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&[DERIVATION_VERSION_0]); + for frame in frames.iter() { + bytes.extend_from_slice(&frame.encode()); + } + Bytes::from(bytes) +} + +impl FrameQueueBuilder { + /// Create a new [FrameQueueBuilder] instance. + pub const fn new() -> Self { + Self { origin: None, config: None, mock: None, expected_frames: vec![], expected_err: None } + } + + /// Sets the rollup config. + pub fn with_rollup_config(mut self, config: &RollupConfig) -> Self { + self.config = Some(config.clone()); + self + } + + /// Set the origin block. + pub const fn with_origin(mut self, origin: BlockInfo) -> Self { + self.origin = Some(origin); + self + } + + /// With expected frames. + pub fn with_expected_frames(mut self, frames: &[Frame]) -> Self { + self.expected_frames = frames.to_vec(); + self + } + + /// Sets the expected error type. + pub fn with_expected_err(mut self, err: PipelineErrorKind) -> Self { + self.expected_err = Some(err); + self + } + + /// With raw frames. + pub fn with_raw_frames(mut self, raw: Bytes) -> Self { + let mock = self.mock.unwrap_or_else(|| TestFrameQueueProvider::new(vec![Ok(raw)])); + self.mock = Some(mock); + self + } + + /// Adds frames to the mock provider. + pub fn with_frames(mut self, frames: &[Frame]) -> Self { + let encoded = encode_frames(frames); + let mock = self.mock.unwrap_or_else(|| TestFrameQueueProvider::new(vec![Ok(encoded)])); + self.mock = Some(mock); + self + } + + /// Build the [FrameQueue]. + pub fn build(self) -> FrameQueueAsserter { + let mut mock = self.mock.unwrap_or_else(|| TestFrameQueueProvider::new(vec![])); + if let Some(origin) = self.origin { + mock.set_origin(origin); + } + let config = self.config.unwrap_or_default(); + let config = Arc::new(config); + let err = self.expected_err.unwrap_or_else(|| PipelineError::Eof.temp()); + FrameQueueAsserter::new(FrameQueue::new(mock, config), self.expected_frames, err) + } +} + +/// The [FrameQueueAsserter] validates frame queue outputs. +#[derive(Debug)] +pub struct FrameQueueAsserter { + inner: FrameQueue, + expected_frames: Vec, + expected_err: PipelineErrorKind, +} + +impl FrameQueueAsserter { + /// Create a new [FrameQueueAsserter] instance. + pub const fn new( + inner: FrameQueue, + expected_frames: Vec, + expected_err: PipelineErrorKind, + ) -> Self { + Self { inner, expected_frames, expected_err } + } + + /// Asserts that holocene is active. + pub fn holocene_active(&self, active: bool) { + let holocene = self.inner.is_holocene_active(self.inner.origin().unwrap_or_default()); + if !active { + assert!(!holocene); + } else { + assert!(holocene); + } + } + + /// Asserts that the frame queue returns with a missing origin error. + pub async fn missing_origin(mut self) { + let err = self.inner.next_frame().await.unwrap_err(); + assert_eq!(err, PipelineError::MissingOrigin.crit()); + } + + /// Asserts that the frame queue produces the expected frames. + pub async fn next_frames(mut self) { + for eframe in self.expected_frames.into_iter() { + let frame = self.inner.next_frame().await.expect("unexpected frame"); + assert_eq!(frame, eframe); + } + let err = self.inner.next_frame().await.unwrap_err(); + assert_eq!(err, self.expected_err); + } +} diff --git a/crates/derive/src/test_utils/macros.rs b/crates/derive/src/test_utils/macros.rs new file mode 100644 index 00000000..b629bf59 --- /dev/null +++ b/crates/derive/src/test_utils/macros.rs @@ -0,0 +1,19 @@ +//! Macros used across test utilities. + +/// A shorthand syntax for constructing [op_alloy_protocol::Frame]s. +#[macro_export] +macro_rules! frame { + ($id:expr, $number:expr, $data:expr, $is_last:expr) => { + op_alloy_protocol::Frame { id: [$id; 16], number: $number, data: $data, is_last: $is_last } + }; +} + +/// A shorthand syntax for constructing a list of [op_alloy_protocol::Frame]s. +#[macro_export] +macro_rules! frames { + ($id:expr, $number:expr, $data:expr, $count:expr) => {{ + let mut frames = vec![$crate::frame!($id, $number, $data, false); $count]; + frames[$count - 1].is_last = true; + frames + }}; +} diff --git a/crates/derive/src/test_utils/mod.rs b/crates/derive/src/test_utils/mod.rs index 4ee92875..0a4e7d2d 100644 --- a/crates/derive/src/test_utils/mod.rs +++ b/crates/derive/src/test_utils/mod.rs @@ -41,3 +41,8 @@ pub use tracing::{CollectingLayer, TraceStorage}; mod sys_config_fetcher; pub use sys_config_fetcher::TestSystemConfigL2Fetcher; + +mod frames; +pub use frames::{FrameQueueAsserter, FrameQueueBuilder}; + +mod macros;