test(tvix/eval): impl Arbitrary for Value

Impl Arbitrary for Value (and NixAttrs and NixList) in the same way we
did for NixString. Value currently only generates non-"internal"
values (no thunks, AttrNotFound, etc.) and can't generate
functions (builtins or closures), because those'd require full
generation of tvix bytecode, which is a bit more work than I'd like to
do now - there's a `todo!` left in the code for a place where we could
allow opting-in to internal values and functions later.

Change-Id: I07a59e2b1d89cfaa912d4ecebd642caf4ddb040a
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6627
Autosubmit: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
Griffin Smith 2022-09-17 14:14:33 -04:00 committed by clbot
parent 221d3b9485
commit f8b3806720
4 changed files with 128 additions and 0 deletions

View file

@ -0,0 +1,78 @@
//! Support for configurable generation of arbitrary nix values
use proptest::{prelude::*, strategy::BoxedStrategy};
use std::{ffi::OsString, rc::Rc};
use super::{NixAttrs, NixList, NixString, Value};
#[derive(Clone)]
pub enum Parameters {
Strategy(BoxedStrategy<Value>),
Parameters {
generate_internal_values: bool,
generate_functions: bool,
generate_nested: bool,
},
}
impl Default for Parameters {
fn default() -> Self {
Self::Parameters {
generate_internal_values: false,
generate_functions: false,
generate_nested: true,
}
}
}
impl Arbitrary for Value {
type Parameters = Parameters;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
match args {
Parameters::Strategy(s) => s,
Parameters::Parameters {
generate_internal_values,
generate_functions,
generate_nested,
} => {
if generate_internal_values || generate_functions {
todo!("Generating internal values and functions not implemented yet")
} else if generate_nested {
non_internal_value().boxed()
} else {
leaf_value().boxed()
}
}
}
}
}
fn leaf_value() -> impl Strategy<Value = Value> {
use Value::*;
prop_oneof![
Just(Null),
any::<bool>().prop_map(Bool),
any::<i64>().prop_map(Integer),
any::<f64>().prop_map(Float),
any::<NixString>().prop_map(String),
any::<OsString>().prop_map(|s| Path(s.into())),
]
}
fn non_internal_value() -> impl Strategy<Value = Value> {
leaf_value().prop_recursive(10, 256, 10, |inner| {
prop_oneof![
any_with::<NixAttrs>((
Default::default(),
Default::default(),
Parameters::Strategy(inner.clone())
))
.prop_map(|a| Value::Attrs(Rc::new(a))),
any_with::<NixList>((Default::default(), Parameters::Strategy(inner)))
.prop_map(Value::List)
]
})
}