rust/hedgewars-network-protocol/src/tests/test.rs
changeset 15830 ea459da15b30
equal deleted inserted replaced
15829:d5e6c8c92d87 15830:ea459da15b30
       
     1 use crate::{
       
     2     messages::{HwProtocolMessage, HwServerMessage},
       
     3     parser::{message, server_message},
       
     4     types::ServerVar::*,
       
     5     types::*,
       
     6     types::{GameCfg, ServerVar, TeamInfo, VoteType},
       
     7 };
       
     8 
       
     9 use proptest::{
       
    10     arbitrary::{any, Arbitrary},
       
    11     proptest,
       
    12     strategy::{BoxedStrategy, Just, Strategy},
       
    13 };
       
    14 
       
    15 // Due to inability to define From between Options
       
    16 pub trait Into2<T>: Sized {
       
    17     fn into2(self) -> T;
       
    18 }
       
    19 impl<T> Into2<T> for T {
       
    20     fn into2(self) -> T {
       
    21         self
       
    22     }
       
    23 }
       
    24 impl Into2<Vec<String>> for Vec<Ascii> {
       
    25     fn into2(self) -> Vec<String> {
       
    26         self.into_iter().map(|x| x.0).collect()
       
    27     }
       
    28 }
       
    29 impl Into2<String> for Ascii {
       
    30     fn into2(self) -> String {
       
    31         self.0
       
    32     }
       
    33 }
       
    34 impl Into2<Option<String>> for Option<Ascii> {
       
    35     fn into2(self) -> Option<String> {
       
    36         self.map(|x| x.0)
       
    37     }
       
    38 }
       
    39 
       
    40 #[macro_export]
       
    41 macro_rules! proto_msg_case {
       
    42     ($val: ident()) => {
       
    43         Just($val)
       
    44     };
       
    45     ($val: ident($arg: ty)) => {
       
    46         any::<$arg>().prop_map(|v| $val(v.into2()))
       
    47     };
       
    48     ($val: ident($arg1: ty, $arg2: ty)) => {
       
    49         any::<($arg1, $arg2)>().prop_map(|v| $val(v.0.into2(), v.1.into2()))
       
    50     };
       
    51     ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) => {
       
    52         any::<($arg1, $arg2, $arg3)>().prop_map(|v| $val(v.0.into2(), v.1.into2(), v.2.into2()))
       
    53     };
       
    54 }
       
    55 
       
    56 macro_rules! proto_msg_match {
       
    57 ($var: expr, def = $default: expr, $($num: expr => $constr: ident $res: tt),*) => (
       
    58     match $var {
       
    59         $($num => (proto_msg_case!($constr $res)).boxed()),*,
       
    60         _ => Just($default).boxed()
       
    61     }
       
    62 )
       
    63 }
       
    64 
       
    65 /// Wrapper type for generating non-empty strings
       
    66 #[derive(Debug)]
       
    67 pub struct Ascii(String);
       
    68 
       
    69 impl Arbitrary for Ascii {
       
    70     type Parameters = <String as Arbitrary>::Parameters;
       
    71 
       
    72     fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
       
    73         "[a-zA-Z0-9]+".prop_map(Ascii).boxed()
       
    74     }
       
    75 
       
    76     type Strategy = BoxedStrategy<Ascii>;
       
    77 }
       
    78 
       
    79 impl Arbitrary for GameCfg {
       
    80     type Parameters = ();
       
    81 
       
    82     fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy {
       
    83         use crate::types::GameCfg::*;
       
    84         (0..10)
       
    85             .no_shrink()
       
    86             .prop_flat_map(|i| {
       
    87                 proto_msg_match!(i, def = FeatureSize(0),
       
    88         0 => FeatureSize(u32),
       
    89         1 => MapType(Ascii),
       
    90         2 => MapGenerator(u32),
       
    91         3 => MazeSize(u32),
       
    92         4 => Seed(Ascii),
       
    93         5 => Template(u32),
       
    94         6 => Ammo(Ascii, Option<Ascii>),
       
    95         7 => Scheme(Ascii, Vec<Ascii>),
       
    96         8 => Script(Ascii),
       
    97         9 => Theme(Ascii),
       
    98         10 => DrawnMap(Ascii))
       
    99             })
       
   100             .boxed()
       
   101     }
       
   102 
       
   103     type Strategy = BoxedStrategy<GameCfg>;
       
   104 }
       
   105 
       
   106 impl Arbitrary for TeamInfo {
       
   107     type Parameters = ();
       
   108 
       
   109     fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy {
       
   110         (
       
   111             "[a-z]+",
       
   112             0u8..127u8,
       
   113             "[a-z]+",
       
   114             "[a-z]+",
       
   115             "[a-z]+",
       
   116             "[a-z]+",
       
   117             0u8..127u8,
       
   118         )
       
   119             .prop_map(|(name, color, grave, fort, voice_pack, flag, difficulty)| {
       
   120                 fn hog(n: u8) -> HedgehogInfo {
       
   121                     HedgehogInfo {
       
   122                         name: format!("hog{}", n),
       
   123                         hat: format!("hat{}", n),
       
   124                     }
       
   125                 }
       
   126                 let hedgehogs = [
       
   127                     hog(1),
       
   128                     hog(2),
       
   129                     hog(3),
       
   130                     hog(4),
       
   131                     hog(5),
       
   132                     hog(6),
       
   133                     hog(7),
       
   134                     hog(8),
       
   135                 ];
       
   136                 TeamInfo {
       
   137                     owner: String::new(),
       
   138                     name,
       
   139                     color,
       
   140                     grave,
       
   141                     fort,
       
   142                     voice_pack,
       
   143                     flag,
       
   144                     difficulty,
       
   145                     hedgehogs,
       
   146                     hedgehogs_number: 0,
       
   147                 }
       
   148             })
       
   149             .boxed()
       
   150     }
       
   151 
       
   152     type Strategy = BoxedStrategy<TeamInfo>;
       
   153 }
       
   154 
       
   155 impl Arbitrary for ServerVar {
       
   156     type Parameters = ();
       
   157 
       
   158     fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
       
   159         (0..=2)
       
   160             .no_shrink()
       
   161             .prop_flat_map(|i| {
       
   162                 proto_msg_match!(i, def = ServerVar::LatestProto(0),
       
   163                     0 => MOTDNew(Ascii),
       
   164                     1 => MOTDOld(Ascii),
       
   165                     2 => LatestProto(u16)
       
   166                 )
       
   167             })
       
   168             .boxed()
       
   169     }
       
   170 
       
   171     type Strategy = BoxedStrategy<ServerVar>;
       
   172 }
       
   173 
       
   174 impl Arbitrary for VoteType {
       
   175     type Parameters = ();
       
   176 
       
   177     fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
       
   178         use VoteType::*;
       
   179         (0..=4)
       
   180             .no_shrink()
       
   181             .prop_flat_map(|i| {
       
   182                 proto_msg_match!(i, def = VoteType::Pause,
       
   183                     0 => Kick(Ascii),
       
   184                     1 => Map(Option<Ascii>),
       
   185                     2 => Pause(),
       
   186                     3 => NewSeed(),
       
   187                     4 => HedgehogsPerTeam(u8)
       
   188                 )
       
   189             })
       
   190             .boxed()
       
   191     }
       
   192 
       
   193     type Strategy = BoxedStrategy<VoteType>;
       
   194 }
       
   195 
       
   196 pub fn gen_proto_msg() -> BoxedStrategy<HwProtocolMessage> where {
       
   197     use HwProtocolMessage::*;
       
   198 
       
   199     let res = (0..=58).no_shrink().prop_flat_map(|i| {
       
   200         proto_msg_match!(i, def = Ping,
       
   201             0 => Ping(),
       
   202             1 => Pong(),
       
   203             2 => Quit(Option<Ascii>),
       
   204             4 => Global(Ascii),
       
   205             5 => Watch(u32),
       
   206             6 => ToggleServerRegisteredOnly(),
       
   207             7 => SuperPower(),
       
   208             8 => Info(Ascii),
       
   209             9 => Nick(Ascii),
       
   210             10 => Proto(u16),
       
   211             11 => Password(Ascii, Ascii),
       
   212             12 => Checker(u16, Ascii, Ascii),
       
   213             13 => List(),
       
   214             14 => Chat(Ascii),
       
   215             15 => CreateRoom(Ascii, Option<Ascii>),
       
   216             16 => JoinRoom(Ascii, Option<Ascii>),
       
   217             17 => Follow(Ascii),
       
   218             18 => Rnd(Vec<Ascii>),
       
   219             19 => Kick(Ascii),
       
   220             20 => Ban(Ascii, Ascii, u32),
       
   221             21 => BanIp(Ascii, Ascii, u32),
       
   222             22 => BanNick(Ascii, Ascii, u32),
       
   223             23 => BanList(),
       
   224             24 => Unban(Ascii),
       
   225             25 => SetServerVar(ServerVar),
       
   226             26 => GetServerVar(),
       
   227             27 => RestartServer(),
       
   228             28 => Stats(),
       
   229             29 => Part(Option<Ascii>),
       
   230             30 => Cfg(GameCfg),
       
   231             31 => AddTeam(Box<TeamInfo>),
       
   232             32 => RemoveTeam(Ascii),
       
   233             33 => SetHedgehogsNumber(Ascii, u8),
       
   234             34 => SetTeamColor(Ascii, u8),
       
   235             35 => ToggleReady(),
       
   236             36 => StartGame(),
       
   237             37 => EngineMessage(Ascii),
       
   238             38 => RoundFinished(),
       
   239             39 => ToggleRestrictJoin(),
       
   240             40 => ToggleRestrictTeams(),
       
   241             41 => ToggleRegisteredOnly(),
       
   242             42 => RoomName(Ascii),
       
   243             43 => Delegate(Ascii),
       
   244             44 => TeamChat(Ascii),
       
   245             45 => MaxTeams(u8),
       
   246             46 => Fix(),
       
   247             47 => Unfix(),
       
   248             48 => Greeting(Option<Ascii>),
       
   249             49 => CallVote(Option<VoteType>),
       
   250             50 => Vote(bool),
       
   251             51 => ForceVote(bool),
       
   252             52 => Save(Ascii, Ascii),
       
   253             53 => Delete(Ascii),
       
   254             54 => SaveRoom(Ascii),
       
   255             55 => LoadRoom(Ascii),
       
   256             56 => CheckerReady(),
       
   257             57 => CheckedOk(Vec<Ascii>),
       
   258             58 => CheckedFail(Ascii)
       
   259         )
       
   260     });
       
   261     res.boxed()
       
   262 }
       
   263 
       
   264 pub fn gen_server_msg() -> BoxedStrategy<HwServerMessage> where {
       
   265     use HwServerMessage::*;
       
   266 
       
   267     let res = (0..=38).no_shrink().prop_flat_map(|i| {
       
   268         proto_msg_match!(i, def = Ping,
       
   269                     0 => Connected(Ascii, u32),
       
   270                     1 => Redirect(u16),
       
   271                     2 => Ping(),
       
   272                     3 => Pong(),
       
   273                     4 => Bye(Ascii),
       
   274                     5 => Nick(Ascii),
       
   275                     6 => Proto(u16),
       
   276                     7 => AskPassword(Ascii),
       
   277                     8 => ServerAuth(Ascii),
       
   278                     9 => LogonPassed(),
       
   279                     10 => LobbyLeft(Ascii, Ascii),
       
   280                     11 => LobbyJoined(Vec<Ascii>),
       
   281         //            12 => ChatMsg { Ascii, Ascii },
       
   282                     13 => ClientFlags(Ascii, Vec<Ascii>),
       
   283                     14 => Rooms(Vec<Ascii>),
       
   284                     15 => RoomAdd(Vec<Ascii>),
       
   285                     16=> RoomJoined(Vec<Ascii>),
       
   286                     17 => RoomLeft(Ascii, Ascii),
       
   287                     18 => RoomRemove(Ascii),
       
   288                     19 => RoomUpdated(Ascii, Vec<Ascii>),
       
   289                     20 => Joining(Ascii),
       
   290                     21 => TeamAdd(Vec<Ascii>),
       
   291                     22 => TeamRemove(Ascii),
       
   292                     23 => TeamAccepted(Ascii),
       
   293                     24 => TeamColor(Ascii, u8),
       
   294                     25 => HedgehogsNumber(Ascii, u8),
       
   295                     26 => ConfigEntry(Ascii, Vec<Ascii>),
       
   296                     27 => Kicked(),
       
   297                     28 => RunGame(),
       
   298                     29 => ForwardEngineMessage(Vec<Ascii>),
       
   299                     30 => RoundFinished(),
       
   300                     31 => ReplayStart(),
       
   301                     32 => Info(Vec<Ascii>),
       
   302                     33 => ServerMessage(Ascii),
       
   303                     34 => ServerVars(Vec<Ascii>),
       
   304                     35 => Notice(Ascii),
       
   305                     36 => Warning(Ascii),
       
   306                     37 => Error(Ascii),
       
   307                     38 => Replay(Vec<Ascii>)
       
   308                 )
       
   309     });
       
   310     res.boxed()
       
   311 }
       
   312 
       
   313 proptest! {
       
   314     #[test]
       
   315     fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) {
       
   316         println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes());
       
   317         assert_eq!(message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone())))
       
   318     }
       
   319 
       
   320     #[test]
       
   321     fn is_server_message_parser_composition_idempotent(ref msg in gen_server_msg()) {
       
   322         println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes());
       
   323         assert_eq!(server_message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone())))
       
   324     }
       
   325 }