···2525- [ ] Backend : More tests
2626 - [x] Lobby tests
2727 - [x] Game end test for actual return from loop
2828- - [ ] More transport crates tests
2828+ - [x] More transport crate tests
2929+ - [ ] Signaling is wrong, only kick everyone else on host leave if the lobby is open
2930 - [x] Organize signalling and seperate out more logic
3031 - [x] Signaling tests
3132 - [ ] Testing crate for integration testing?
+627-19
manhunt-transport/src/matchbox.rs
···11-use std::{collections::HashSet, pin::Pin, sync::Arc};
11+use std::{collections::HashSet, marker::PhantomData, pin::Pin, sync::Arc};
2233use anyhow::{Context, anyhow};
44use futures::{
55- SinkExt, StreamExt,
55+ SinkExt, Stream, StreamExt,
66 channel::mpsc::{UnboundedReceiver, UnboundedSender},
77};
88use log::{error, info};
···2222type MsgPair = (Option<Uuid>, TransportMessage);
2323type Queue = QueuePair<MsgPair>;
24242525-pub struct MatchboxTransport {
2525+pub struct MatchboxTransport<S: SocketImpl = WebRtcSocket> {
2626 my_id: Uuid,
2727 incoming: Queue,
2828 all_peers: Mutex<HashSet<Uuid>>,
2929 msg_sender: UnboundedSender<MatchboxMsgPair>,
3030 cancel_token: CancellationToken,
3131+ phantom: PhantomData<S>,
3132}
32333334type LoopFutRes = Result<(), SocketError>;
34353536type MatchboxMsgPair = (PeerId, Box<[u8]>);
3737+type MatchboxSender = UnboundedSender<MatchboxMsgPair>;
3838+type MatchboxReceiver = UnboundedReceiver<MatchboxMsgPair>;
3939+type MatchboxChannel = (MatchboxSender, MatchboxReceiver);
36403741fn map_socket_error(err: SocketError) -> anyhow::Error {
3842 match err {
···4145 }
4246}
43474444-impl MatchboxTransport {
4545- pub async fn new(join_code: &str, is_host: bool) -> Result<Arc<Self>> {
4646- let (itx, irx) = mpsc::channel(15);
4848+type FutPin<T> = Pin<Box<dyn Future<Output = T> + Send>>;
4949+type MessageLoopFuture = FutPin<LoopFutRes>;
5050+type PeerMsg = (PeerId, PeerState);
5151+5252+pub trait SocketImpl: Unpin + Send + Sync + Sized + Stream<Item = PeerMsg> {
5353+ fn new(room_url: &str) -> (Self, MessageLoopFuture);
5454+ fn get_id(&mut self) -> Option<PeerId>;
5555+ fn take_channel(&mut self) -> MatchboxChannel;
5656+}
5757+5858+impl SocketImpl for WebRtcSocket {
5959+ fn new(room_url: &str) -> (Self, MessageLoopFuture) {
6060+ Self::new_reliable(room_url)
6161+ }
6262+6363+ fn get_id(&mut self) -> Option<PeerId> {
6464+ self.id()
6565+ }
47666767+ fn take_channel(&mut self) -> MatchboxChannel {
6868+ self.take_channel(0).expect("Failed to get channel").split()
6969+ }
7070+}
7171+7272+impl<S: SocketImpl + 'static> MatchboxTransport<S> {
7373+ pub async fn new(join_code: &str, is_host: bool) -> Result<Arc<Self>> {
4874 let ws_url = server::room_url(join_code, is_host);
7575+ let (socket, loop_fut) = S::new(&ws_url);
7676+ Self::from_socket_and_loop_fut(socket, loop_fut).await
7777+ }
49785050- let (mut socket, mut loop_fut) = WebRtcSocket::new_reliable(&ws_url);
5151-5252- let (mtx, mrx) = socket
5353- .take_channel(0)
5454- .expect("Failed to get channel")
5555- .split();
7979+ async fn from_socket_and_loop_fut(
8080+ mut socket: S,
8181+ mut loop_fut: MessageLoopFuture,
8282+ ) -> Result<Arc<Self>> {
8383+ let (itx, irx) = mpsc::channel(15);
8484+ let (mtx, mrx) = socket.take_channel();
56855786 let res = loop {
5887 tokio::select! {
···79108 all_peers: Mutex::new(HashSet::with_capacity(5)),
80109 msg_sender: mtx.clone(),
81110 cancel_token: CancellationToken::new(),
111111+ phantom: PhantomData,
82112 });
8311384114 tokio::spawn({
···92122 }
93123 Err(why) => {
94124 drop(mrx);
9595- mtx.close_channel();
125125+ drop(mtx);
96126 drop(socket);
97127 Err(why)
98128 }
99129 }
100130 }
101131102102- async fn wait_for_id(socket: &mut WebRtcSocket) -> Option<Uuid> {
103103- if let Some(id) = socket.id() {
132132+ async fn wait_for_id(socket: &mut S) -> Option<Uuid> {
133133+ if let Some(id) = socket.get_id() {
104134 Some(id.0)
105135 } else {
106136 yield_now().await;
···118148119149 async fn main_loop(
120150 &self,
121121- mut socket: WebRtcSocket,
122122- loop_fut: Pin<Box<dyn Future<Output = LoopFutRes> + Send + 'static>>,
151151+ mut socket: S,
152152+ loop_fut: MessageLoopFuture,
123153 mut mrx: UnboundedReceiver<MatchboxMsgPair>,
124154 ) {
125155 tokio::pin!(loop_fut);
···164194 self.push_incoming(Some(self.my_id), msg).await;
165195166196 self.msg_sender.close_channel();
197197+ self.incoming.1.lock().await.close();
167198 drop(mrx);
168168- socket.try_update_peers().ok();
169199 drop(socket);
170200 if should_await {
171201 if let Err(why) = loop_fut.await {
···251281 buffer
252282 }
253283284284+ #[cfg(test)]
285285+ pub async fn force_recv_msg(&self) -> MsgPair {
286286+ self.incoming
287287+ .1
288288+ .lock()
289289+ .await
290290+ .recv()
291291+ .await
292292+ .expect("No messages")
293293+ }
294294+295295+ #[cfg(test)]
296296+ pub async fn assert_no_incoming(&self) {
297297+ assert!(self.incoming.1.lock().await.is_empty());
298298+ }
299299+254300 pub fn cancel(&self) {
255301 self.cancel_token.cancel();
256302 }
257303}
258304259259-impl Transport for MatchboxTransport {
305305+impl<S: SocketImpl + 'static> Transport for MatchboxTransport<S> {
260306 fn self_id(&self) -> Uuid {
261307 self.my_id
262308 }
···295341 Self::new(code, host).await
296342 }
297343}
344344+345345+#[cfg(test)]
346346+mod tests {
347347+348348+ use futures::{
349349+ channel::{mpsc, oneshot},
350350+ lock::Mutex as FutMutex,
351351+ };
352352+ use manhunt_logic::{GameEvent, LobbyMessage, PlayerProfile};
353353+ use matchbox_socket::SignalingError;
354354+355355+ use super::*;
356356+ use tokio::test;
357357+358358+ use std::{collections::HashMap, sync::Mutex as StdMutex, time::Duration};
359359+360360+ type PeerRx = UnboundedReceiver<PeerMsg>;
361361+ type PeerTx = UnboundedSender<PeerMsg>;
362362+ type IdHandle = Arc<StdMutex<Option<PeerId>>>;
363363+364364+ struct MockSocket {
365365+ peer_recv: PeerRx,
366366+ id: IdHandle,
367367+ channel: Option<MatchboxChannel>,
368368+ cancel: CancellationToken,
369369+ }
370370+371371+ impl MockSocket {
372372+ pub fn new(
373373+ peer_recv: PeerRx,
374374+ channel: MatchboxChannel,
375375+ id: IdHandle,
376376+ ) -> (
377377+ Self,
378378+ MessageLoopFuture,
379379+ oneshot::Sender<LoopFutRes>,
380380+ CancellationToken,
381381+ ) {
382382+ let (stop_tx, stop_rx) = oneshot::channel();
383383+ let cancel = CancellationToken::new();
384384+ let sock = Self {
385385+ peer_recv,
386386+ channel: Some(channel),
387387+ id,
388388+ cancel: cancel.clone(),
389389+ };
390390+391391+ let fut = Box::pin(async move { stop_rx.await.expect("Failed to recv") });
392392+393393+ (sock, fut, stop_tx, cancel)
394394+ }
395395+ }
396396+397397+ impl Drop for MockSocket {
398398+ fn drop(&mut self) {
399399+ self.cancel.cancel();
400400+ }
401401+ }
402402+403403+ impl Stream for MockSocket {
404404+ type Item = (PeerId, PeerState);
405405+406406+ fn poll_next(
407407+ self: Pin<&mut Self>,
408408+ cx: &mut std::task::Context<'_>,
409409+ ) -> std::task::Poll<Option<Self::Item>> {
410410+ let mut peer_state_rx = Pin::new(&mut self.get_mut().peer_recv);
411411+ peer_state_rx.as_mut().poll_next(cx)
412412+ }
413413+ }
414414+415415+ impl SocketImpl for MockSocket {
416416+ fn new(_room_url: &str) -> (Self, MessageLoopFuture) {
417417+ unreachable!("Tests should use [MatchboxTransport::from_socket_and_loop_fut]")
418418+ }
419419+420420+ fn get_id(&mut self) -> Option<PeerId> {
421421+ *self.id.lock().unwrap()
422422+ }
423423+424424+ fn take_channel(&mut self) -> (MatchboxSender, MatchboxReceiver) {
425425+ self.channel.take().expect("Channel already taken")
426426+ }
427427+ }
428428+429429+ type MatchboxTransport = super::MatchboxTransport<MockSocket>;
430430+431431+ struct WaitingPeer {
432432+ incoming: MatchboxSender,
433433+ outgoing: MatchboxReceiver,
434434+ peer_tx: PeerTx,
435435+ intended_id: PeerId,
436436+ id_handle: IdHandle,
437437+ disconnect: oneshot::Sender<LoopFutRes>,
438438+ client_cancel: CancellationToken,
439439+ }
440440+441441+ #[derive(Default, Debug)]
442442+ struct MockSignaling {
443443+ peers: HashMap<
444444+ PeerId,
445445+ (
446446+ PeerTx,
447447+ oneshot::Sender<LoopFutRes>,
448448+ CancellationToken,
449449+ CancellationToken,
450450+ ),
451451+ >,
452452+ senders: Arc<FutMutex<HashMap<PeerId, MatchboxSender>>>,
453453+ }
454454+455455+ impl MockSignaling {
456456+ fn new() -> Self {
457457+ tokio::time::pause();
458458+ Self::default()
459459+ }
460460+461461+ fn client_connect(
462462+ &self,
463463+ id: Uuid,
464464+ ) -> (
465465+ WaitingPeer,
466466+ FutPin<Result<Arc<MatchboxTransport>, anyhow::Error>>,
467467+ ) {
468468+ let (itx, irx) = mpsc::unbounded();
469469+ let (otx, orx) = mpsc::unbounded();
470470+ let (peer_tx, peer_rx) = mpsc::unbounded();
471471+ let id_handle = Arc::new(StdMutex::new(None));
472472+473473+ let (sock, fut, disconnect, cancel) =
474474+ MockSocket::new(peer_rx, (otx, irx), id_handle.clone());
475475+476476+ let transport_fut = Box::pin(MatchboxTransport::from_socket_and_loop_fut(sock, fut));
477477+478478+ let peer = WaitingPeer {
479479+ incoming: itx,
480480+ outgoing: orx,
481481+ peer_tx,
482482+ intended_id: PeerId(id),
483483+ id_handle,
484484+ disconnect,
485485+ client_cancel: cancel,
486486+ };
487487+488488+ (peer, transport_fut)
489489+ }
490490+491491+ async fn broadcast_peer_join(&mut self, source: PeerId) {
492492+ let (source_sender, _, _, _) = self.peers.get(&source).expect("Source not in peers");
493493+ let mut source_sender = source_sender.clone();
494494+ let peers = self.peers.iter_mut().filter(|(k, _)| **k != source);
495495+ for (id, (peer_tx, _, _, _)) in peers {
496496+ peer_tx
497497+ .send((source, PeerState::Connected))
498498+ .await
499499+ .expect("Failed to send");
500500+ source_sender
501501+ .send((*id, PeerState::Connected))
502502+ .await
503503+ .expect("Failed to send");
504504+ }
505505+ }
506506+507507+ async fn broadcast_peer_leave(&mut self, source: PeerId) {
508508+ let peers = self.peers.iter_mut().filter(|(k, _)| **k != source);
509509+ for (_, (peer_tx, _, _, _)) in peers {
510510+ peer_tx
511511+ .send((source, PeerState::Disconnected))
512512+ .await
513513+ .expect("Failed to send");
514514+ }
515515+ }
516516+517517+ /// Assign an ID to a MockSocket and set it so the future resolves
518518+ async fn assign_id(&mut self, waiting: WaitingPeer) {
519519+ let WaitingPeer {
520520+ id_handle,
521521+ intended_id,
522522+ incoming,
523523+ mut outgoing,
524524+ peer_tx,
525525+ disconnect,
526526+ client_cancel,
527527+ } = waiting;
528528+529529+ let cancel = CancellationToken::new();
530530+531531+ *id_handle.lock().unwrap() = Some(intended_id);
532532+ self.peers.insert(
533533+ intended_id,
534534+ (peer_tx, disconnect, cancel.clone(), client_cancel),
535535+ );
536536+ self.senders.lock().await.insert(intended_id, incoming);
537537+ self.broadcast_peer_join(intended_id).await;
538538+539539+ let senders = self.senders.clone();
540540+541541+ tokio::spawn(async move {
542542+ let id = intended_id;
543543+ loop {
544544+ tokio::select! {
545545+ biased;
546546+547547+ _ = cancel.cancelled() => { break; }
548548+549549+ Some((peer, packet)) = outgoing.next() => {
550550+ let mut senders = senders.lock().await;
551551+ let sender = senders.get_mut(&peer).expect("Failed to find peer");
552552+ sender.send((id, packet)).await.expect("Failed to send");
553553+ }
554554+ }
555555+ }
556556+ });
557557+ }
558558+559559+ async fn disconnect_peer(&mut self, id: Uuid, res: LoopFutRes) {
560560+ let (_, dc, cancel, _) = self.peers.remove(&PeerId(id)).expect("Peer not connected");
561561+ cancel.cancel();
562562+ dc.send(res).expect("Failed to send dc");
563563+ self.broadcast_peer_leave(PeerId(id)).await;
564564+ }
565565+566566+ async fn wait_for_socket_drop(&self, id: Uuid) {
567567+ let cancel = self.peers.get(&PeerId(id)).unwrap().3.clone();
568568+ cancel.cancelled().await;
569569+ }
570570+571571+ async fn wait_for_client_disconnected(&mut self, id: Uuid) {
572572+ self.wait_for_socket_drop(id).await;
573573+ self.disconnect_peer(id, Ok(())).await;
574574+ }
575575+576576+ async fn wait(&self) {
577577+ tokio::time::sleep(Duration::from_millis(1)).await;
578578+ }
579579+580580+ async fn quick_join(&mut self, id: Uuid) -> Arc<MatchboxTransport> {
581581+ let (wait, fut) = self.client_connect(id);
582582+ self.assign_id(wait).await;
583583+ fut.await.expect("Transport init failed")
584584+ }
585585+ }
586586+587587+ const fn id(x: u128) -> Uuid {
588588+ Uuid::from_u128(x)
589589+ }
590590+591591+ #[test]
592592+ async fn test_full_loop() {
593593+ let mut sig = MockSignaling::new();
594594+595595+ let (wait, fut) = sig.client_connect(id(1));
596596+597597+ sig.assign_id(wait).await;
598598+599599+ let transport = fut.await.expect("Tansport failed to initialize");
600600+601601+ assert_eq!(transport.my_id, id(1));
602602+603603+ transport.disconnect().await;
604604+605605+ sig.wait_for_client_disconnected(id(1)).await;
606606+ }
607607+608608+ #[test]
609609+ async fn test_dc_pre_assign() {
610610+ let sig = MockSignaling::new();
611611+612612+ let (wait, fut) = sig.client_connect(id(1));
613613+614614+ wait.disconnect.send(Ok(())).expect("Failed to send");
615615+616616+ let res = fut.await;
617617+618618+ assert!(res.is_err());
619619+ assert!(wait.incoming.is_closed());
620620+ assert!(wait.peer_tx.is_closed());
621621+ assert!(wait.client_cancel.is_cancelled());
622622+ }
623623+624624+ #[test]
625625+ async fn test_err_pre_assign() {
626626+ let sig = MockSignaling::new();
627627+628628+ let (wait, fut) = sig.client_connect(id(1));
629629+630630+ wait.disconnect
631631+ .send(Err(SocketError::Disconnected(
632632+ SignalingError::UnknownFormat,
633633+ )))
634634+ .expect("Failed to send");
635635+636636+ let res = fut.await;
637637+638638+ assert!(res.is_err());
639639+ assert!(wait.incoming.is_closed());
640640+ assert!(wait.peer_tx.is_closed());
641641+ assert!(wait.client_cancel.is_cancelled());
642642+ }
643643+644644+ #[test]
645645+ async fn test_graceful_disconnect() {
646646+ let mut sig = MockSignaling::new();
647647+648648+ let (wait, fut) = sig.client_connect(id(1));
649649+650650+ let can = wait.client_cancel.clone();
651651+652652+ sig.assign_id(wait).await;
653653+654654+ let transport = fut.await.expect("Transport init failed");
655655+656656+ sig.disconnect_peer(id(1), Ok(())).await;
657657+658658+ let (_, disconnected) = transport
659659+ .incoming
660660+ .1
661661+ .lock()
662662+ .await
663663+ .recv()
664664+ .await
665665+ .expect("Transport didnt send error");
666666+667667+ assert!(matches!(disconnected, TransportMessage::Disconnected));
668668+669669+ can.cancelled().await;
670670+671671+ assert!(transport.incoming.0.is_closed());
672672+ assert!(transport.msg_sender.is_closed());
673673+ }
674674+675675+ #[test]
676676+ async fn test_error_handle() {
677677+ let mut sig = MockSignaling::new();
678678+679679+ let (wait, fut) = sig.client_connect(id(1));
680680+681681+ let can = wait.client_cancel.clone();
682682+683683+ sig.assign_id(wait).await;
684684+685685+ let transport = fut.await.expect("Transport init failed");
686686+687687+ sig.disconnect_peer(
688688+ id(1),
689689+ Err(SocketError::Disconnected(SignalingError::UnknownFormat)),
690690+ )
691691+ .await;
692692+693693+ let (_, disconnected) = transport
694694+ .incoming
695695+ .1
696696+ .lock()
697697+ .await
698698+ .recv()
699699+ .await
700700+ .expect("Transport didnt send error");
701701+702702+ assert!(matches!(disconnected, TransportMessage::Error(_)));
703703+704704+ // Wait for the transport to drop the socket
705705+ can.cancelled().await;
706706+707707+ assert!(transport.incoming.0.is_closed());
708708+ assert!(transport.msg_sender.is_closed());
709709+ }
710710+711711+ #[test]
712712+ async fn test_message_passing() {
713713+ let mut sig = MockSignaling::new();
714714+715715+ let t1 = sig.quick_join(id(1)).await;
716716+ let t2 = sig.quick_join(id(2)).await;
717717+718718+ sig.wait().await;
719719+720720+ let (_, msg) = t1.force_recv_msg().await;
721721+ let (_, msg2) = t2.force_recv_msg().await;
722722+723723+ assert_eq!(t1.all_peers.lock().await.len(), 1);
724724+ assert_eq!(t2.all_peers.lock().await.len(), 1);
725725+ assert!(matches!(msg, TransportMessage::PeerConnect(pid) if pid == id(2)));
726726+ assert!(matches!(msg2, TransportMessage::PeerConnect(pid) if pid == id(1)));
727727+728728+ t1.send_transport_message(Some(id(2)), GameEvent::PlayerCaught(id(1)).into())
729729+ .await;
730730+731731+ sig.wait().await;
732732+733733+ let (_, msg) = t2.force_recv_msg().await;
734734+735735+ assert!(
736736+ matches!(msg, TransportMessage::Game(ge) if matches!(*ge, GameEvent::PlayerCaught(i) if i == id(1)))
737737+ );
738738+739739+ t2.send_transport_message(None, LobbyMessage::PlayerSwitch(id(2), true).into())
740740+ .await;
741741+742742+ sig.wait().await;
743743+744744+ let (_, msg) = t1.force_recv_msg().await;
745745+746746+ assert!(
747747+ matches!(msg, TransportMessage::Lobby(lm) if matches!(*lm, LobbyMessage::PlayerSwitch(i, b) if i == id(2) && b))
748748+ );
749749+ }
750750+751751+ #[test]
752752+ async fn test_msg_broadcast() {
753753+ let mut sig = MockSignaling::new();
754754+755755+ let t1 = sig.quick_join(id(1)).await;
756756+ let t2 = sig.quick_join(id(2)).await;
757757+ let t3 = sig.quick_join(id(3)).await;
758758+ let t4 = sig.quick_join(id(4)).await;
759759+760760+ sig.wait().await;
761761+762762+ let ts = [t1, t2, t3, t4];
763763+764764+ for t in ts.iter() {
765765+ assert_eq!(t.all_peers.lock().await.len(), ts.len() - 1);
766766+ // Eat the PeerConnected messages
767767+ for _ in 0..(ts.len() - 1) {
768768+ t.force_recv_msg().await;
769769+ }
770770+ }
771771+772772+ ts[0]
773773+ .send_transport_message(None, GameEvent::PlayerCaught(id(1)).into())
774774+ .await;
775775+776776+ sig.wait().await;
777777+778778+ ts[0].assert_no_incoming().await;
779779+780780+ for t in ts.iter().skip(1) {
781781+ let (pid, msg) = t.force_recv_msg().await;
782782+ assert_eq!(pid, Some(id(1)));
783783+ assert!(
784784+ matches!(msg, TransportMessage::Game(ge) if matches!(*ge, GameEvent::PlayerCaught(i) if i == id(1)))
785785+ );
786786+ }
787787+ }
788788+789789+ #[test]
790790+ async fn test_direct_msg() {
791791+ let mut sig = MockSignaling::new();
792792+793793+ let t1 = sig.quick_join(id(1)).await;
794794+ let t2 = sig.quick_join(id(2)).await;
795795+ let t3 = sig.quick_join(id(3)).await;
796796+797797+ sig.wait().await;
798798+799799+ let ts = [t1, t2, t3];
800800+801801+ for t in ts.iter() {
802802+ assert_eq!(t.all_peers.lock().await.len(), ts.len() - 1);
803803+ // Eat the PeerConnected messages
804804+ for _ in 0..(ts.len() - 1) {
805805+ t.force_recv_msg().await;
806806+ }
807807+ }
808808+809809+ ts[0]
810810+ .send_transport_message(Some(id(2)), GameEvent::PlayerCaught(id(1)).into())
811811+ .await;
812812+813813+ sig.wait().await;
814814+815815+ ts[0].assert_no_incoming().await;
816816+ ts[2].assert_no_incoming().await;
817817+818818+ let (pid, msg) = ts[1].force_recv_msg().await;
819819+ assert_eq!(pid, Some(id(1)));
820820+ assert!(
821821+ matches!(msg, TransportMessage::Game(ge) if matches!(*ge, GameEvent::PlayerCaught(i) if i == id(1)))
822822+ );
823823+ }
824824+825825+ #[test]
826826+ async fn test_multiple_disconnect() {
827827+ let mut sig = MockSignaling::new();
828828+829829+ let t1 = sig.quick_join(id(1)).await;
830830+ let t2 = sig.quick_join(id(2)).await;
831831+ let t3 = sig.quick_join(id(3)).await;
832832+833833+ sig.wait().await;
834834+835835+ let ts = [t1, t2, t3];
836836+837837+ for t in ts.iter() {
838838+ assert_eq!(t.all_peers.lock().await.len(), ts.len() - 1);
839839+ // Eat the PeerConnected messages
840840+ for _ in 0..(ts.len() - 1) {
841841+ t.force_recv_msg().await;
842842+ }
843843+ }
844844+845845+ ts[0].disconnect().await;
846846+847847+ sig.wait_for_client_disconnected(id(1)).await;
848848+849849+ sig.wait().await;
850850+851851+ for t in ts.iter().skip(1) {
852852+ let (_, msg) = t.force_recv_msg().await;
853853+854854+ let all = t.all_peers.lock().await;
855855+ assert!(!all.contains(&id(1)));
856856+ assert!(matches!(msg, TransportMessage::PeerDisconnect(i) if i == id(1)));
857857+ }
858858+ }
859859+860860+ #[test]
861861+ async fn test_big_message() {
862862+ // Just a random string that's bigger than the max packet size
863863+ let pfp = "vbnsj".repeat(65560);
864864+ let pfp2 = pfp.clone();
865865+866866+ let mut sig = MockSignaling::new();
867867+868868+ let t1 = sig.quick_join(id(1)).await;
869869+ let t2 = sig.quick_join(id(2)).await;
870870+871871+ sig.wait().await;
872872+873873+ t1.force_recv_msg().await;
874874+ t2.force_recv_msg().await;
875875+876876+ t1.send_transport_message(
877877+ Some(id(2)),
878878+ LobbyMessage::PlayerSync(
879879+ id(1),
880880+ PlayerProfile {
881881+ display_name: "asdf".to_string(),
882882+ pfp_base64: Some(pfp2),
883883+ },
884884+ )
885885+ .into(),
886886+ )
887887+ .await;
888888+889889+ sig.wait().await;
890890+891891+ let (_, msg) = t2.force_recv_msg().await;
892892+893893+ if let TransportMessage::Lobby(le) = msg {
894894+ if let LobbyMessage::PlayerSync(i, p) = *le {
895895+ assert_eq!(i, id(1));
896896+ assert_eq!(p.display_name, "asdf".to_string());
897897+ assert_eq!(p.pfp_base64, Some(pfp));
898898+ } else {
899899+ panic!("Incorrect lobby message");
900900+ }
901901+ } else {
902902+ panic!("Incorrect message");
903903+ }
904904+ }
905905+}