Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
IT WORKS AND PASSES 100k TESTS
  • Loading branch information
kianenigma committed Aug 9, 2022
commit 0b5cd9f14d7138ef7462360f0eb95e8798e643fe
2 changes: 1 addition & 1 deletion frame/nomination-pools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ log = { version = "0.4.0", default-features = false }
[dev-dependencies]
pallet-balances = { version = "4.0.0-dev", path = "../balances" }
sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" }
rand = "0.8.5"
rand = { version = "0.8.5", features = ["small_rng"] }

[features]
runtime-benchmarks = []
Expand Down
49 changes: 30 additions & 19 deletions frame/nomination-pools/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4925,20 +4925,30 @@ mod fuzz_test {
use super::*;
use crate::pallet::{Call as PoolsCall, Event as PoolsEvents};
use frame_support::traits::UnfilteredDispatchable;
use rand::{seq::SliceRandom, thread_rng, Rng};
use rand::{rngs::SmallRng, seq::SliceRandom, thread_rng, Rng, SeedableRng};
use sp_runtime::{assert_eq_error_rate, Perquintill};

const ERA: BlockNumber = 1000;
const MAX_ED_MULTIPLE: Balance = 1000;
const MAX_ED_MULTIPLE: Balance = 10_000;
const MIN_ED_MULTIPLE: Balance = 10;

// not quite elegant, just to make it available in random_signed_origin.
const REWARD_AGENT_ACCOUNT: AccountId = 42;

/// Grab random accounts, either known ones, or new ones.
fn random_signed_origin<R: Rng>(rng: &mut R) -> (Origin, AccountId) {
let count = PoolMembers::<T>::count();
if rng.gen::<bool>() && count > 0 {
// take an existing account.
let skip = rng.gen_range(0..count as usize);
let acc = PoolMembers::<T>::iter_keys().skip(skip).take(1).next().unwrap();

// this is tricky: the account might be our reward agent, which we never want to be
// randomly chosen here. Try another one, or, if it is only our agent, return a random
// one nonetheless.
let candidate = PoolMembers::<T>::iter_keys().skip(skip).take(1).next().unwrap();
let acc =
if candidate == REWARD_AGENT_ACCOUNT { rng.gen::<AccountId>() } else { candidate };

(Origin::signed(acc), acc)
} else {
// create a new account
Expand Down Expand Up @@ -5043,17 +5053,22 @@ mod fuzz_test {
expected_reward: Balance,
}

// TODO: inject some slashes into the game.
impl RewardAgent {
fn new(who: AccountId) -> Self {
Self { who, ..Default::default() }
}

fn join(&mut self) {
if self.pool_id.is_some() {
return
}
let pool_id = LastPoolId::<T>::get();
let amount = 10 * ExistentialDeposit::get();
let origin = Origin::signed(self.who);
let _ = Balances::deposit_creating(&self.who, 10 * amount);
self.pool_id = Some(pool_id);
log::info!(target: "reward-agent", "🤖 reward agent joining in {} with {}", pool_id, amount);
assert_ok!(PoolsCall::join::<T> { amount, pool_id }.dispatch_bypass_filter(origin));
}

Expand All @@ -5070,24 +5085,21 @@ mod fuzz_test {
let post = Balances::free_balance(&42);

let income = post - pre;
log!(
info,
"CLAIM: actual: {}, expected: {}, {:?} ({:?})",
log::info!(
target: "reward-agent", "🤖 CLAIM: actual: {}, expected: {}",
income,
self.expected_reward,
System::events(),
BondedPool::<T>::get(self.pool_id.unwrap())
);
assert_eq!(income, self.expected_reward);
assert_eq_error_rate!(income, self.expected_reward, 10);
self.expected_reward = 0;
}
}

#[test]
fn fuzz_test() {
let mut reward_agent = RewardAgent::new(42);

sp_tracing::try_init_simple();
// let mut rng = SmallRng::from_seed([0u8; 32]);
let mut rng = thread_rng();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good follow up:

From experience I can tell you that it is a good idea to create a random seed, print it, and add the possibility to overide it via env variable.
Then you always have a different set of tests, but if it fails, you can reproduce by using the printed seed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excellent idea, I actually have this in my code as a commented code that: If you want reproducible output, do this and that, but using a seed and env var makes a whole lot more sense.

let mut ext = sp_io::TestExternalities::new_empty();
// NOTE: sadly events don't fulfill the requirements of hashmap or btreemap.
Expand All @@ -5105,7 +5117,8 @@ mod fuzz_test {
MinJoinBond::<T>::set(5 * ExistentialDeposit::get());
System::set_block_number(1);
});
ExistentialDeposit::set(10u128.pow(1u32));

ExistentialDeposit::set(10u128.pow(12u32));
BondingDuration::set(8);

loop {
Expand All @@ -5131,13 +5144,12 @@ mod fuzz_test {
);

// possibly join the reward_agent
if iteration == ERA / 2 {
log!(info, "reward agent joining in");
if iteration > ERA / 2 && BondedPools::<T>::count() > 0 {
reward_agent.join();
}
// and possibly roughly every era, trigger payout for the agent. Doing this more
// and possibly roughly every 4 era, trigger payout for the agent. Doing this more
// frequent is also harmless.
if rng.gen_range(0..ERA) == 0 {
if rng.gen_range(0..(4 * ERA)) == 0 {
reward_agent.claim_payout();
}

Expand Down Expand Up @@ -5182,13 +5194,12 @@ mod fuzz_test {
let member_points =
PoolMembers::<T>::get(reward_agent.who).map(|m| m.points).unwrap();
let agent_share = Perquintill::from_rational(member_points, all_points);
log!(
info,
"REWARD = amount = {:?}, ratio: {:?}, share {:?}, ({:?})",
log::info!(
target: "reward-agent",
"🤖 REWARD = amount = {:?}, ratio: {:?}, share {:?}",
amount,
agent_share,
agent_share * amount,
BondedPool::<T>::get(id).unwrap()
);
reward_agent.expected_reward += agent_share * amount;
}
Expand Down