change gen.range based queries into frequency and one_of calls

This commit is contained in:
alpaylan
2024-12-18 17:09:44 -05:00
parent 66e7a4edec
commit 39b5dbed55
6 changed files with 122 additions and 98 deletions

View File

@@ -30,7 +30,7 @@ pub(crate) fn frequency<'a, T, R: rand::Rng>(
unreachable!()
}
pub(crate) fn one_of<T, R: rand::Rng>(choices: Vec<Box<dyn Fn(&mut R) -> T>>, rng: &mut R) -> T {
pub(crate) fn one_of<'a, T, R: rand::Rng>(choices: Vec<Box<dyn Fn(&mut R) -> T + 'a>>, rng: &mut R) -> T {
let index = rng.gen_range(0..choices.len());
choices[index](rng)
}

View File

@@ -1,10 +1,12 @@
use crate::generation::table::{GTValue, LTValue};
use crate::generation::{Arbitrary, ArbitraryFrom};
use crate::generation::{one_of, Arbitrary, ArbitraryFrom};
use crate::model::query::{Create, Delete, Insert, Predicate, Query, Select};
use crate::model::table::{Table, Value};
use rand::Rng;
use super::{frequency, pick};
impl Arbitrary for Create {
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
Create {
@@ -15,20 +17,20 @@ impl Arbitrary for Create {
impl ArbitraryFrom<Vec<Table>> for Select {
fn arbitrary_from<R: Rng>(rng: &mut R, tables: &Vec<Table>) -> Self {
let table = rng.gen_range(0..tables.len());
let table = pick(tables, rng);
Select {
table: tables[table].name.clone(),
predicate: Predicate::arbitrary_from(rng, &tables[table]),
table: table.name.clone(),
predicate: Predicate::arbitrary_from(rng, table),
}
}
}
impl ArbitraryFrom<Vec<&Table>> for Select {
fn arbitrary_from<R: Rng>(rng: &mut R, tables: &Vec<&Table>) -> Self {
let table = rng.gen_range(0..tables.len());
let table = pick(tables, rng);
Select {
table: tables[table].name.clone(),
predicate: Predicate::arbitrary_from(rng, tables[table]),
table: table.name.clone(),
predicate: Predicate::arbitrary_from(rng, *table),
}
}
}
@@ -58,15 +60,24 @@ impl ArbitraryFrom<Table> for Delete {
impl ArbitraryFrom<Table> for Query {
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
match rng.gen_range(0..=200) {
0 => Query::Create(Create::arbitrary(rng)),
1..=100 => Query::Select(Select::arbitrary_from(rng, &vec![table])),
101..=200 => Query::Insert(Insert::arbitrary_from(rng, table)),
// todo: This branch is currently never taken, as DELETE is not yet implemented.
// Change this when DELETE is implemented.
201..=300 => Query::Delete(Delete::arbitrary_from(rng, table)),
_ => unreachable!(),
}
frequency(
vec![
(1, Box::new(|rng| Query::Create(Create::arbitrary(rng)))),
(
100,
Box::new(|rng| Query::Select(Select::arbitrary_from(rng, &vec![table]))),
),
(
100,
Box::new(|rng| Query::Insert(Insert::arbitrary_from(rng, table))),
),
(
0,
Box::new(|rng| Query::Delete(Delete::arbitrary_from(rng, table))),
),
],
rng,
)
}
}
@@ -84,35 +95,53 @@ impl ArbitraryFrom<(&Table, bool)> for SimplePredicate {
.map(|r| &r[column_index])
.collect::<Vec<_>>();
// Pick an operator
let operator = match rng.gen_range(0..3) {
0 => {
if *predicate_value {
Predicate::Eq(
column.name.clone(),
Value::arbitrary_from(rng, &column_values),
)
} else {
Predicate::Eq(
column.name.clone(),
Value::arbitrary_from(rng, &column.column_type),
)
}
}
1 => Predicate::Gt(
column.name.clone(),
match predicate_value {
true => GTValue::arbitrary_from(rng, &column_values).0,
false => LTValue::arbitrary_from(rng, &column_values).0,
},
let operator = match predicate_value {
true => one_of(
vec![
Box::new(|rng| {
Predicate::Eq(
column.name.clone(),
Value::arbitrary_from(rng, &column_values),
)
}),
Box::new(|rng| {
Predicate::Gt(
column.name.clone(),
GTValue::arbitrary_from(rng, &column_values).0,
)
}),
Box::new(|rng| {
Predicate::Lt(
column.name.clone(),
LTValue::arbitrary_from(rng, &column_values).0,
)
}),
],
rng,
),
2 => Predicate::Lt(
column.name.clone(),
match predicate_value {
true => LTValue::arbitrary_from(rng, &column_values).0,
false => GTValue::arbitrary_from(rng, &column_values).0,
},
false => one_of(
vec![
Box::new(|rng| {
Predicate::Neq(
column.name.clone(),
Value::arbitrary_from(rng, &column.column_type),
)
}),
Box::new(|rng| {
Predicate::Gt(
column.name.clone(),
LTValue::arbitrary_from(rng, &column_values).0,
)
}),
Box::new(|rng| {
Predicate::Lt(
column.name.clone(),
GTValue::arbitrary_from(rng, &column_values).0,
)
}),
],
rng,
),
_ => unreachable!(),
};
SimplePredicate(operator)
@@ -191,17 +220,10 @@ impl ArbitraryFrom<Table> for Predicate {
impl ArbitraryFrom<(&str, &Value)> for Predicate {
fn arbitrary_from<R: Rng>(rng: &mut R, (column_name, value): &(&str, &Value)) -> Self {
match rng.gen_range(0..3) {
0 => Predicate::Eq(column_name.to_string(), (*value).clone()),
1 => Predicate::Gt(
column_name.to_string(),
LTValue::arbitrary_from(rng, *value).0,
),
2 => Predicate::Lt(
column_name.to_string(),
LTValue::arbitrary_from(rng, *value).0,
),
_ => unreachable!(),
}
one_of(vec![
Box::new(|rng| Predicate::Eq(column_name.to_string(), (*value).clone())),
Box::new(|rng| Predicate::Gt(column_name.to_string(), GTValue::arbitrary_from(rng, *value).0)),
Box::new(|rng| Predicate::Lt(column_name.to_string(), LTValue::arbitrary_from(rng, *value).0)),
], rng)
}
}

View File

@@ -1,6 +1,6 @@
use rand::Rng;
use crate::generation::{gen_random_text, readable_name_custom, Arbitrary, ArbitraryFrom};
use crate::generation::{pick_index, gen_random_text, pick, readable_name_custom, Arbitrary, ArbitraryFrom};
use crate::model::table::{Column, ColumnType, Name, Table, Value};
impl Arbitrary for Name {
@@ -13,7 +13,7 @@ impl Arbitrary for Name {
impl Arbitrary for Table {
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
let name = Name::arbitrary(rng).0;
let columns = (1..=rng.gen_range(1..10))
let columns = (1..=rng.gen_range(1..5))
.map(|_| Column::arbitrary(rng))
.collect();
Table {
@@ -39,13 +39,16 @@ impl Arbitrary for Column {
impl Arbitrary for ColumnType {
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
match rng.gen_range(0..4) {
0 => ColumnType::Integer,
1 => ColumnType::Float,
2 => ColumnType::Text,
3 => ColumnType::Blob,
_ => unreachable!(),
}
pick(
&vec![
ColumnType::Integer,
ColumnType::Float,
ColumnType::Text,
ColumnType::Blob,
],
rng,
)
.to_owned()
}
}
@@ -55,8 +58,7 @@ impl ArbitraryFrom<Vec<&Value>> for Value {
return Value::Null;
}
let index = rng.gen_range(0..values.len());
values[index].clone()
pick(values, rng).to_owned().clone()
}
}
@@ -78,8 +80,8 @@ impl ArbitraryFrom<Vec<&Value>> for LTValue {
if values.is_empty() {
return LTValue(Value::Null);
}
let index = rng.gen_range(0..values.len());
let index = pick_index(values.len(), rng);
LTValue::arbitrary_from(rng, values[index])
}
}
@@ -139,7 +141,7 @@ impl ArbitraryFrom<Vec<&Value>> for GTValue {
return GTValue(Value::Null);
}
let index = rng.gen_range(0..values.len());
let index = pick_index(values.len(), rng);
GTValue::arbitrary_from(rng, values[index])
}
}

View File

@@ -1,7 +1,7 @@
use generation::plan::{Interaction, ResultSet};
use generation::{pick, pick_index, Arbitrary, ArbitraryFrom};
use limbo_core::{Connection, Database, File, OpenFlags, PlatformIO, Result, RowResult, IO};
use model::query::{Insert, Predicate, Query, Select};
use model::query::{Create, Insert, Predicate, Query, Select};
use model::table::{Column, Name, Table, Value};
use properties::{property_insert_select, property_select_all};
use rand::prelude::*;
@@ -189,7 +189,12 @@ fn maybe_add_table(env: &mut SimulatorEnv, conn: &mut Rc<Connection>) -> Result<
.map(|_| Column::arbitrary(&mut env.rng))
.collect(),
};
let rows = get_all_rows(env, conn, table.to_create_str().as_str())?;
let query = Query::Create(Create { table: table.clone() });
let rows = get_all_rows(
env,
conn,
query.to_string().as_str(),
)?;
log::debug!("{:?}", rows);
let rows = get_all_rows(
env,
@@ -207,7 +212,7 @@ fn maybe_add_table(env: &mut SimulatorEnv, conn: &mut Rc<Connection>) -> Result<
_ => unreachable!(),
};
assert!(
*as_text != table.to_create_str(),
*as_text != query.to_string(),
"table was not inserted correctly"
);
env.tables.push(table);

View File

@@ -7,6 +7,7 @@ pub(crate) enum Predicate {
And(Vec<Predicate>), // p1 AND p2 AND p3... AND pn
Or(Vec<Predicate>), // p1 OR p2 OR p3... OR pn
Eq(String, Value), // column = Value
Neq(String, Value), // column != Value
Gt(String, Value), // column > Value
Lt(String, Value), // column < Value
}
@@ -44,6 +45,7 @@ impl Display for Predicate {
}
}
Predicate::Eq(name, value) => write!(f, "{} = {}", name, value),
Predicate::Neq(name, value) => write!(f, "{} != {}", name, value),
Predicate::Gt(name, value) => write!(f, "{} > {}", name, value),
Predicate::Lt(name, value) => write!(f, "{} < {}", name, value),
}
@@ -85,7 +87,18 @@ pub(crate) struct Delete {
impl Display for Query {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Query::Create(Create { table }) => write!(f, "{}", table.to_create_str()),
Query::Create(Create { table }) => {
write!(f, "CREATE TABLE {} (", table.name)?;
for (i, column) in table.columns.iter().enumerate() {
if i != 0 {
write!(f, ",")?;
}
write!(f, "{} {}", column.name, column.column_type)?;
}
write!(f, ")")
},
Query::Select(Select {
table,
predicate: guard,

View File

@@ -17,24 +17,6 @@ pub(crate) struct Table {
pub(crate) columns: Vec<Column>,
}
impl Table {
pub fn to_create_str(&self) -> String {
let mut out = String::new();
out.push_str(format!("CREATE TABLE {} (", self.name).as_str());
assert!(!self.columns.is_empty());
for column in &self.columns {
out.push_str(format!("{} {},", column.name, column.column_type.as_str()).as_str());
}
// remove last comma
out.pop();
out.push_str(");");
out
}
}
#[derive(Debug, Clone)]
pub(crate) struct Column {
pub(crate) name: String,
@@ -51,13 +33,13 @@ pub(crate) enum ColumnType {
Blob,
}
impl ColumnType {
pub fn as_str(&self) -> &str {
impl Display for ColumnType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ColumnType::Integer => "INTEGER",
ColumnType::Float => "FLOAT",
ColumnType::Text => "TEXT",
ColumnType::Blob => "BLOB",
ColumnType::Integer => write!(f, "INTEGER"),
ColumnType::Float => write!(f, "REAL"),
ColumnType::Text => write!(f, "TEXT"),
ColumnType::Blob => write!(f, "BLOB"),
}
}
}