Skip to content

Commit

Permalink
refactor test using TraceRunner
Browse files Browse the repository at this point in the history
  • Loading branch information
rnbguy committed Nov 16, 2023
1 parent d0e2644 commit bcb5e41
Showing 1 changed file with 109 additions and 53 deletions.
162 changes: 109 additions & 53 deletions Code/itf/tests/votekeeper.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
use std::collections::HashMap;
use std::ops::Deref;
use std::path::PathBuf;

use rand::rngs::StdRng;
use rand::SeedableRng;

use malachite_common::Round;
use malachite_itf::votekeeper::State as ItfState;
use malachite_itf::votekeeper::Value as ItfValue;
use malachite_common::{Context, Round, Value};
use malachite_itf::votekeeper::{State as ItfState, Value as ItfValue};
use malachite_itf::TraceRunner;
use malachite_test::{Address, PrivateKey, TestContext, ValueId, Vote};
use malachite_vote::keeper::{Message, VoteKeeper};
use malachite_vote::Weight;

use itf::Itf;
use rstest::{fixture, rstest};

const ADDRESSES: [&str; 3] = ["alice", "bob", "john"];

// TODO: move to itf-rs repo
fn from_itf<T, U>(bigint: Itf<T>) -> Option<U>
fn from_itf<T, U>(bigint: &Itf<T>) -> Option<U>
where
U: TryFrom<T>,
T: Clone,
{
bigint.value().try_into().ok()
bigint.deref().clone().try_into().ok()
}

fn value_from_model(value: ItfValue) -> Option<ValueId> {
fn value_from_model(value: &ItfValue) -> Option<ValueId> {
match value.as_str() {
"nil" => None,
"proposal" => Some(0.into()),
Expand All @@ -33,85 +37,137 @@ fn value_from_model(value: ItfValue) -> Option<ValueId> {
}
}

#[fixture]
#[once]
fn model_address_map() -> HashMap<String, Address> {
let mut rng = StdRng::seed_from_u64(0x42);

// build mapping from model addresses to real addresses
let valid_model_addresses = ["alice", "bob", "john"];
valid_model_addresses
.iter()
.map(|&name| {
let pk = PrivateKey::generate(&mut rng).public_key();
(name.into(), Address::from_public_key(&pk))
})
.collect()
struct VoteKeeperRunner {
address_map: HashMap<String, Address>,
}

#[rstest]
fn test_itf(
#[files("tests/fixtures/votekeeper/*.json")] json_fixture: PathBuf,
model_address_map: &HashMap<String, Address>,
) {
println!("Parsing {json_fixture:?}");
impl TraceRunner for VoteKeeperRunner {
type ActualState = VoteKeeper<TestContext>;
type Result = Option<Message<<<TestContext as Context>::Value as Value>::Id>>;

let json = std::fs::read_to_string(&json_fixture).unwrap();
let trace = itf::trace_from_str::<ItfState>(&json).unwrap();

// Obtain the initial total_weight from the first state in the model.
let bookkeper = trace.states[0].value.bookkeeper.clone();
let total_weight: Weight = from_itf(bookkeper.total_weight).unwrap();
type ExpectedState = ItfState;
type Error = ();

let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(total_weight);

for state in &trace.states[1..] {
let state = state.clone().value;
fn init(
&mut self,
expected_state: &Self::ExpectedState,
) -> Result<Self::ActualState, Self::Error> {
// Obtain the initial total_weight from the first state in the model.
let total_weight: Weight = from_itf(&expected_state.bookkeeper.total_weight).unwrap();
Ok(VoteKeeper::new(total_weight))
}

fn step(
&mut self,
state: &mut Self::ActualState,
expected_state: &Self::ExpectedState,
) -> Result<Self::Result, Self::Error> {
// Build step to execute.
let (input_vote, weight) = state.weighted_vote.value();
let round = Round::new(from_itf(input_vote.round).unwrap());
let value = value_from_model(input_vote.value);
let address = model_address_map.get(input_vote.address.as_str()).unwrap();
let (input_vote, weight) = expected_state.weighted_vote.deref();
let round = Round::new(from_itf(&input_vote.round).unwrap());
let value = value_from_model(&input_vote.value);
let address = self.address_map.get(input_vote.address.as_str()).unwrap();
let vote = match input_vote.typ.as_str() {
"Prevote" => Vote::new_prevote(round, value, *address),
"Precommit" => Vote::new_precommit(round, value, *address),
_ => unreachable!(),
};
let weight: Weight = from_itf(weight).unwrap();
println!(
"🟢 step: vote={:?}, round={:?}, value={:?}, address={:?}, weight={:?}",
"🔵 step: vote={:?}, round={:?}, value={:?}, address={:?}, weight={:?}",
input_vote.typ, round, value, input_vote.address, weight
);

// Execute step.
let result = keeper.apply_vote(vote.clone(), weight);
Ok(state.apply_vote(vote.clone(), weight))
}

fn result_invariant(
&self,
_state: &Self::ActualState,
result: &Self::Result,
expected_state: &Self::ExpectedState,
) -> Result<bool, Self::Error> {
// Get expected result.
let model_result = state.last_emitted;
let expected_result = &expected_state.last_emitted;
println!(
"🟣 result: model={:?}({:?}), code={:?}",
model_result.name, model_result.value, result
expected_result.name, expected_result.value, result
);

// Check result against expected result.
match result {
Some(result) => match result {
Message::PolkaValue(value) => {
assert_eq!(model_result.name, "PolkaValue");
assert_eq!(value_from_model(model_result.value), Some(value));
assert_eq!(expected_result.name, "PolkaValue");
assert_eq!(
value_from_model(&expected_result.value).as_ref(),
Some(value)
);
}
Message::PrecommitValue(value) => {
assert_eq!(model_result.name, "PrecommitValue");
assert_eq!(value_from_model(model_result.value), Some(value));
assert_eq!(expected_result.name, "PrecommitValue");
assert_eq!(
value_from_model(&expected_result.value).as_ref(),
Some(value)
);
}
Message::SkipRound(round) => {
assert_eq!(model_result.name, "SkipRound");
assert_eq!(Round::new(from_itf(model_result.round).unwrap()), round);
assert_eq!(expected_result.name, "SkipRound");
assert_eq!(
&Round::new(from_itf(&expected_result.round).unwrap()),
round
);
}
msg => assert_eq!(model_result.name, format!("{:?}", msg)),
msg => assert_eq!(expected_result.name, format!("{msg:?}")),
},
None => assert_eq!(model_result.name, "None"),
None => assert_eq!(expected_result.name, "None"),
}
Ok(true)
}

fn state_invariant(
&self,
_actual_state: &Self::ActualState,
_expected_state: &Self::ExpectedState,
) -> Result<bool, Self::Error> {
Ok(true)
}
}

#[fixture]
fn vote_keeper_runner() -> VoteKeeperRunner {
let mut rng = StdRng::seed_from_u64(0x42);

// build mapping from model addresses to real addresses
VoteKeeperRunner {
address_map: ADDRESSES
.iter()
.map(|&name| {
let pk = PrivateKey::generate(&mut rng).public_key();
(name.into(), Address::from_public_key(&pk))
})
.collect(),
}
}

#[rstest]
fn test_itf(
#[files("tests/fixtures/votekeeper/*.json")] json_fixture: PathBuf,
mut vote_keeper_runner: VoteKeeperRunner,
) {
println!("Parsing {json_fixture:?}");

let json = std::fs::read_to_string(&json_fixture).unwrap();
let trace = itf::trace_from_str::<ItfState>(&json).unwrap();

vote_keeper_runner
.test(
trace
.states
.into_iter()
.map(|s| s.value)
.collect::<Vec<_>>()
.as_slice(),
)
.unwrap();
}

0 comments on commit bcb5e41

Please sign in to comment.