diff --git a/crates/derive/src/stages/channel/channel_assembler.rs b/crates/derive/src/stages/channel/channel_assembler.rs index 609ec7c7e..2e48de481 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 f2f4422fe..7f2b05c6b 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 c51f68d4f..6d6ad94b9 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 23b358c73..945dedcd3 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 000000000..9c4a8eb0a --- /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 000000000..b629bf591 --- /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 4ee92875b..0a4e7d2d4 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;