1 use proptest::{ |
|
2 arbitrary::{any, any_with, Arbitrary, StrategyFor}, |
|
3 strategy::{BoxedStrategy, Just, Map, Strategy}, |
|
4 test_runner::{Reason, TestRunner}, |
|
5 }; |
|
6 |
|
7 use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, ServerVar::*, TeamInfo, VoteType}; |
|
8 |
|
9 use super::messages::{HwProtocolMessage, HwProtocolMessage::*}; |
|
10 |
|
11 // Due to inability to define From between Options |
|
12 trait Into2<T>: Sized { |
|
13 fn into2(self) -> T; |
|
14 } |
|
15 impl<T> Into2<T> for T { |
|
16 fn into2(self) -> T { |
|
17 self |
|
18 } |
|
19 } |
|
20 impl Into2<Vec<String>> for Vec<Ascii> { |
|
21 fn into2(self) -> Vec<String> { |
|
22 self.into_iter().map(|x| x.0).collect() |
|
23 } |
|
24 } |
|
25 impl Into2<String> for Ascii { |
|
26 fn into2(self) -> String { |
|
27 self.0 |
|
28 } |
|
29 } |
|
30 impl Into2<Option<String>> for Option<Ascii> { |
|
31 fn into2(self) -> Option<String> { |
|
32 self.map(|x| x.0) |
|
33 } |
|
34 } |
|
35 |
|
36 macro_rules! proto_msg_case { |
|
37 ($val: ident()) => { |
|
38 Just($val) |
|
39 }; |
|
40 ($val: ident($arg: ty)) => { |
|
41 any::<$arg>().prop_map(|v| $val(v.into2())) |
|
42 }; |
|
43 ($val: ident($arg1: ty, $arg2: ty)) => { |
|
44 any::<($arg1, $arg2)>().prop_map(|v| $val(v.0.into2(), v.1.into2())) |
|
45 }; |
|
46 ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) => { |
|
47 any::<($arg1, $arg2, $arg3)>().prop_map(|v| $val(v.0.into2(), v.1.into2(), v.2.into2())) |
|
48 }; |
|
49 } |
|
50 |
|
51 macro_rules! proto_msg_match { |
|
52 ($var: expr, def = $default: expr, $($num: expr => $constr: ident $res: tt),*) => ( |
|
53 match $var { |
|
54 $($num => (proto_msg_case!($constr $res)).boxed()),*, |
|
55 _ => Just($default).boxed() |
|
56 } |
|
57 ) |
|
58 } |
|
59 |
|
60 /// Wrapper type for generating non-empty strings |
|
61 #[derive(Debug)] |
|
62 struct Ascii(String); |
|
63 |
|
64 impl Arbitrary for Ascii { |
|
65 type Parameters = <String as Arbitrary>::Parameters; |
|
66 |
|
67 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { |
|
68 "[a-zA-Z0-9]+".prop_map(Ascii).boxed() |
|
69 } |
|
70 |
|
71 type Strategy = BoxedStrategy<Ascii>; |
|
72 } |
|
73 |
|
74 impl Arbitrary for GameCfg { |
|
75 type Parameters = (); |
|
76 |
|
77 fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy { |
|
78 use crate::core::types::GameCfg::*; |
|
79 (0..10) |
|
80 .no_shrink() |
|
81 .prop_flat_map(|i| { |
|
82 proto_msg_match!(i, def = FeatureSize(0), |
|
83 0 => FeatureSize(u32), |
|
84 1 => MapType(Ascii), |
|
85 2 => MapGenerator(u32), |
|
86 3 => MazeSize(u32), |
|
87 4 => Seed(Ascii), |
|
88 5 => Template(u32), |
|
89 6 => Ammo(Ascii, Option<Ascii>), |
|
90 7 => Scheme(Ascii, Vec<Ascii>), |
|
91 8 => Script(Ascii), |
|
92 9 => Theme(Ascii), |
|
93 10 => DrawnMap(Ascii)) |
|
94 }) |
|
95 .boxed() |
|
96 } |
|
97 |
|
98 type Strategy = BoxedStrategy<GameCfg>; |
|
99 } |
|
100 |
|
101 impl Arbitrary for TeamInfo { |
|
102 type Parameters = (); |
|
103 |
|
104 fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy { |
|
105 ( |
|
106 "[a-z]+", |
|
107 0u8..127u8, |
|
108 "[a-z]+", |
|
109 "[a-z]+", |
|
110 "[a-z]+", |
|
111 "[a-z]+", |
|
112 0u8..127u8, |
|
113 ) |
|
114 .prop_map(|(name, color, grave, fort, voice_pack, flag, difficulty)| { |
|
115 fn hog(n: u8) -> HedgehogInfo { |
|
116 HedgehogInfo { |
|
117 name: format!("hog{}", n), |
|
118 hat: format!("hat{}", n), |
|
119 } |
|
120 } |
|
121 let hedgehogs = [ |
|
122 hog(1), |
|
123 hog(2), |
|
124 hog(3), |
|
125 hog(4), |
|
126 hog(5), |
|
127 hog(6), |
|
128 hog(7), |
|
129 hog(8), |
|
130 ]; |
|
131 TeamInfo { |
|
132 owner: String::new(), |
|
133 name, |
|
134 color, |
|
135 grave, |
|
136 fort, |
|
137 voice_pack, |
|
138 flag, |
|
139 difficulty, |
|
140 hedgehogs, |
|
141 hedgehogs_number: 0, |
|
142 } |
|
143 }) |
|
144 .boxed() |
|
145 } |
|
146 |
|
147 type Strategy = BoxedStrategy<TeamInfo>; |
|
148 } |
|
149 |
|
150 impl Arbitrary for ServerVar { |
|
151 type Parameters = (); |
|
152 |
|
153 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { |
|
154 (0..=2) |
|
155 .no_shrink() |
|
156 .prop_flat_map(|i| { |
|
157 proto_msg_match!(i, def = ServerVar::LatestProto(0), |
|
158 0 => MOTDNew(Ascii), |
|
159 1 => MOTDOld(Ascii), |
|
160 2 => LatestProto(u16) |
|
161 ) |
|
162 }) |
|
163 .boxed() |
|
164 } |
|
165 |
|
166 type Strategy = BoxedStrategy<ServerVar>; |
|
167 } |
|
168 |
|
169 impl Arbitrary for VoteType { |
|
170 type Parameters = (); |
|
171 |
|
172 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { |
|
173 use VoteType::*; |
|
174 (0..=4) |
|
175 .no_shrink() |
|
176 .prop_flat_map(|i| { |
|
177 proto_msg_match!(i, def = VoteType::Pause, |
|
178 0 => Kick(Ascii), |
|
179 1 => Map(Option<Ascii>), |
|
180 2 => Pause(), |
|
181 3 => NewSeed(), |
|
182 4 => HedgehogsPerTeam(u8) |
|
183 ) |
|
184 }) |
|
185 .boxed() |
|
186 } |
|
187 |
|
188 type Strategy = BoxedStrategy<VoteType>; |
|
189 } |
|
190 |
|
191 pub fn gen_proto_msg() -> BoxedStrategy<HwProtocolMessage> where { |
|
192 let res = (0..=55).no_shrink().prop_flat_map(|i| { |
|
193 proto_msg_match!(i, def = Ping, |
|
194 0 => Ping(), |
|
195 1 => Pong(), |
|
196 2 => Quit(Option<Ascii>), |
|
197 4 => Global(Ascii), |
|
198 5 => Watch(u32), |
|
199 6 => ToggleServerRegisteredOnly(), |
|
200 7 => SuperPower(), |
|
201 8 => Info(Ascii), |
|
202 9 => Nick(Ascii), |
|
203 10 => Proto(u16), |
|
204 11 => Password(Ascii, Ascii), |
|
205 12 => Checker(u16, Ascii, Ascii), |
|
206 13 => List(), |
|
207 14 => Chat(Ascii), |
|
208 15 => CreateRoom(Ascii, Option<Ascii>), |
|
209 16 => JoinRoom(Ascii, Option<Ascii>), |
|
210 17 => Follow(Ascii), |
|
211 18 => Rnd(Vec<Ascii>), |
|
212 19 => Kick(Ascii), |
|
213 20 => Ban(Ascii, Ascii, u32), |
|
214 21 => BanIp(Ascii, Ascii, u32), |
|
215 22 => BanNick(Ascii, Ascii, u32), |
|
216 23 => BanList(), |
|
217 24 => Unban(Ascii), |
|
218 25 => SetServerVar(ServerVar), |
|
219 26 => GetServerVar(), |
|
220 27 => RestartServer(), |
|
221 28 => Stats(), |
|
222 29 => Part(Option<Ascii>), |
|
223 30 => Cfg(GameCfg), |
|
224 31 => AddTeam(Box<TeamInfo>), |
|
225 32 => RemoveTeam(Ascii), |
|
226 33 => SetHedgehogsNumber(Ascii, u8), |
|
227 34 => SetTeamColor(Ascii, u8), |
|
228 35 => ToggleReady(), |
|
229 36 => StartGame(), |
|
230 37 => EngineMessage(Ascii), |
|
231 38 => RoundFinished(), |
|
232 39 => ToggleRestrictJoin(), |
|
233 40 => ToggleRestrictTeams(), |
|
234 41 => ToggleRegisteredOnly(), |
|
235 42 => RoomName(Ascii), |
|
236 43 => Delegate(Ascii), |
|
237 44 => TeamChat(Ascii), |
|
238 45 => MaxTeams(u8), |
|
239 46 => Fix(), |
|
240 47 => Unfix(), |
|
241 48 => Greeting(Option<Ascii>), |
|
242 49 => CallVote(Option<VoteType>), |
|
243 50 => Vote(bool), |
|
244 51 => ForceVote(bool), |
|
245 52 => Save(Ascii, Ascii), |
|
246 53 => Delete(Ascii), |
|
247 54 => SaveRoom(Ascii), |
|
248 55 => LoadRoom(Ascii) |
|
249 ) |
|
250 }); |
|
251 res.boxed() |
|
252 } |
|