about summary refs log tree commit diff
path: root/tvix
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2022-09-17T18·14-0400
committerclbot <clbot@tvl.fyi>2022-09-18T17·55+0000
commitf8b380672043e9b870c4303ce47205a1435de8ee (patch)
tree6132b8ccbf869634ff7e4ec7dca9667c3e495520 /tvix
parent221d3b9485a1d84fc4d9f06d864242d3c393d0ba (diff)
test(tvix/eval): impl Arbitrary for Value r/4902
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>
Diffstat (limited to 'tvix')
-rw-r--r--tvix/eval/src/value/arbitrary.rs78
-rw-r--r--tvix/eval/src/value/attrs.rs29
-rw-r--r--tvix/eval/src/value/list.rs19
-rw-r--r--tvix/eval/src/value/mod.rs2
4 files changed, 128 insertions, 0 deletions
diff --git a/tvix/eval/src/value/arbitrary.rs b/tvix/eval/src/value/arbitrary.rs
new file mode 100644
index 0000000000..56ccd3b28a
--- /dev/null
+++ b/tvix/eval/src/value/arbitrary.rs
@@ -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)
+        ]
+    })
+}
diff --git a/tvix/eval/src/value/attrs.rs b/tvix/eval/src/value/attrs.rs
index 86e52206b8..0551fe20d9 100644
--- a/tvix/eval/src/value/attrs.rs
+++ b/tvix/eval/src/value/attrs.rs
@@ -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.
diff --git a/tvix/eval/src/value/list.rs b/tvix/eval/src/value/list.rs
index c00ddd4191..19415c0d55 100644
--- a/tvix/eval/src/value/list.rs
+++ b/tvix/eval/src/value/list.rs
@@ -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();
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index c4e8221e15..9805af7f4b 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -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;