use proptest::{
collection::{hash_set, vec},
prelude::*,
};
use super::Arguments;
proptest! {
#[test]
fn argument_order_is_preserved(argument_list in Argument::list_strategy()) {
let arguments = collect_arguments(argument_list.clone());
let argument_strings: Vec<_> = arguments.into_arguments().collect();
let expected_strings = expand_arguments(argument_list);
assert_eq!(argument_strings, expected_strings);
}
#[test]
fn arguments_can_be_overridden(
(argument_list, override_list) in Argument::list_and_overrides_strategy(),
extra_arguments in Argument::list_strategy(),
) {
let arguments_to_add: Vec<_> = argument_list
.into_iter()
.chain(override_list)
.chain(extra_arguments)
.collect();
let arguments = collect_arguments(arguments_to_add.clone());
let argument_strings: Vec<_> = arguments.into_arguments().collect();
let overridden_list = handle_overrides(arguments_to_add);
let expected_strings = expand_arguments(overridden_list);
assert_eq!(argument_strings, expected_strings);
}
#[test]
fn arguments_can_be_merged(
(argument_list, override_list) in Argument::list_and_overrides_strategy(),
extra_arguments in Argument::list_strategy(),
) {
let all_arguments: Vec<_> = argument_list
.clone()
.into_iter()
.chain(override_list.clone())
.chain(extra_arguments.clone())
.collect();
let arguments_for_second_instance = override_list.into_iter().chain(extra_arguments).collect();
let mut first_arguments = collect_arguments(argument_list);
let second_arguments = collect_arguments(arguments_for_second_instance);
first_arguments.merge_with(second_arguments);
let argument_strings: Vec<_> = first_arguments.into_arguments().collect();
let overridden_list = handle_overrides(all_arguments);
let expected_strings = expand_arguments(overridden_list);
assert_eq!(argument_strings, expected_strings);
}
}
fn collect_arguments(argument_list: Vec<Argument>) -> Arguments {
let mut arguments = Arguments::new();
for argument in argument_list {
match argument {
Argument::LoneArgument(argument) => arguments.set_argument(argument),
Argument::KeyValuePair(key, value) => arguments.set_parameter(key, value),
}
}
arguments
}
fn expand_arguments(argument_list: Vec<Argument>) -> Vec<String> {
let mut expected_strings = Vec::new();
for argument in argument_list {
match argument {
Argument::LoneArgument(argument) => expected_strings.push(argument),
Argument::KeyValuePair(key, value) => expected_strings.extend([key, value]),
}
}
expected_strings
}
fn handle_overrides(argument_list: Vec<Argument>) -> Vec<Argument> {
let mut overridden_list = Vec::new();
for override_argument in argument_list {
let search_term = match &override_argument {
Argument::LoneArgument(argument) => argument,
Argument::KeyValuePair(key, _value) => key,
};
let argument_to_override =
overridden_list
.iter_mut()
.find(|existing_argument| match existing_argument {
Argument::LoneArgument(argument) => argument == search_term,
Argument::KeyValuePair(key, _value) => key == search_term,
});
if let Some(argument_to_override) = argument_to_override {
*argument_to_override = override_argument;
} else {
overridden_list.push(override_argument);
}
}
overridden_list
}
#[derive(Clone, Debug)]
pub enum Argument {
LoneArgument(String),
KeyValuePair(String, String),
}
impl Argument {
pub fn list_strategy() -> impl Strategy<Value = Vec<Argument>> {
hash_set("\\PC+", 0..10)
.prop_map(|keys| keys.into_iter().collect::<Vec<_>>())
.prop_shuffle()
.prop_flat_map(|keys| {
let key_count = keys.len();
(Just(keys), vec(any::<bool>(), key_count))
})
.prop_flat_map(|(keys, is_pair)| {
let value_count = is_pair.iter().filter(|&&is_pair| is_pair).count();
(Just(keys), Just(is_pair), vec("\\PC+", value_count))
})
.prop_map(|(keys, is_pair, mut values)| {
keys.into_iter()
.zip(is_pair)
.map(|(key, is_pair)| {
if is_pair {
Argument::KeyValuePair(
key,
values.pop().expect("missing value from generated list"),
)
} else {
Argument::LoneArgument(key)
}
})
.collect()
})
}
pub fn list_and_overrides_strategy() -> impl Strategy<Value = (Vec<Argument>, Vec<Argument>)> {
Self::list_strategy()
.prop_flat_map(|argument_list| {
let argument_list_count = argument_list.len();
let max_overrides = argument_list_count * 3;
let min_overrides = 1.min(max_overrides);
let override_strategy = (0..argument_list_count, "\\PC*");
(
Just(argument_list),
hash_set(override_strategy, min_overrides..=max_overrides),
)
})
.prop_map(|(argument_list, override_seeds)| {
let override_list = override_seeds
.into_iter()
.map(|(index, new_value)| {
let key = match &argument_list[index] {
Argument::LoneArgument(argument) => argument,
Argument::KeyValuePair(key, _value) => key,
}
.clone();
if new_value.is_empty() {
Argument::LoneArgument(key)
} else {
Argument::KeyValuePair(key, new_value)
}
})
.collect();
(argument_list, override_list)
})
}
}