use super::*;
use crate::backend::{DataSource, DataSourceError};
use crate::cbor_utils::{cbor_map, map_get};
use crate::native::core::NativeTestCase;
use crate::native::rng::EngineRng;
fn random_source() -> (NativeDataSource, NativeTestCaseHandle) {
let ntc = NativeTestCase::new_random(EngineRng::seeded(7));
NativeDataSource::new(ntc)
}
fn exhausted_source() -> (NativeDataSource, NativeTestCaseHandle) {
let ntc = NativeTestCase::for_choices(&[], None, None);
NativeDataSource::new(ntc)
}
#[test]
fn take_nodes_and_take_spans_return_recorded_data() {
let (ds, handle) = random_source();
let schema = cbor_map! {
"type" => "integer",
"min_value" => 0,
"max_value" => 10,
};
ds.generate(&schema).unwrap();
let nodes = NativeDataSource::take_nodes(&handle);
let spans = NativeDataSource::take_spans(&handle);
assert_eq!(nodes.len(), 1);
assert_eq!(spans.len(), 1);
assert_eq!(spans[0].label, "integer");
}
#[test]
fn start_and_stop_span_return_ok() {
let (ds, handle) = random_source();
ds.start_span(42).unwrap();
ds.stop_span(false).unwrap();
ds.start_span(17).unwrap();
ds.stop_span(true).unwrap();
let spans = NativeDataSource::take_spans(&handle);
assert_eq!(spans.len(), 2);
assert_eq!(spans[0].label, "42");
assert_eq!(spans[0].start, spans[0].end);
assert!(!spans[0].discarded);
assert_eq!(spans[1].label, "17");
assert_eq!(spans[1].start, spans[1].end);
assert!(spans[1].discarded);
}
#[test]
fn new_collection_returns_sequential_id() {
let (ds, _handle) = random_source();
let id_a = ds.new_collection(0, None).unwrap();
let id_b = ds.new_collection(1, Some(3)).unwrap();
assert_eq!(id_a, 0);
assert_eq!(id_b, 1);
}
#[test]
fn collection_more_and_reject_round_trip() {
let (ds, _handle) = random_source();
let id = ds.new_collection(2, Some(4)).unwrap();
assert!(ds.collection_more(id).unwrap());
ds.collection_reject(id, None).unwrap();
assert!(ds.collection_more(id).unwrap());
ds.collection_reject(id, Some("nope")).unwrap();
}
#[test]
fn new_pool_pool_add_and_pool_generate_non_consuming() {
let (ds, _handle) = random_source();
let pool = ds.new_pool().unwrap();
let v1 = ds.pool_add(pool).unwrap();
let v2 = ds.pool_add(pool).unwrap();
assert_eq!(v1, 1);
assert_eq!(v2, 2);
let drawn = ds.pool_generate(pool, false).unwrap();
assert!(drawn == v1 || drawn == v2);
assert_eq!(ds.pool_generate(pool, true).ok().map(|_| ()), Some(()));
}
#[test]
fn pool_generate_on_empty_pool_returns_assume() {
let (ds, _handle) = random_source();
let pool = ds.new_pool().unwrap();
assert!(matches!(
ds.pool_generate(pool, false),
Err(DataSourceError::Assume)
));
}
#[test]
fn generate_invalid_schema_maps_to_invalid_argument_without_aborting() {
let (ds, _handle) = random_source();
let schema = cbor_map! { "type" => "no-such-type" };
let err = ds.generate(&schema).unwrap_err();
assert!(matches!(err, DataSourceError::InvalidArgument(_)));
assert!(!ds.test_aborted());
let good = cbor_map! { "type" => "integer", "min_value" => 0, "max_value" => 0 };
assert!(ds.generate(&good).is_ok());
}
#[test]
fn generate_stoptest_sets_aborted_and_short_circuits() {
let (ds, _handle) = exhausted_source();
let schema = cbor_map! {
"type" => "integer",
"min_value" => 0,
"max_value" => 10,
};
let err = ds.generate(&schema).unwrap_err();
assert!(matches!(err, DataSourceError::StopTest));
assert!(ds.test_aborted());
let err2 = ds.generate(&schema).unwrap_err();
assert!(matches!(err2, DataSourceError::StopTest));
assert!(ds.start_span(0).is_err());
assert!(ds.stop_span(false).is_err());
assert!(ds.new_collection(0, None).is_err());
assert!(ds.collection_more(0).is_err());
assert!(ds.collection_reject(0, None).is_err());
assert!(ds.new_pool().is_err());
assert!(ds.pool_add(0).is_err());
assert!(ds.pool_generate(0, false).is_err());
}
#[test]
fn generate_integer_round_trips() {
let (ds, _handle) = random_source();
let schema = cbor_map! {
"type" => "integer",
"min_value" => 5,
"max_value" => 5,
};
let value = ds.generate(&schema).unwrap();
let n = map_get(&cbor_map! { "v" => value.clone() }, "v")
.cloned()
.unwrap();
assert_eq!(n, ciborium::Value::Integer(5.into()));
}
#[test]
fn target_observation_records_finite_score() {
let (ds, handle) = random_source();
ds.target_observation(1.5, "x");
let obs = NativeDataSource::take_target_observations(&handle);
assert_eq!(obs.get("x"), Some(&1.5));
}
#[test]
fn target_observation_take_drains() {
let (ds, handle) = random_source();
ds.target_observation(1.0, "x");
let first = NativeDataSource::take_target_observations(&handle);
assert_eq!(first.len(), 1);
let second = NativeDataSource::take_target_observations(&handle);
assert!(second.is_empty());
}
#[test]
#[should_panic(expected = "requires a finite score")]
fn target_observation_rejects_nan() {
let (ds, _handle) = random_source();
ds.target_observation(f64::NAN, "x");
}
#[test]
#[should_panic(expected = "requires a finite score")]
fn target_observation_rejects_infinity() {
let (ds, _handle) = random_source();
ds.target_observation(f64::INFINITY, "x");
}
#[test]
#[should_panic(expected = "would overwrite previous")]
fn target_observation_rejects_duplicate_label() {
let (ds, _handle) = random_source();
ds.target_observation(1.0, "x");
ds.target_observation(2.0, "x");
}