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;