tvl-depot/tvix/nix-compat-derive-tests/tests/write_derive.rs
Brian Olsen b88579ade4 feat(tvix/nix-compat): Add nix serialization support
This change implements the serialization part that is needed to
implement the nix daemon protocol. Previously was add deserialization
and derivers for that and this then adds the other part of that equation
so that you can write types that can then be read using deserialization.

Change-Id: I2917de634980a93822a4f5a8ad38897b9ce16d89
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12729
Autosubmit: Brian Olsen <me@griff.name>
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
2024-11-04 20:02:54 +00:00

370 lines
8.5 KiB
Rust

use std::fmt;
use nix_compat::nix_daemon::ser::{
mock::{Builder, Error},
NixWrite as _,
};
use nix_compat_derive::NixSerialize;
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct UnitTest;
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct EmptyTupleTest();
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct StructTest {
first: u64,
second: String,
}
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct TupleTest(u64, String);
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct StructVersionTest {
test: u64,
#[nix(version = "20..")]
hello: String,
}
fn default_test() -> StructVersionTest {
StructVersionTest {
test: 89,
hello: String::from("klomp"),
}
}
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct TupleVersionTest(u64, #[nix(version = "25..")] String);
#[derive(Debug, PartialEq, Eq, NixSerialize)]
pub struct TupleVersionDefaultTest(u64, #[nix(version = "..25")] StructVersionTest);
#[tokio::test]
async fn write_unit() {
let mut mock = Builder::new().build();
mock.write_value(&UnitTest).await.unwrap();
}
#[tokio::test]
async fn write_empty_tuple() {
let mut mock = Builder::new().build();
mock.write_value(&EmptyTupleTest()).await.unwrap();
}
#[tokio::test]
async fn write_struct() {
let mut mock = Builder::new()
.write_number(89)
.write_slice(b"klomp")
.build();
mock.write_value(&StructTest {
first: 89,
second: String::from("klomp"),
})
.await
.unwrap();
}
#[tokio::test]
async fn write_tuple() {
let mut mock = Builder::new()
.write_number(89)
.write_slice(b"klomp")
.build();
mock.write_value(&TupleTest(89, String::from("klomp")))
.await
.unwrap();
}
#[tokio::test]
async fn write_struct_version() {
let mut mock = Builder::new()
.version((1, 20))
.write_number(89)
.write_slice(b"klomp")
.build();
mock.write_value(&default_test()).await.unwrap();
}
#[tokio::test]
async fn write_struct_without_version() {
let mut mock = Builder::new().version((1, 19)).write_number(89).build();
mock.write_value(&StructVersionTest {
test: 89,
hello: String::new(),
})
.await
.unwrap();
}
#[tokio::test]
async fn write_tuple_version() {
let mut mock = Builder::new()
.version((1, 26))
.write_number(89)
.write_slice(b"klomp")
.build();
mock.write_value(&TupleVersionTest(89, "klomp".into()))
.await
.unwrap();
}
#[tokio::test]
async fn write_tuple_without_version() {
let mut mock = Builder::new().version((1, 19)).write_number(89).build();
mock.write_value(&TupleVersionTest(89, String::new()))
.await
.unwrap();
}
#[tokio::test]
async fn write_complex_1() {
let mut mock = Builder::new()
.version((1, 19))
.write_number(999)
.write_number(666)
.build();
mock.write_value(&TupleVersionDefaultTest(
999,
StructVersionTest {
test: 666,
hello: String::new(),
},
))
.await
.unwrap();
}
#[tokio::test]
async fn write_complex_2() {
let mut mock = Builder::new()
.version((1, 20))
.write_number(999)
.write_number(666)
.write_slice(b"The quick brown \xF0\x9F\xA6\x8A jumps over 13 lazy \xF0\x9F\x90\xB6.")
.build();
mock.write_value(&TupleVersionDefaultTest(
999,
StructVersionTest {
test: 666,
hello: String::from("The quick brown 🦊 jumps over 13 lazy 🐶."),
},
))
.await
.unwrap();
}
#[tokio::test]
async fn write_complex_3() {
let mut mock = Builder::new().version((1, 25)).write_number(999).build();
mock.write_value(&TupleVersionDefaultTest(
999,
StructVersionTest {
test: 89,
hello: String::from("klomp"),
},
))
.await
.unwrap();
}
#[tokio::test]
async fn write_complex_4() {
let mut mock = Builder::new().version((1, 26)).write_number(999).build();
mock.write_value(&TupleVersionDefaultTest(
999,
StructVersionTest {
test: 89,
hello: String::from("klomp"),
},
))
.await
.unwrap();
}
#[derive(Debug, PartialEq, Eq, NixSerialize)]
#[nix(display)]
struct TestFromStr;
impl fmt::Display for TestFromStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "test")
}
}
#[tokio::test]
async fn write_display() {
let mut mock = Builder::new().write_display("test").build();
mock.write_value(&TestFromStr).await.unwrap();
}
#[derive(Debug, PartialEq, Eq, NixSerialize)]
#[nix(display = "TestFromStr2::display")]
struct TestFromStr2;
struct TestFromStrDisplay;
impl fmt::Display for TestFromStrDisplay {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "test")
}
}
impl TestFromStr2 {
fn display(&self) -> TestFromStrDisplay {
TestFromStrDisplay
}
}
#[tokio::test]
async fn write_display_path() {
let mut mock = Builder::new().write_display("test").build();
mock.write_value(&TestFromStr2).await.unwrap();
}
#[derive(Clone, Debug, PartialEq, Eq, NixSerialize)]
#[nix(try_into = "u64")]
struct TestTryFromU64(u64);
impl TryFrom<TestTryFromU64> for u64 {
type Error = u64;
fn try_from(value: TestTryFromU64) -> Result<Self, Self::Error> {
if value.0 != 42 {
Ok(value.0)
} else {
Err(value.0)
}
}
}
#[tokio::test]
async fn write_try_into_u64() {
let mut mock = Builder::new().write_number(666).build();
mock.write_value(&TestTryFromU64(666)).await.unwrap();
}
#[tokio::test]
async fn write_try_into_u64_invalid_data() {
let mut mock = Builder::new().build();
let err = mock.write_value(&TestTryFromU64(42)).await.unwrap_err();
assert_eq!(Error::UnsupportedData("42".into()), err);
}
#[derive(Clone, Debug, PartialEq, Eq, NixSerialize)]
#[nix(into = "u64")]
struct TestFromU64;
impl From<TestFromU64> for u64 {
fn from(_value: TestFromU64) -> u64 {
42
}
}
#[tokio::test]
async fn write_into_u64() {
let mut mock = Builder::new().write_number(42).build();
mock.write_value(&TestFromU64).await.unwrap();
}
#[derive(Debug, PartialEq, Eq, NixSerialize)]
enum TestEnum {
#[nix(version = "..=19")]
Pre20(TestFromU64, #[nix(version = "10..")] u64),
#[nix(version = "20..=29")]
Post20(StructVersionTest),
#[nix(version = "30..=39")]
Post30,
#[nix(version = "40..")]
Post40 {
msg: String,
#[nix(version = "45..")]
level: u64,
},
}
#[tokio::test]
async fn write_enum_9() {
let mut mock = Builder::new().version((1, 9)).write_number(42).build();
mock.write_value(&TestEnum::Pre20(TestFromU64, 666))
.await
.unwrap();
}
#[tokio::test]
async fn write_enum_19() {
let mut mock = Builder::new()
.version((1, 19))
.write_number(42)
.write_number(666)
.build();
mock.write_value(&TestEnum::Pre20(TestFromU64, 666))
.await
.unwrap();
}
#[tokio::test]
async fn write_enum_20() {
let mut mock = Builder::new()
.version((1, 20))
.write_number(666)
.write_slice(b"klomp")
.build();
mock.write_value(&TestEnum::Post20(StructVersionTest {
test: 666,
hello: "klomp".into(),
}))
.await
.unwrap();
}
#[tokio::test]
async fn write_enum_30() {
let mut mock = Builder::new().version((1, 30)).build();
mock.write_value(&TestEnum::Post30).await.unwrap();
}
#[tokio::test]
async fn write_enum_40() {
let mut mock = Builder::new()
.version((1, 40))
.write_slice(b"hello world")
.build();
mock.write_value(&TestEnum::Post40 {
msg: "hello world".into(),
level: 9001,
})
.await
.unwrap();
}
#[tokio::test]
async fn write_enum_45() {
let mut mock = Builder::new()
.version((1, 45))
.write_slice(b"hello world")
.write_number(9001)
.build();
mock.write_value(&TestEnum::Post40 {
msg: "hello world".into(),
level: 9001,
})
.await
.unwrap();
}
#[tokio::test]
async fn write_wrong_enum() {
let mut mock = Builder::new().version((1, 30)).build();
let err = mock
.write_value(&TestEnum::Post40 {
msg: "hello world".into(),
level: 9001,
})
.await
.unwrap_err();
assert_eq!(
err,
Error::InvalidEnum("Post40 is not valid for version 1.30".into())
)
}