Skip to content

Commit 80e81fb

Browse files
committed
Mock-like expectations for TestChainSource
Add a method to TestChainSource to test chain::Filter expectations. This is limited to register_output, allowing tests to assert that the method was called with a specific output and dictate what the return value is. Multiple expectations are checked in the order in which they were added. Failure occurs if a call doesn't match the next expectation or if there are unsatisfied expectations. If not expectations are added, then no calls are checked.
1 parent 6b14ade commit 80e81fb

File tree

1 file changed

+76
-2
lines changed

1 file changed

+76
-2
lines changed

lightning/src/util/test_utils.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use std::time::Duration;
3939
use std::sync::{Mutex, Arc};
4040
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
4141
use std::{cmp, mem};
42-
use std::collections::{HashMap, HashSet};
42+
use std::collections::{HashMap, HashSet, VecDeque};
4343
use chain::keysinterface::InMemorySigner;
4444

4545
pub struct TestVecWriter(pub Vec<u8>);
@@ -518,6 +518,7 @@ pub struct TestChainSource {
518518
pub utxo_ret: Mutex<Result<TxOut, chain::AccessError>>,
519519
pub watched_txn: Mutex<HashSet<(Txid, Script)>>,
520520
pub watched_outputs: Mutex<HashSet<(OutPoint, Script)>>,
521+
expectations: Mutex<Option<VecDeque<OnRegisterOutput>>>,
521522
}
522523

523524
impl TestChainSource {
@@ -528,8 +529,17 @@ impl TestChainSource {
528529
utxo_ret: Mutex::new(Ok(TxOut { value: u64::max_value(), script_pubkey })),
529530
watched_txn: Mutex::new(HashSet::new()),
530531
watched_outputs: Mutex::new(HashSet::new()),
532+
expectations: Mutex::new(None),
531533
}
532534
}
535+
536+
/// Sets an expectation that [`chain::Filter::register_output`] is called.
537+
pub fn expect(&self, expectation: OnRegisterOutput) -> &Self {
538+
self.expectations.lock().unwrap()
539+
.get_or_insert_with(|| VecDeque::new())
540+
.push_back(expectation);
541+
self
542+
}
533543
}
534544

535545
impl chain::Access for TestChainSource {
@@ -548,7 +558,71 @@ impl chain::Filter for TestChainSource {
548558
}
549559

550560
fn register_output(&self, output: WatchedOutput) -> Option<(usize, Transaction)> {
561+
let dependent_tx = match &mut *self.expectations.lock().unwrap() {
562+
None => None,
563+
Some(expectations) => match expectations.pop_front() {
564+
None => {
565+
panic!("Unexpected register_output: {:?}",
566+
(output.outpoint, output.script_pubkey));
567+
},
568+
Some(expectation) => {
569+
assert_eq!(output.outpoint, expectation.outpoint());
570+
assert_eq!(&output.script_pubkey, expectation.script_pubkey());
571+
expectation.returns
572+
},
573+
},
574+
};
575+
551576
self.watched_outputs.lock().unwrap().insert((output.outpoint, output.script_pubkey));
552-
None
577+
dependent_tx
578+
}
579+
}
580+
581+
impl Drop for TestChainSource {
582+
fn drop(&mut self) {
583+
if std::thread::panicking() {
584+
return;
585+
}
586+
587+
if let Some(expectations) = &*self.expectations.lock().unwrap() {
588+
if !expectations.is_empty() {
589+
panic!("Unsatisfied expectations: {:?}", expectations);
590+
}
591+
}
592+
}
593+
}
594+
595+
/// An expectation that [`chain::Filter::register_output`] was called with a transaction output and
596+
/// returns an optional dependent transaction that spends the output in the same block.
597+
pub struct OnRegisterOutput {
598+
/// The transaction output to register.
599+
pub with: TxOutReference,
600+
601+
/// A dependent transaction spending the output along with its position in the block.
602+
pub returns: Option<(usize, Transaction)>,
603+
}
604+
605+
/// A transaction output as identified by an index into a transaction's output list.
606+
pub struct TxOutReference(pub Transaction, pub usize);
607+
608+
impl OnRegisterOutput {
609+
fn outpoint(&self) -> OutPoint {
610+
let txid = self.with.0.txid();
611+
let index = self.with.1 as u16;
612+
OutPoint { txid, index }
613+
}
614+
615+
fn script_pubkey(&self) -> &Script {
616+
let index = self.with.1;
617+
&self.with.0.output[index].script_pubkey
618+
}
619+
}
620+
621+
impl std::fmt::Debug for OnRegisterOutput {
622+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
623+
f.debug_struct("OnRegisterOutput")
624+
.field("outpoint", &self.outpoint())
625+
.field("script_pubkey", self.script_pubkey())
626+
.finish()
553627
}
554628
}

0 commit comments

Comments
 (0)