use minicbor::{Encode, Decode};
use minicbor_io::{AsyncReader, AsyncWriter, Error};
use quickcheck::{Arbitrary, Gen};
use rand::Rng;
use std::io;
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
struct Record {
#[n(0)] firstname: String,
#[n(1)] lastname: String,
#[n(2)] birthday: u32,
#[n(3)] addresses: Vec
}
#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
struct RecordView<'a> {
#[b(0)] firstname: &'a str,
#[b(1)] lastname: &'a str,
#[n(2)] birthday: u32,
#[b(3)] addresses: Vec>
}
#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
struct Address {
#[n(0)] street: String,
#[n(1)] houseno: String,
#[n(2)] postcode: u32,
#[n(3)] city: String,
#[n(4)] country: String
}
#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
struct AddressView<'a> {
#[b(0)] street: &'a str,
#[b(1)] houseno: &'a str,
#[n(2)] postcode: u32,
#[b(3)] city: &'a str,
#[b(4)] country: &'a str
}
impl Arbitrary for Record {
fn arbitrary(g: &mut Gen) -> Self {
Record {
firstname: Arbitrary::arbitrary(g),
lastname: Arbitrary::arbitrary(g),
birthday: Arbitrary::arbitrary(g),
addresses: Arbitrary::arbitrary(g)
}
}
}
impl Arbitrary for Address {
fn arbitrary(g: &mut Gen) -> Self {
Address {
street: Arbitrary::arbitrary(g),
houseno: Arbitrary::arbitrary(g),
postcode: Arbitrary::arbitrary(g),
city: Arbitrary::arbitrary(g),
country: Arbitrary::arbitrary(g)
}
}
}
/// Write `Record`s, read `RecordView`s and assert their structural equality.
#[tokio::test]
async fn read_write_identity() {
let (addr, server) = server().await.unwrap();
let join = tokio::spawn(echo::(server));
let mut gen = Gen::new(20);
let mut rng = rand::thread_rng();
let rounds = rng.gen_range(10 .. 30);
for n in 0u8 .. rounds {
let mut client = TcpStream::connect(addr).await.unwrap();
let (reader, writer) = client.split();
let mut reader = AsyncReader::new(reader.compat());
let mut writer = AsyncWriter::new(writer.compat_write());
for _ in 0u8 .. rng.gen_range(1 .. 50) {
let a = Record::arbitrary(&mut gen);
writer.write(Command::Value(&a)).await.unwrap();
let b: RecordView<'_> = reader.read().await.unwrap().unwrap();
assert_eq!(a.firstname, b.firstname);
assert_eq!(a.lastname, b.lastname);
assert_eq!(a.birthday, b.birthday);
assert_eq!(a.addresses.len(), b.addresses.len());
for (a, b) in a.addresses.iter().zip(b.addresses.iter()) {
assert_eq!(a.street, b.street);
assert_eq!(a.houseno, b.houseno);
assert_eq!(a.postcode, b.postcode);
assert_eq!(a.city, b.city);
assert_eq!(a.country, b.country);
}
}
if n == rounds - 1 {
writer.write(Command::::Stop).await.unwrap();
}
}
join.await.unwrap().unwrap()
}
#[derive(Debug, Encode, Decode)]
enum Command {
#[n(0)] Stop,
#[n(1)] Value(#[n(0)] T)
}
/// Bind a server to a random port.
async fn server() -> io::Result<(SocketAddr, TcpListener)> {
let l = TcpListener::bind("127.0.0.1:0").await?;
let a = l.local_addr()?;
Ok((a, l))
}
/// For each connection, read the `Command` and if a
/// `Command::Value`, send back the value.
async fn echo(l: TcpListener) -> Result<(), Error>
where
T: Encode<()> + for<'a> Decode<'a, ()>
{
while let Ok((mut s, _)) = l.accept().await {
let (r, w) = s.split();
let mut r = AsyncReader::new(r.compat());
let mut w = AsyncWriter::new(w.compat_write());
loop {
match r.read().await? {
None => break,
Some(Command::::Stop) => return Ok(()),
Some(Command::::Value(v)) => { w.write(v).await?; }
}
}
}
Ok(())
}