Introduce a concrete Conn type for peer's websocket connection

This is mostly to set us up to test the rpc::Client's reconnect
logic.

There are multiple ways that the `rpc::Client` may establish
its websocket connection: (SSL in production, plain TCP during
local development, and using an in-memory connection for tests).
Now we can represent all of those connections using a common type.

Also, several long methods no longer need to be generic, which
is good for compile time.
This commit is contained in:
Max Brunsfeld 2021-09-08 17:49:07 -07:00
parent c3e29e0a2d
commit b6eac57f63
9 changed files with 196 additions and 228 deletions

View file

@ -10,7 +10,7 @@ use crate::{
AppState,
};
use anyhow::{anyhow, Result};
use gpui::{Entity, ModelHandle, MutableAppContext, TestAppContext};
use gpui::{AsyncAppContext, Entity, ModelHandle, MutableAppContext, TestAppContext};
use parking_lot::Mutex;
use postage::{mpsc, prelude::Stream as _};
use smol::channel;
@ -20,10 +20,7 @@ use std::{
sync::Arc,
};
use tempdir::TempDir;
use zrpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope};
#[cfg(feature = "test-support")]
pub use zrpc::test::Channel;
use zrpc::{proto, Conn, ConnectionId, Peer, Receipt, TypedEnvelope};
#[cfg(test)]
#[ctor::ctor]
@ -201,40 +198,64 @@ impl<T: Entity> Observer<T> {
pub struct FakeServer {
peer: Arc<Peer>,
incoming: mpsc::Receiver<Box<dyn proto::AnyTypedEnvelope>>,
connection_id: ConnectionId,
incoming: Mutex<Option<mpsc::Receiver<Box<dyn proto::AnyTypedEnvelope>>>>,
connection_id: Mutex<Option<ConnectionId>>,
}
impl FakeServer {
pub async fn for_client(user_id: u64, client: &Arc<Client>, cx: &TestAppContext) -> Self {
let (client_conn, server_conn) = zrpc::test::Channel::bidirectional();
let peer = Peer::new();
let (connection_id, io, incoming) = peer.add_connection(server_conn).await;
cx.background().spawn(io).detach();
pub async fn for_client(user_id: u64, client: &Arc<Client>, cx: &TestAppContext) -> Arc<Self> {
let result = Arc::new(Self {
peer: Peer::new(),
incoming: Default::default(),
connection_id: Default::default(),
});
let conn = result.connect(&cx.to_async()).await;
client
.set_connection(user_id, client_conn, &cx.to_async())
.set_connection(user_id, conn, &cx.to_async())
.await
.unwrap();
result
}
Self {
peer,
incoming,
connection_id,
}
pub async fn disconnect(&self) {
self.peer.disconnect(self.connection_id()).await;
self.connection_id.lock().take();
self.incoming.lock().take();
}
async fn connect(&self, cx: &AsyncAppContext) -> Conn {
let (client_conn, server_conn) = Conn::in_memory();
let (connection_id, io, incoming) = self.peer.add_connection(server_conn).await;
cx.background().spawn(io).detach();
*self.incoming.lock() = Some(incoming);
*self.connection_id.lock() = Some(connection_id);
client_conn
}
pub async fn send<T: proto::EnvelopedMessage>(&self, message: T) {
self.peer.send(self.connection_id, message).await.unwrap();
self.peer.send(self.connection_id(), message).await.unwrap();
}
pub async fn receive<M: proto::EnvelopedMessage>(&mut self) -> Result<TypedEnvelope<M>> {
pub async fn receive<M: proto::EnvelopedMessage>(&self) -> Result<TypedEnvelope<M>> {
let message = self
.incoming
.lock()
.as_mut()
.expect("not connected")
.recv()
.await
.ok_or_else(|| anyhow!("other half hung up"))?;
Ok(*message.into_any().downcast::<TypedEnvelope<M>>().unwrap())
let type_name = message.payload_type_name();
Ok(*message
.into_any()
.downcast::<TypedEnvelope<M>>()
.unwrap_or_else(|_| {
panic!(
"fake server received unexpected message type: {:?}",
type_name
);
}))
}
pub async fn respond<T: proto::RequestMessage>(
@ -244,4 +265,8 @@ impl FakeServer {
) {
self.peer.respond(receipt, response).await.unwrap()
}
fn connection_id(&self) -> ConnectionId {
self.connection_id.lock().expect("not connected")
}
}