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:
parent
221d3b9485
commit
f8b3806720
4 changed files with 128 additions and 0 deletions
78
tvix/eval/src/value/arbitrary.rs
Normal file
78
tvix/eval/src/value/arbitrary.rs
Normal 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)
|
||||
]
|
||||
})
|
||||
}
|
|
@ -149,6 +149,35 @@ impl PartialEq for NixAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
mod arbitrary {
|
||||
use super::*;
|
||||
|
||||
use proptest::prelude::*;
|
||||
use proptest::prop_oneof;
|
||||
use proptest::strategy::{BoxedStrategy, Just, Strategy};
|
||||
|
||||
impl Arbitrary for NixAttrs {
|
||||
type Parameters = <BTreeMap<NixString, Value> as Arbitrary>::Parameters;
|
||||
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
prop_oneof![
|
||||
Just(Self(AttrsRep::Empty)),
|
||||
(
|
||||
any_with::<Value>(args.2.clone()),
|
||||
any_with::<Value>(args.2.clone())
|
||||
)
|
||||
.prop_map(|(name, value)| Self(AttrsRep::KV { name, value })),
|
||||
any_with::<BTreeMap<NixString, Value>>(args)
|
||||
.prop_map(|map| Self(AttrsRep::Map(map)))
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NixAttrs {
|
||||
/// Return an attribute set containing the merge of the two
|
||||
/// provided sets. Keys from the `other` set have precedence.
|
||||
|
|
|
@ -20,6 +20,25 @@ impl Display for NixList {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
mod arbitrary {
|
||||
use proptest::{
|
||||
prelude::{any_with, Arbitrary},
|
||||
strategy::{BoxedStrategy, Strategy},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for NixList {
|
||||
type Parameters = <Vec<Value> as Arbitrary>::Parameters;
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
any_with::<Vec<Value>>(args).prop_map(Self).boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NixList {
|
||||
pub fn concat(&self, other: &Self) -> Self {
|
||||
let mut lhs = self.clone();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
use std::rc::Rc;
|
||||
use std::{fmt::Display, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
mod arbitrary;
|
||||
mod attrs;
|
||||
mod builtin;
|
||||
mod function;
|
||||
|
|
Loading…
Reference in a new issue