Skip to content

Commit 472db11

Browse files
author
DogLooksGood
committed
Improve testkit for faking random result
1 parent e3eac4e commit 472db11

File tree

5 files changed

+113
-132
lines changed

5 files changed

+113
-132
lines changed

api/src/effect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ impl Effect {
498498
Ok(())
499499
}
500500

501-
/// List the sub games to launch, deserialize the ,
501+
/// List the sub games to launch with their init account data.
502502
pub fn list_sub_game_data<D: BorshDeserialize>(&self) -> HandleResult<Vec<D>> {
503503
self.launch_sub_games.iter().map(|sub_game| {
504504
let data = D::try_from_slice(&sub_game.init_account.data)?;

core/src/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,12 @@ impl GameContext {
590590
&self.dispatch
591591
}
592592

593+
pub fn take_dispatch(&mut self) -> Option<DispatchEvent> {
594+
let mut dispatch = None;
595+
std::mem::swap(&mut dispatch, &mut self.dispatch);
596+
dispatch
597+
}
598+
593599
pub fn cancel_dispatch(&mut self) {
594600
self.dispatch = None;
595601
}

examples/raffle/src/lib.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct Player {
4040
/// The deposit, since the ticket is fixed in this game
4141
/// Every player has the same `balance`.
4242
pub balance: u64,
43-
/// Wether this player is eligible for raffle.
43+
/// whether this player is eligible for raffle.
4444
pub status: PlayerStatus,
4545
}
4646

@@ -224,6 +224,7 @@ impl Raffle {
224224

225225
#[cfg(test)]
226226
mod tests {
227+
use std::collections::HashMap;
227228
use race_test::prelude::*;
228229
use super::*;
229230

@@ -250,15 +251,21 @@ mod tests {
250251
// After enough players joined, we expect there's a waiting timeout event being dispatched
251252
assert_eq!(dispatch, Some(DispatchEvent::new(Event::WaitingTimeout, DRAW_TIMEOUT)));
252253

253-
ctx.handle_dispatch_until_no_events(vec![&mut alice, &mut bob, &mut tx])?;
254+
// We handle the dispatched event and all events after it, stop right before the SecretsReady
255+
let (secrets_ready, _) = ctx.handle_dispatch_until(vec![&mut alice, &mut bob, &mut tx], |e| matches!(e, Some(&Event::SecretsReady {..})))?;
254256

257+
let random_id = ctx.state().random_id;
258+
259+
// Before process the dispatching events, we set a faked random result
260+
// We select bob as the winner.
261+
ctx.set_random_result(random_id, HashMap::from([(0, alice.id().to_string())]));
262+
263+
ctx.handle_event_until_no_events(&secrets_ready.unwrap(), vec![&mut alice, &mut bob, &mut tx])?;
255264
{
256265
let state = ctx.state();
257-
258-
assert!(state.winner_player_id.is_some());
266+
assert_eq!(state.winner_player_id, Some(alice.id()));
259267
}
260268

261-
262269
Ok(())
263270
}
264271
}

test/src/context_helpers.rs

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,96 @@ impl<H: GameHandler> TestContext<H> {
4646
(Event::Join { players }, Event::Deposit{ deposits })
4747
}
4848

49+
/// Handle one event and return the effects.
4950
pub fn handle_event(&mut self, event: &Event) -> Result<EventEffects> {
5051
self.handler.handle_event(&mut self.context, event)
5152
}
5253

53-
pub fn handle_multiple_events(&mut self, events: &[Event]) -> Result<EventEffects> {
54-
let mut e = EventEffects::default();
55-
for event in events {
56-
e = self.handle_event(event)?;
54+
/// Handle one event and all events generated after it, until the next event satisfies
55+
/// the prediction. Return the next event and the last effects.
56+
pub fn handle_event_until(
57+
&mut self,
58+
event: &Event,
59+
mut clients: Vec<&mut TestClient>,
60+
event_pred: impl for<'a> Fn(Option<&'a Event>) -> bool,
61+
) -> Result<(Option<Event>, EventEffects)> {
62+
let mut events_queue = vec![event.clone()];
63+
let mut effects = EventEffects::default();
64+
65+
while !event_pred(events_queue.first()) {
66+
let this_event = &events_queue.remove(0);
67+
println!("* Handle event: {}", this_event);
68+
69+
effects = self.handler.handle_event(&mut self.context, this_event)?;
70+
71+
// Handle the following dispatched event and clients events.
72+
if let Some(dispatch) = self.take_dispatch() {
73+
if dispatch.timeout == self.context.get_timestamp() {
74+
events_queue.push(dispatch.event);
75+
}
76+
}
77+
78+
// Handle the following clients event.
79+
for client in clients.iter_mut() {
80+
let client_events = client.handle_updated_context(&mut self.context)?;
81+
events_queue.extend_from_slice(&client_events);
82+
if effects.checkpoint.is_some() {
83+
client.flush_secret_state();
84+
}
85+
}
5786
}
58-
Ok(e)
87+
88+
let next_event = if events_queue.is_empty() {
89+
None
90+
} else {
91+
Some(events_queue.remove(0))
92+
};
93+
Ok((next_event, effects))
5994
}
6095

61-
pub fn handle_dispatch_event(&mut self) -> Result<EventEffects> {
62-
self.handler.handle_dispatch_event(&mut self.context)
96+
/// Handle one event and all events generated after it, until there's no more event.
97+
/// Return the last effects.
98+
pub fn handle_event_until_no_events(
99+
&mut self,
100+
event: &Event,
101+
clients: Vec<&mut TestClient>,
102+
) -> Result<EventEffects> {
103+
let (_, effects) = self.handle_event_until(&event, clients, |e|{ e.is_none() })?;
104+
Ok(effects)
63105
}
64106

65-
pub fn handle_until_no_events(&mut self, clients: Vec<&mut TestClient>) -> Result<EventEffects> {
66-
self.handler.handle_until_no_events(&mut self.context, clients)
107+
/// Like `handle_event` but pass in the current dispatched event.
108+
pub fn handle_dispatch(&mut self) -> Result<EventEffects> {
109+
let event = &self.take_dispatch().expect("No dispatch event").event;
110+
self.handle_event(event)
67111
}
68112

113+
/// Like `handle_event_until_no_events` but start with the current dispatched event.
69114
pub fn handle_dispatch_until_no_events(
70115
&mut self,
71116
clients: Vec<&mut TestClient>,
72117
) -> Result<EventEffects> {
73-
self.handler
74-
.handle_dispatch_until_no_events(&mut self.context, clients)
118+
let event = &self.take_dispatch().expect("No dispatch event").event;
119+
self.handle_event_until_no_events(event, clients)
120+
}
121+
122+
/// Like `handle_event_until` but start with the current dispatched event.
123+
pub fn handle_dispatch_until(
124+
&mut self,
125+
clients: Vec<&mut TestClient>,
126+
event_pred: impl for<'a> Fn(Option<&'a Event>) -> bool,
127+
) -> Result<(Option<Event>, EventEffects)> {
128+
let event = &self.take_dispatch().expect("No dispatch event").event;
129+
self.handle_event_until(event, clients, event_pred)
130+
}
131+
132+
/// Handle multiple events and return the effects of the last event.
133+
pub fn handle_multiple_events(&mut self, events: &[Event]) -> Result<EventEffects> {
134+
let mut e = EventEffects::default();
135+
for event in events {
136+
e = self.handle_event(event)?;
137+
}
138+
Ok(e)
75139
}
76140

77141
pub fn init_account(&self) -> Result<InitAccount> {
@@ -94,6 +158,14 @@ impl<H: GameHandler> TestContext<H> {
94158
self.context.get_random_state_mut(random_id)
95159
}
96160

161+
pub fn set_random_result(&mut self, random_id: usize, result: HashMap<usize, String>) {
162+
self.handler.set_random_result(random_id, result);
163+
}
164+
165+
pub fn take_dispatch(&mut self) -> Option<DispatchEvent> {
166+
self.context.take_dispatch()
167+
}
168+
97169
pub fn current_dispatch(&self) -> Option<DispatchEvent> {
98170
self.context.get_dispatch().clone()
99171
}

test/src/handler_helpers.rs

Lines changed: 11 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::mem::swap;
2+
use std::collections::HashMap;
23

34
use race_api::engine::GameHandler;
45
use race_api::event::Event;
6+
use race_api::types::RandomId;
57
use race_core::context::{EventEffects, GameContext};
68
use race_core::engine::general_handle_event;
79
use race_core::error::Result;
810
use race_encryptor::Encryptor;
911

10-
use crate::client_helpers::TestClient;
11-
1212
// Some event has special handling in event loop.
1313
fn patch_handle_event_effects(context: &mut GameContext, event_effects: &EventEffects) {
1414
if event_effects.start_game {
@@ -23,6 +23,7 @@ where
2323
H: GameHandler,
2424
{
2525
handler: H,
26+
random_results: HashMap<RandomId, HashMap<usize, String>>,
2627
}
2728

2829
impl<H: GameHandler> TestHandler<H> {
@@ -32,9 +33,10 @@ impl<H: GameHandler> TestHandler<H> {
3233
let mut effect = new_context.derive_effect(true);
3334
let handler = H::init_state(&mut effect, init_account)?;
3435
let event_effects = new_context.apply_effect(effect)?;
36+
let random_results = HashMap::default();
3537
patch_handle_event_effects(&mut new_context, &event_effects);
3638
swap(context, &mut new_context);
37-
Ok((Self { handler }, event_effects))
39+
Ok((Self { handler, random_results }, event_effects))
3840
}
3941

4042
pub fn handle_event(
@@ -46,125 +48,19 @@ impl<H: GameHandler> TestHandler<H> {
4648
let encryptor = Encryptor::default();
4749
general_handle_event(&mut new_context, event, &encryptor)?;
4850
let mut effect = new_context.derive_effect(false);
51+
// patch the fake random result if we have
52+
if !self.random_results.is_empty() {
53+
effect.revealed = self.random_results.clone();
54+
}
4955
self.handler.handle_event(&mut effect, event.to_owned())?;
5056
let event_effects = new_context.apply_effect(effect)?;
5157
patch_handle_event_effects(&mut new_context, &event_effects);
5258
swap(context, &mut new_context);
5359
Ok(event_effects)
5460
}
5561

56-
/// Find the event which is going to be disptached in the context, then process it.
57-
/// In real cases, the disptached event will be handled by an event loop.
58-
/// We use this function to simulate such cases, since we don't have an event loop in tests.
59-
pub fn handle_dispatch_event(&mut self, context: &mut GameContext) -> Result<EventEffects> {
60-
let evt = context
61-
.get_dispatch()
62-
.as_ref()
63-
.expect("No dispatch event")
64-
.event
65-
.clone();
66-
context.cancel_dispatch();
67-
println!("* Dispatch event: {}", evt);
68-
self.handle_event(context, &evt)
69-
}
70-
71-
pub fn handle_dispatch_until_no_events(
72-
&mut self,
73-
context: &mut GameContext,
74-
clients: Vec<&mut TestClient>,
75-
) -> Result<EventEffects> {
76-
let evt = context
77-
.get_dispatch()
78-
.as_ref()
79-
.expect("No dispatch event")
80-
.event
81-
.clone();
82-
context.cancel_dispatch();
83-
println!("* Dispatch event: {}", evt);
84-
self.handle_event_until_no_events(context, &evt, clients)
85-
}
86-
87-
// Handle both client events and dispatch event, until there's no more.
88-
pub fn handle_until_no_events(
89-
&mut self,
90-
context: &mut GameContext,
91-
mut clients: Vec<&mut TestClient>,
92-
) -> Result<EventEffects> {
93-
let mut evts: Vec<Event> = vec![];
94-
let mut event_effects = EventEffects::default();
95-
96-
loop {
97-
if let Some(ctx_evt) = context.get_dispatch() {
98-
if ctx_evt.timeout == context.get_timestamp() {
99-
evts.push(ctx_evt.event.clone());
100-
context.cancel_dispatch();
101-
}
102-
}
103-
104-
for c in clients.iter_mut() {
105-
let cli_evts = c.handle_updated_context(context)?;
106-
evts.extend_from_slice(&cli_evts);
107-
if event_effects.checkpoint.is_some() {
108-
c.flush_secret_state();
109-
}
110-
}
111-
112-
if let Some(evt) = evts.first() {
113-
event_effects = self.handle_event(context, evt)?;
114-
evts.remove(0);
115-
} else {
116-
break;
117-
}
118-
}
119-
Ok(event_effects)
120-
}
121-
122-
/// This fn keeps handling events of the following two types, until there is none:
123-
/// 1. Event dispatched from within the (updated) context: context.dispatch
124-
/// 2. Event dispatched by clients after they see the updated context
125-
pub fn handle_event_until_no_events(
126-
&mut self,
127-
context: &mut GameContext,
128-
event: &Event,
129-
mut clients: Vec<&mut TestClient>,
130-
) -> Result<EventEffects> {
131-
// 1. Process the `event'(arg) --> context updated
132-
// 2. context may dispatch --> take care those with timeout == current timestamp
133-
// 3. iter clients to syn with updated context --> a couple of events
134-
// 4. handle these client/trans events
135-
let mut evts: Vec<Event> = vec![event.clone()]; // keep handling events in this vec
136-
let mut event_effects = EventEffects::default();
137-
138-
while !evts.is_empty() {
139-
let evt = &evts[0];
140-
println!("* Received event: {}", evt);
141-
142-
event_effects = self.handle_event(context, evt)?;
143-
if evts.len() == 1 {
144-
evts.clear();
145-
} else {
146-
evts = evts.iter().skip(1).map(|e| e.clone()).collect();
147-
}
148-
if let Some(ctx_evt) = context.get_dispatch() {
149-
if ctx_evt.timeout == context.get_timestamp() {
150-
evts.push(ctx_evt.event.clone());
151-
context.cancel_dispatch();
152-
}
153-
}
154-
155-
for c in clients.iter_mut() {
156-
let cli_evts = c.handle_updated_context(context)?;
157-
evts.extend_from_slice(&cli_evts);
158-
if event_effects.checkpoint.is_some() {
159-
c.flush_secret_state();
160-
}
161-
}
162-
163-
if let Some(dispatch) = context.get_dispatch() {
164-
println!("* Context dispatch: {:?}", dispatch);
165-
}
166-
}
167-
Ok(event_effects)
62+
pub fn set_random_result(&mut self, random_id: RandomId, result: HashMap<usize, String>) {
63+
self.random_results.insert(random_id, result);
16864
}
16965

17066
pub fn state(&self) -> &H {

0 commit comments

Comments
 (0)