# HG changeset patch # User unc0rr # Date 1625005133 -7200 # Node ID ee84e417d8d0258e9e8d609505554297426d78b9 # Parent c3971b38bbfa2f6d0339b2d5456447700edf0ddd Add parser and idempotention tests for server messages diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-network-protocol/src/messages.rs --- a/rust/hedgewars-network-protocol/src/messages.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-network-protocol/src/messages.rs Wed Jun 30 00:18:53 2021 +0200 @@ -107,9 +107,9 @@ ProtocolFlags::format('-', flags) } -#[derive(Debug)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum HwServerMessage { - Connected(u32), + Connected(String, u32), Redirect(u16), Ping, @@ -151,7 +151,6 @@ Notice(String), Warning(String), Error(String), - Unreachable, //Deprecated messages LegacyReady(bool, Vec), @@ -370,11 +369,7 @@ match self { Ping => msg!["PING"], Pong => msg!["PONG"], - Connected(protocol_version) => msg![ - "CONNECTED", - "Hedgewars server https://www.hedgewars.org/", - protocol_version - ], + Connected(message, protocol_version) => msg!["CONNECTED", message, protocol_version], Redirect(port) => msg!["REDIRECT", port], Bye(msg) => msg!["BYE", msg], Nick(nick) => msg!["NICK", nick], @@ -414,8 +409,6 @@ LegacyReady(is_ready, nicks) => { construct_message(&[if *is_ready { "READY" } else { "NOT_READY" }], &nicks) } - - _ => msg!["ERROR", "UNIMPLEMENTED"], } } } diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-network-protocol/src/parser.rs --- a/rust/hedgewars-network-protocol/src/parser.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-network-protocol/src/parser.rs Wed Jun 30 00:18:53 2021 +0200 @@ -23,7 +23,7 @@ str::{FromStr, Utf8Error}, }; -use crate::messages::{HwProtocolMessage, HwProtocolMessage::*}; +use crate::messages::{HwProtocolMessage, HwProtocolMessage::*, HwServerMessage}; use crate::types::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType}; #[derive(Debug, PartialEq)] @@ -503,3 +503,158 @@ end_of_message, )(input) } + +pub fn server_message(input: &[u8]) -> HwResult { + use HwServerMessage::*; + + fn single_arg_message<'a, T, F, G>( + name: &'a str, + parser: F, + constructor: G, + ) -> impl FnMut(&'a [u8]) -> HwResult<'a, HwServerMessage> + where + F: Fn(&[u8]) -> HwResult, + G: Fn(T) -> HwServerMessage, + { + map( + preceded(terminated(tag(name), newline), parser), + constructor, + ) + } + + fn list_message<'a, G>( + name: &'a str, + constructor: G, + ) -> impl FnMut(&'a [u8]) -> HwResult<'a, HwServerMessage> + where + G: Fn(Vec) -> HwServerMessage, + { + map( + preceded( + tag(name), + alt(( + map(peek(end_of_message), |_| None), + map(preceded(newline, separated_list0(newline, a_line)), Some), + )), + ), + move |values| constructor(values.unwrap_or_default()), + ) + } + + fn string_and_list_message<'a, G>( + name: &'a str, + constructor: G, + ) -> impl FnMut(&'a [u8]) -> HwResult<'a, HwServerMessage> + where + G: Fn(String, Vec) -> HwServerMessage, + { + preceded( + pair(tag(name), newline), + map( + pair( + a_line, + alt(( + map(peek(end_of_message), |_| None), + map(preceded(newline, separated_list0(newline, a_line)), Some), + )), + ), + move |(name, values)| constructor(name, values.unwrap_or_default()), + ), + ) + } + + fn message<'a>( + name: &'a str, + msg: HwServerMessage, + ) -> impl Fn(&'a [u8]) -> HwResult<'a, HwServerMessage> { + move |i| map(tag(name), |_| msg.clone())(i) + } + + delimited( + take_while(|c| c == b'\n'), + alt(( + alt(( + message("PING", Ping), + message("PONG", Pong), + message("LOGONPASSED", LogonPassed), + message("KICKED", Kicked), + message("RUN_GAME", RunGame), + message("ROUND_FINISHED", RoundFinished), + message("REPLAY_START", ReplayStart), + )), + alt(( + single_arg_message("REDIRECT", u16_line, Redirect), + single_arg_message("BYE", a_line, Bye), + single_arg_message("NICK", a_line, Nick), + single_arg_message("PROTO", u16_line, Proto), + single_arg_message("ASKPASSWORD", a_line, AskPassword), + single_arg_message("SERVER_AUTH", a_line, ServerAuth), + single_arg_message("ROOM\nDEL", a_line, RoomRemove), + single_arg_message("JOINING", a_line, Joining), + single_arg_message("REMOVE_TEAM", a_line, TeamRemove), + single_arg_message("TEAM_ACCEPTED", a_line, TeamAccepted), + single_arg_message("SERVER_MESSAGE", a_line, ServerMessage), + single_arg_message("NOTICE", a_line, Notice), + single_arg_message("WARNING", a_line, Warning), + single_arg_message("ERROR", a_line, Error), + )), + alt(( + preceded( + pair(tag("LOBBY:LEFT"), newline), + map(pair(terminated(a_line, newline), a_line), |(nick, msg)| { + LobbyLeft(nick, msg) + }), + ), + preceded( + pair(tag("CHAT"), newline), + map(pair(terminated(a_line, newline), a_line), |(nick, msg)| { + ChatMsg { nick, msg } + }), + ), + preceded( + pair(tag("TEAM_COLOR"), newline), + map( + pair(terminated(a_line, newline), u8_line), + |(name, color)| TeamColor(name, color), + ), + ), + preceded( + pair(tag("HH_NUM"), newline), + map( + pair(terminated(a_line, newline), u8_line), + |(name, count)| HedgehogsNumber(name, count), + ), + ), + preceded( + pair(tag("CONNECTED"), newline), + map( + pair(terminated(a_line, newline), u32_line), + |(msg, server_protocol_version)| Connected(msg, server_protocol_version), + ), + ), + preceded( + pair(tag("LEFT"), newline), + map(pair(terminated(a_line, newline), a_line), |(nick, msg)| { + RoomLeft(nick, msg) + }), + ), + )), + alt(( + string_and_list_message("CLIENT_FLAGS", ClientFlags), + string_and_list_message("ROOM\nUPD", RoomUpdated), + string_and_list_message("CFG", ConfigEntry), + )), + alt(( + list_message("LOBBY:JOINED", LobbyJoined), + list_message("ROOMS", Rooms), + list_message("ROOM\nADD", RoomAdd), + list_message("JOINED", RoomJoined), + list_message("ADD_TEAM", TeamAdd), + list_message("EM", ForwardEngineMessage), + list_message("INFO", Info), + list_message("SERVER_VARS", ServerVars), + )), + )), + end_of_message, + )(input) +} diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-network-protocol/tests/parser.rs --- a/rust/hedgewars-network-protocol/tests/parser.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-network-protocol/tests/parser.rs Wed Jun 30 00:18:53 2021 +0200 @@ -1,11 +1,13 @@ use hedgewars_network_protocol::{ - parser::message, + parser::HwProtocolError, + parser::{message, server_message}, types::GameCfg, - {messages::HwProtocolMessage::*, parser::HwProtocolError}, }; #[test] fn parse_test() { + use hedgewars_network_protocol::messages::HwProtocolMessage::*; + assert_eq!(message(b"PING\n\n"), Ok((&b""[..], Ping))); assert_eq!(message(b"START_GAME\n\n"), Ok((&b""[..], StartGame))); assert_eq!( @@ -52,3 +54,23 @@ Err(nom::Err::Error(HwProtocolError::new())) ); } + +#[test] +fn parse_server_messages_test() { + use hedgewars_network_protocol::messages::HwServerMessage::*; + + assert_eq!(server_message(b"PING\n\n"), Ok((&b""[..], Ping))); + + assert_eq!( + server_message(b"JOINING\nnoone\n\n"), + Ok((&b""[..], Joining("noone".to_string()))) + ); + + assert_eq!( + server_message(b"CLIENT_FLAGS\naaa\nA\nB\n\n"), + Ok(( + &b""[..], + ClientFlags("aaa".to_string(), vec!["A".to_string(), "B".to_string()]) + )) + ) +} diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-network-protocol/tests/test.rs --- a/rust/hedgewars-network-protocol/tests/test.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-network-protocol/tests/test.rs Wed Jun 30 00:18:53 2021 +0200 @@ -4,14 +4,16 @@ strategy::{BoxedStrategy, Just, Strategy}, }; -use hedgewars_network_protocol::messages::{HwProtocolMessage, HwProtocolMessage::*}; -use hedgewars_network_protocol::parser::message; +use hedgewars_network_protocol::messages::{HwProtocolMessage, HwServerMessage}; +use hedgewars_network_protocol::parser::{message, server_message}; use hedgewars_network_protocol::types::{GameCfg, ServerVar, TeamInfo, VoteType}; use hedgewars_network_protocol::types::testing::*; use hedgewars_network_protocol::{proto_msg_case, proto_msg_match}; pub fn gen_proto_msg() -> BoxedStrategy where { + use hedgewars_network_protocol::messages::HwProtocolMessage::*; + let res = (0..=55).no_shrink().prop_flat_map(|i| { proto_msg_match!(i, def = Ping, 0 => Ping(), @@ -74,10 +76,64 @@ res.boxed() } +pub fn gen_server_msg() -> BoxedStrategy where { + use hedgewars_network_protocol::messages::HwServerMessage::*; + + let res = (0..=55).no_shrink().prop_flat_map(|i| { + proto_msg_match!(i, def = Ping, + 0 => Connected(Ascii, u32), + 1 => Redirect(u16), + 2 => Ping(), + 3 => Pong(), + 4 => Bye(Ascii), + 5 => Nick(Ascii), + 6 => Proto(u16), + 7 => AskPassword(Ascii), + 8 => ServerAuth(Ascii), + 9 => LogonPassed(), + 10 => LobbyLeft(Ascii, Ascii), + 11 => LobbyJoined(Vec), + // 12 => ChatMsg { Ascii, Ascii }, + 13 => ClientFlags(Ascii, Vec), + 14 => Rooms(Vec), + 15 => RoomAdd(Vec), + 16=> RoomJoined(Vec), + 17 => RoomLeft(Ascii, Ascii), + 18 => RoomRemove(Ascii), + 19 => RoomUpdated(Ascii, Vec), + 20 => Joining(Ascii), + 21 => TeamAdd(Vec), + 22 => TeamRemove(Ascii), + 23 => TeamAccepted(Ascii), + 24 => TeamColor(Ascii, u8), + 25 => HedgehogsNumber(Ascii, u8), + 26 => ConfigEntry(Ascii, Vec), + 27 => Kicked(), + 28 => RunGame(), + 29 => ForwardEngineMessage(Vec), + 30 => RoundFinished(), + 31 => ReplayStart(), + 32 => Info(Vec), + 33 => ServerMessage(Ascii), + 34 => ServerVars(Vec), + 35 => Notice(Ascii), + 36 => Warning(Ascii), + 37 => Error(Ascii) + ) + }); + res.boxed() +} + proptest! { #[test] fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) { println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes()); assert_eq!(message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone()))) } + + #[test] + fn is_server_message_parser_composition_idempotent(ref msg in gen_server_msg()) { + println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes()); + assert_eq!(server_message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone()))) + } } diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-server/src/handlers.rs --- a/rust/hedgewars-server/src/handlers.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-server/src/handlers.rs Wed Jun 30 00:18:53 2021 +0200 @@ -401,7 +401,7 @@ .anteroom .add_client(client_id, encode(&salt), is_local); - response.add(HwServerMessage::Connected(utils::SERVER_VERSION).send_self()); + response.add(HwServerMessage::Connected(utils::SERVER_MESSAGE.to_owned(), utils::SERVER_VERSION).send_self()); } } diff -r c3971b38bbfa -r ee84e417d8d0 rust/hedgewars-server/src/utils.rs --- a/rust/hedgewars-server/src/utils.rs Sat Jun 26 00:13:28 2021 +0200 +++ b/rust/hedgewars-server/src/utils.rs Wed Jun 30 00:18:53 2021 +0200 @@ -3,6 +3,7 @@ use std::iter::Iterator; pub const SERVER_VERSION: u32 = 3; +pub const SERVER_MESSAGE: &str = &"Hedgewars server https://www.hedgewars.org/"; pub const SERVER_TOKEN: mio::Token = mio::Token(1_000_000_000); pub const SECURE_SERVER_TOKEN: mio::Token = mio::Token(1_000_000_001); pub const IO_TOKEN: mio::Token = mio::Token(1_000_000_003);