mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-25 20:14:21 +01:00
add connection Index to interaction struct
This commit is contained in:
@@ -143,37 +143,26 @@ pub enum InteractionsType {
|
||||
Fault(Fault),
|
||||
}
|
||||
|
||||
impl Shadow for InteractionsType {
|
||||
impl Shadow for Interactions {
|
||||
type Result = ();
|
||||
|
||||
fn shadow(&self, tables: &mut SimulatorTables) {
|
||||
match self {
|
||||
Self::Property(property) => {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
let initial_tables = tables.clone();
|
||||
let mut is_error = false;
|
||||
for interaction in property.interactions() {
|
||||
match interaction {
|
||||
Interaction::Query(query) | Interaction::FsyncQuery(query) => {
|
||||
if query.shadow(tables).is_err() {
|
||||
is_error = true;
|
||||
}
|
||||
}
|
||||
Interaction::FaultyQuery(..) => {}
|
||||
Interaction::Assertion(_) => {}
|
||||
Interaction::Assumption(_) => {}
|
||||
Interaction::Fault(_) => {}
|
||||
}
|
||||
if is_error {
|
||||
for interaction in property.interactions(self.connection_index) {
|
||||
let res = interaction.shadow(tables);
|
||||
if res.is_err() {
|
||||
// If any interaction fails, we reset the tables to the initial state
|
||||
*tables = initial_tables.clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::Query(query) => {
|
||||
InteractionsType::Query(query) => {
|
||||
let _ = query.shadow(tables);
|
||||
}
|
||||
Self::Fault(_) => {}
|
||||
InteractionsType::Fault(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,26 +178,30 @@ impl Interactions {
|
||||
|
||||
pub(crate) fn interactions(&self) -> Vec<Interaction> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => property.interactions(),
|
||||
InteractionsType::Query(query) => vec![Interaction::Query(query.clone())],
|
||||
InteractionsType::Fault(fault) => vec![Interaction::Fault(fault.clone())],
|
||||
InteractionsType::Property(property) => property.interactions(self.connection_index),
|
||||
InteractionsType::Query(query) => vec![Interaction::new(
|
||||
self.connection_index,
|
||||
InteractionType::Query(query.clone()),
|
||||
)],
|
||||
InteractionsType::Fault(fault) => vec![Interaction::new(
|
||||
self.connection_index,
|
||||
InteractionType::Fault(fault.clone()),
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dependencies(&self) -> IndexSet<String> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
property
|
||||
.interactions()
|
||||
.iter()
|
||||
.fold(IndexSet::new(), |mut acc, i| match i {
|
||||
Interaction::Query(q) => {
|
||||
acc.extend(q.dependencies());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
})
|
||||
}
|
||||
InteractionsType::Property(property) => property
|
||||
.interactions(self.connection_index)
|
||||
.iter()
|
||||
.fold(IndexSet::new(), |mut acc, i| match &i.interaction {
|
||||
InteractionType::Query(q) => {
|
||||
acc.extend(q.dependencies());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
}),
|
||||
InteractionsType::Query(query) => query.dependencies(),
|
||||
InteractionsType::Fault(_) => IndexSet::new(),
|
||||
}
|
||||
@@ -216,18 +209,16 @@ impl Interactions {
|
||||
|
||||
pub(crate) fn uses(&self) -> Vec<String> {
|
||||
match &self.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
property
|
||||
.interactions()
|
||||
.iter()
|
||||
.fold(vec![], |mut acc, i| match i {
|
||||
Interaction::Query(q) => {
|
||||
acc.extend(q.uses());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
})
|
||||
}
|
||||
InteractionsType::Property(property) => property
|
||||
.interactions(self.connection_index)
|
||||
.iter()
|
||||
.fold(vec![], |mut acc, i| match &i.interaction {
|
||||
InteractionType::Query(q) => {
|
||||
acc.extend(q.uses());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
}),
|
||||
InteractionsType::Query(query) => query.uses(),
|
||||
InteractionsType::Fault(_) => vec![],
|
||||
}
|
||||
@@ -242,27 +233,8 @@ impl Display for InteractionPlan {
|
||||
InteractionsType::Property(property) => {
|
||||
let name = property.name();
|
||||
writeln!(f, "-- begin testing '{name}'")?;
|
||||
for interaction in property.interactions() {
|
||||
write!(f, "\t")?;
|
||||
|
||||
match interaction {
|
||||
Interaction::Query(query) => writeln!(f, "{query};")?,
|
||||
Interaction::Assumption(assumption) => {
|
||||
writeln!(f, "-- ASSUME {};", assumption.name)?
|
||||
}
|
||||
Interaction::Assertion(assertion) => {
|
||||
writeln!(f, "-- ASSERT {};", assertion.name)?
|
||||
}
|
||||
Interaction::Fault(fault) => writeln!(f, "-- FAULT '{fault}';")?,
|
||||
Interaction::FsyncQuery(query) => {
|
||||
writeln!(f, "-- FSYNC QUERY;")?;
|
||||
writeln!(f, "{query};")?;
|
||||
writeln!(f, "{query};")?
|
||||
}
|
||||
Interaction::FaultyQuery(query) => {
|
||||
writeln!(f, "{query}; -- FAULTY QUERY")?
|
||||
}
|
||||
}
|
||||
for interaction in property.interactions(interactions.connection_index) {
|
||||
writeln!(f, "\t{}", interaction)?;
|
||||
}
|
||||
writeln!(f, "-- end testing '{name}'")?;
|
||||
}
|
||||
@@ -355,7 +327,7 @@ impl Assertion {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub(crate) enum Fault {
|
||||
Disconnect,
|
||||
ReopenDatabase,
|
||||
@@ -406,8 +378,8 @@ impl InteractionPlan {
|
||||
for interactions in &self.plan {
|
||||
match &interactions.interactions {
|
||||
InteractionsType::Property(property) => {
|
||||
for interaction in &property.interactions() {
|
||||
if let Interaction::Query(query) = interaction {
|
||||
for interaction in &property.interactions(interactions.connection_index) {
|
||||
if let InteractionType::Query(query) = &interaction.interaction {
|
||||
query_stat(query, &mut stats);
|
||||
}
|
||||
}
|
||||
@@ -453,8 +425,37 @@ impl InteractionPlan {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Interaction {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Interaction {
|
||||
pub connection_index: usize,
|
||||
pub interaction: InteractionType,
|
||||
}
|
||||
|
||||
impl Deref for Interaction {
|
||||
type Target = InteractionType;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.interaction
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Interaction {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.interaction
|
||||
}
|
||||
}
|
||||
|
||||
impl Interaction {
|
||||
pub fn new(connection_index: usize, interaction: InteractionType) -> Self {
|
||||
Self {
|
||||
connection_index,
|
||||
interaction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InteractionType {
|
||||
Query(Query),
|
||||
Assumption(Assertion),
|
||||
Assertion(Assertion),
|
||||
@@ -465,20 +466,33 @@ pub(crate) enum Interaction {
|
||||
FaultyQuery(Query),
|
||||
}
|
||||
|
||||
// FIXME: add the connection index here later
|
||||
impl Display for Interaction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.interaction)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InteractionType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Query(query) => write!(f, "{query}"),
|
||||
Self::Assumption(assumption) => write!(f, "ASSUME {}", assumption.name),
|
||||
Self::Assertion(assertion) => write!(f, "ASSERT {}", assertion.name),
|
||||
Self::Fault(fault) => write!(f, "FAULT '{fault}'"),
|
||||
Self::FsyncQuery(query) => write!(f, "{query}"),
|
||||
Self::Assumption(assumption) => write!(f, "-- ASSUME {};", assumption.name),
|
||||
Self::Assertion(assertion) => {
|
||||
write!(f, "-- ASSERT {};", assertion.name)
|
||||
}
|
||||
Self::Fault(fault) => write!(f, "-- FAULT '{fault}';"),
|
||||
Self::FsyncQuery(query) => {
|
||||
writeln!(f, "-- FSYNC QUERY;")?;
|
||||
writeln!(f, "{query};")?;
|
||||
write!(f, "{query};")
|
||||
}
|
||||
Self::FaultyQuery(query) => write!(f, "{query}; -- FAULTY QUERY"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shadow for Interaction {
|
||||
impl Shadow for InteractionType {
|
||||
type Result = anyhow::Result<Vec<Vec<SimValue>>>;
|
||||
fn shadow(&self, env: &mut SimulatorTables) -> Self::Result {
|
||||
match self {
|
||||
@@ -494,7 +508,7 @@ impl Shadow for Interaction {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Interaction {
|
||||
impl InteractionType {
|
||||
pub(crate) fn execute_query(&self, conn: &mut Arc<Connection>) -> ResultSet {
|
||||
if let Self::Query(query) = self {
|
||||
let query_str = query.to_string();
|
||||
|
||||
@@ -16,7 +16,10 @@ use turso_core::{LimboError, types};
|
||||
use turso_parser::ast::{self, Distinctness};
|
||||
|
||||
use crate::{
|
||||
generation::Shadow as _, model::Query, profiles::query::QueryProfile, runner::env::SimulatorEnv,
|
||||
generation::{Shadow as _, plan::InteractionType},
|
||||
model::Query,
|
||||
profiles::query::QueryProfile,
|
||||
runner::env::SimulatorEnv,
|
||||
};
|
||||
|
||||
use super::plan::{Assertion, Interaction, InteractionStats, ResultSet};
|
||||
@@ -215,12 +218,12 @@ impl Property {
|
||||
/// interactions construct a list of interactions, which is an executable representation of the property.
|
||||
/// the requirement of property -> vec<interaction> conversion emerges from the need to serialize the property,
|
||||
/// and `interaction` cannot be serialized directly.
|
||||
pub(crate) fn interactions(&self) -> Vec<Interaction> {
|
||||
pub(crate) fn interactions(&self, connection_index: usize) -> Vec<Interaction> {
|
||||
match self {
|
||||
Property::TableHasExpectedContent { table } => {
|
||||
let table = table.to_string();
|
||||
let table_name = table.clone();
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {} exists", table.clone()),
|
||||
move |_: &Vec<ResultSet>, env: &mut SimulatorEnv| {
|
||||
if env.tables.iter().any(|t| t.name == table_name) {
|
||||
@@ -231,12 +234,12 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let select_interaction = Interaction::Query(Query::Select(Select::simple(
|
||||
let select_interaction = InteractionType::Query(Query::Select(Select::simple(
|
||||
table.clone(),
|
||||
Predicate::true_(),
|
||||
)));
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
format!("table {} should have the expected content", table.clone()),
|
||||
move |stack: &Vec<ResultSet>, env| {
|
||||
let rows = stack.last().unwrap();
|
||||
@@ -269,11 +272,15 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
vec![assumption, select_interaction, assertion]
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select_interaction),
|
||||
Interaction::new(connection_index, assertion),
|
||||
]
|
||||
}
|
||||
Property::ReadYourUpdatesBack { update, select } => {
|
||||
let table = update.table().to_string();
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {} exists", table.clone()),
|
||||
move |_: &Vec<ResultSet>, env: &mut SimulatorEnv| {
|
||||
if env.tables.iter().any(|t| t.name == table.clone()) {
|
||||
@@ -284,14 +291,14 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let update_interaction = Interaction::Query(Query::Update(update.clone()));
|
||||
let select_interaction = Interaction::Query(Query::Select(select.clone()));
|
||||
let update_interaction = InteractionType::Query(Query::Update(update.clone()));
|
||||
let select_interaction = InteractionType::Query(Query::Select(select.clone()));
|
||||
|
||||
let update = update.clone();
|
||||
|
||||
let table = update.table().to_string();
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
format!(
|
||||
"updated rows should be found and have the updated values for table {}",
|
||||
table.clone()
|
||||
@@ -318,10 +325,10 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
assumption,
|
||||
update_interaction,
|
||||
select_interaction,
|
||||
assertion,
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, update_interaction),
|
||||
Interaction::new(connection_index, select_interaction),
|
||||
Interaction::new(connection_index, assertion),
|
||||
]
|
||||
}
|
||||
Property::InsertValuesSelect {
|
||||
@@ -348,7 +355,7 @@ impl Property {
|
||||
let row = values[*row_index].clone();
|
||||
|
||||
// Assume that the table exists
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {} exists", insert.table()),
|
||||
{
|
||||
let table_name = table.clone();
|
||||
@@ -362,7 +369,7 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
format!(
|
||||
"row [{:?}] should be found in table {}, interactive={} commit={}, rollback={}",
|
||||
row.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
|
||||
@@ -397,18 +404,29 @@ impl Property {
|
||||
));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(assumption);
|
||||
interactions.push(Interaction::Query(Query::Insert(insert.clone())));
|
||||
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
|
||||
interactions.push(Interaction::Query(Query::Select(select.clone())));
|
||||
interactions.push(assertion);
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(
|
||||
connection_index,
|
||||
InteractionType::Query(Query::Insert(insert.clone())),
|
||||
));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(
|
||||
connection_index,
|
||||
InteractionType::Query(Query::Select(select.clone())),
|
||||
));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
Property::DoubleCreateFailure { create, queries } => {
|
||||
let table_name = create.table.name.clone();
|
||||
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
"Double-Create-Failure should not be called on an existing table".to_string(),
|
||||
move |_: &Vec<ResultSet>, env: &mut SimulatorEnv| {
|
||||
if !env.tables.iter().any(|t| t.name == table_name) {
|
||||
@@ -419,12 +437,12 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let cq1 = Interaction::Query(Query::Create(create.clone()));
|
||||
let cq2 = Interaction::Query(Query::Create(create.clone()));
|
||||
let cq1 = InteractionType::Query(Query::Create(create.clone()));
|
||||
let cq2 = InteractionType::Query(Query::Create(create.clone()));
|
||||
|
||||
let table_name = create.table.name.clone();
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new("creating two tables with the name should result in a failure for the second query"
|
||||
let assertion = InteractionType::Assertion(Assertion::new("creating two tables with the name should result in a failure for the second query"
|
||||
.to_string(), move |stack: &Vec<ResultSet>, _| {
|
||||
let last = stack.last().unwrap();
|
||||
match last {
|
||||
@@ -440,16 +458,21 @@ impl Property {
|
||||
}) );
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(assumption);
|
||||
interactions.push(cq1);
|
||||
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
|
||||
interactions.push(cq2);
|
||||
interactions.push(assertion);
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, cq1));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(connection_index, cq2));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
Property::SelectLimit { select } => {
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!(
|
||||
"table ({}) exists",
|
||||
select
|
||||
@@ -481,7 +504,7 @@ impl Property {
|
||||
.limit
|
||||
.expect("Property::SelectLimit without a LIMIT clause");
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
"select query should respect the limit clause".to_string(),
|
||||
move |stack: &Vec<ResultSet>, _| {
|
||||
let last = stack.last().unwrap();
|
||||
@@ -503,9 +526,12 @@ impl Property {
|
||||
));
|
||||
|
||||
vec![
|
||||
assumption,
|
||||
Interaction::Query(Query::Select(select.clone())),
|
||||
assertion,
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(
|
||||
connection_index,
|
||||
InteractionType::Query(Query::Select(select.clone())),
|
||||
),
|
||||
Interaction::new(connection_index, assertion),
|
||||
]
|
||||
}
|
||||
Property::DeleteSelect {
|
||||
@@ -513,7 +539,7 @@ impl Property {
|
||||
predicate,
|
||||
queries,
|
||||
} => {
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {table} exists"),
|
||||
{
|
||||
let table = table.clone();
|
||||
@@ -533,17 +559,17 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let delete = Interaction::Query(Query::Delete(Delete {
|
||||
let delete = InteractionType::Query(Query::Delete(Delete {
|
||||
table: table.clone(),
|
||||
predicate: predicate.clone(),
|
||||
}));
|
||||
|
||||
let select = Interaction::Query(Query::Select(Select::simple(
|
||||
let select = InteractionType::Query(Query::Select(Select::simple(
|
||||
table.clone(),
|
||||
predicate.clone(),
|
||||
)));
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
format!("`{select}` should return no values for table `{table}`",),
|
||||
move |stack: &Vec<ResultSet>, _| {
|
||||
let rows = stack.last().unwrap();
|
||||
@@ -568,11 +594,16 @@ impl Property {
|
||||
));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
interactions.push(assumption);
|
||||
interactions.push(delete);
|
||||
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
|
||||
interactions.push(select);
|
||||
interactions.push(assertion);
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, delete));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(connection_index, select));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
@@ -581,7 +612,7 @@ impl Property {
|
||||
queries,
|
||||
select,
|
||||
} => {
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {table} exists"),
|
||||
{
|
||||
let table = table.clone();
|
||||
@@ -603,7 +634,7 @@ impl Property {
|
||||
|
||||
let table_name = table.clone();
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
format!("select query should result in an error for table '{table}'"),
|
||||
move |stack: &Vec<ResultSet>, _| {
|
||||
let last = stack.last().unwrap();
|
||||
@@ -626,24 +657,29 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let drop = Interaction::Query(Query::Drop(Drop {
|
||||
let drop = InteractionType::Query(Query::Drop(Drop {
|
||||
table: table.clone(),
|
||||
}));
|
||||
|
||||
let select = Interaction::Query(Query::Select(select.clone()));
|
||||
let select = InteractionType::Query(Query::Select(select.clone()));
|
||||
|
||||
let mut interactions = Vec::new();
|
||||
|
||||
interactions.push(assumption);
|
||||
interactions.push(drop);
|
||||
interactions.extend(queries.clone().into_iter().map(Interaction::Query));
|
||||
interactions.push(select);
|
||||
interactions.push(assertion);
|
||||
interactions.push(Interaction::new(connection_index, assumption));
|
||||
interactions.push(Interaction::new(connection_index, drop));
|
||||
interactions.extend(
|
||||
queries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|q| Interaction::new(connection_index, InteractionType::Query(q))),
|
||||
);
|
||||
interactions.push(Interaction::new(connection_index, select));
|
||||
interactions.push(Interaction::new(connection_index, assertion));
|
||||
|
||||
interactions
|
||||
}
|
||||
Property::SelectSelectOptimizer { table, predicate } => {
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!("table {table} exists"),
|
||||
{
|
||||
let table = table.clone();
|
||||
@@ -663,7 +699,7 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
let select1 = Interaction::Query(Query::Select(Select::single(
|
||||
let select1 = InteractionType::Query(Query::Select(Select::single(
|
||||
table.clone(),
|
||||
vec![ResultColumn::Expr(predicate.clone())],
|
||||
Predicate::true_(),
|
||||
@@ -673,9 +709,9 @@ impl Property {
|
||||
|
||||
let select2_query = Query::Select(Select::simple(table.clone(), predicate.clone()));
|
||||
|
||||
let select2 = Interaction::Query(select2_query);
|
||||
let select2 = InteractionType::Query(select2_query);
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
"select queries should return the same amount of results".to_string(),
|
||||
move |stack: &Vec<ResultSet>, _| {
|
||||
let select_star = stack.last().unwrap();
|
||||
@@ -723,16 +759,25 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
vec![assumption, select1, select2, assertion]
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select1),
|
||||
Interaction::new(connection_index, select2),
|
||||
Interaction::new(connection_index, assertion),
|
||||
]
|
||||
}
|
||||
Property::FsyncNoWait { query, tables } => {
|
||||
let checks = assert_all_table_values(tables);
|
||||
let checks = assert_all_table_values(tables, connection_index);
|
||||
Vec::from_iter(
|
||||
std::iter::once(Interaction::FsyncQuery(query.clone())).chain(checks),
|
||||
std::iter::once(Interaction::new(
|
||||
connection_index,
|
||||
InteractionType::FsyncQuery(query.clone()),
|
||||
))
|
||||
.chain(checks),
|
||||
)
|
||||
}
|
||||
Property::FaultyQuery { query, tables } => {
|
||||
let checks = assert_all_table_values(tables);
|
||||
let checks = assert_all_table_values(tables, connection_index);
|
||||
let query_clone = query.clone();
|
||||
// A fault may not occur as we first signal we want a fault injected,
|
||||
// then when IO is called the fault triggers. It may happen that a fault is injected
|
||||
@@ -756,14 +801,15 @@ impl Property {
|
||||
},
|
||||
);
|
||||
let first = [
|
||||
Interaction::FaultyQuery(query.clone()),
|
||||
Interaction::Assertion(assert),
|
||||
InteractionType::FaultyQuery(query.clone()),
|
||||
InteractionType::Assertion(assert),
|
||||
]
|
||||
.into_iter();
|
||||
.into_iter()
|
||||
.map(|i| Interaction::new(connection_index, i));
|
||||
Vec::from_iter(first.chain(checks))
|
||||
}
|
||||
Property::WhereTrueFalseNull { select, predicate } => {
|
||||
let assumption = Interaction::Assumption(Assertion::new(
|
||||
let assumption = InteractionType::Assumption(Assertion::new(
|
||||
format!(
|
||||
"tables ({}) exists",
|
||||
select
|
||||
@@ -838,11 +884,11 @@ impl Property {
|
||||
limit: None,
|
||||
};
|
||||
|
||||
let select = Interaction::Query(Query::Select(select.clone()));
|
||||
let select_tlp = Interaction::Query(Query::Select(select_tlp));
|
||||
let select = InteractionType::Query(Query::Select(select.clone()));
|
||||
let select_tlp = InteractionType::Query(Query::Select(select_tlp));
|
||||
|
||||
// select and select_tlp should return the same rows
|
||||
let assertion = Interaction::Assertion(Assertion::new(
|
||||
let assertion = InteractionType::Assertion(Assertion::new(
|
||||
"select and select_tlp should return the same rows".to_string(),
|
||||
move |stack: &Vec<ResultSet>, _: &mut SimulatorEnv| {
|
||||
if stack.len() < 2 {
|
||||
@@ -912,7 +958,12 @@ impl Property {
|
||||
},
|
||||
));
|
||||
|
||||
vec![assumption, select, select_tlp, assertion]
|
||||
vec![
|
||||
Interaction::new(connection_index, assumption),
|
||||
Interaction::new(connection_index, select),
|
||||
Interaction::new(connection_index, select_tlp),
|
||||
Interaction::new(connection_index, assertion),
|
||||
]
|
||||
}
|
||||
Property::UNIONAllPreservesCardinality {
|
||||
select,
|
||||
@@ -924,10 +975,10 @@ impl Property {
|
||||
let s3 = Select::compound(s1.clone(), s2.clone(), CompoundOperator::UnionAll);
|
||||
|
||||
vec![
|
||||
Interaction::Query(Query::Select(s1.clone())),
|
||||
Interaction::Query(Query::Select(s2.clone())),
|
||||
Interaction::Query(Query::Select(s3.clone())),
|
||||
Interaction::Assertion(Assertion::new(
|
||||
InteractionType::Query(Query::Select(s1.clone())),
|
||||
InteractionType::Query(Query::Select(s2.clone())),
|
||||
InteractionType::Query(Query::Select(s3.clone())),
|
||||
InteractionType::Assertion(Assertion::new(
|
||||
"UNION ALL should preserve cardinality".to_string(),
|
||||
move |stack: &Vec<ResultSet>, _: &mut SimulatorEnv| {
|
||||
if stack.len() < 3 {
|
||||
@@ -960,20 +1011,23 @@ impl Property {
|
||||
}
|
||||
},
|
||||
)),
|
||||
]
|
||||
].into_iter().map(|i| Interaction::new(connection_index, i)).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_all_table_values(tables: &[String]) -> impl Iterator<Item = Interaction> + use<'_> {
|
||||
tables.iter().flat_map(|table| {
|
||||
let select = Interaction::Query(Query::Select(Select::simple(
|
||||
fn assert_all_table_values(
|
||||
tables: &[String],
|
||||
connection_index: usize,
|
||||
) -> impl Iterator<Item = Interaction> + use<'_> {
|
||||
tables.iter().flat_map(move |table| {
|
||||
let select = InteractionType::Query(Query::Select(Select::simple(
|
||||
table.clone(),
|
||||
Predicate::true_(),
|
||||
)));
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion::new(format!("table {table} should contain all of its expected values"), {
|
||||
let assertion = InteractionType::Assertion(Assertion::new(format!("table {table} should contain all of its expected values"), {
|
||||
let table = table.clone();
|
||||
move |stack: &Vec<ResultSet>, env: &mut SimulatorEnv| {
|
||||
let table = env.tables.iter().find(|t| t.name == table).ok_or_else(|| {
|
||||
@@ -1017,7 +1071,7 @@ fn assert_all_table_values(tables: &[String]) -> impl Iterator<Item = Interactio
|
||||
}
|
||||
}
|
||||
}));
|
||||
[select, assertion].into_iter()
|
||||
[select, assertion].into_iter().map(move |i| Interaction::new(connection_index, i))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,9 @@ use sql_generation::{generation::pick_index, model::table::SimValue};
|
||||
use turso_core::Value;
|
||||
|
||||
use crate::{
|
||||
InteractionPlan,
|
||||
generation::{
|
||||
Shadow as _,
|
||||
plan::{Interaction, InteractionPlanState, ResultSet},
|
||||
},
|
||||
model::Query,
|
||||
runner::execution::ExecutionContinuation,
|
||||
plan::{Interaction, InteractionPlanState, InteractionType, ResultSet}, Shadow as _
|
||||
}, model::Query, runner::execution::ExecutionContinuation, InteractionPlan
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -394,8 +390,8 @@ fn execute_interaction_rusqlite(
|
||||
connection_index,
|
||||
interaction
|
||||
);
|
||||
match interaction {
|
||||
Interaction::Query(query) => {
|
||||
match &interaction.interaction {
|
||||
InteractionType::Query(query) => {
|
||||
let conn = match &mut env.connections[connection_index] {
|
||||
SimConnection::SQLiteConnection(conn) => conn,
|
||||
SimConnection::LimboConnection(_) => unreachable!(),
|
||||
@@ -409,14 +405,14 @@ fn execute_interaction_rusqlite(
|
||||
tracing::debug!("{:?}", results);
|
||||
stack.push(results);
|
||||
}
|
||||
Interaction::FsyncQuery(..) => {
|
||||
InteractionType::FsyncQuery(..) => {
|
||||
unimplemented!("cannot implement fsync query in rusqlite, as we do not control IO");
|
||||
}
|
||||
Interaction::Assertion(_) => {
|
||||
InteractionType::Assertion(_) => {
|
||||
interaction.execute_assertion(stack, env)?;
|
||||
stack.clear();
|
||||
}
|
||||
Interaction::Assumption(_) => {
|
||||
InteractionType::Assumption(_) => {
|
||||
let assumption_result = interaction.execute_assumption(stack, env);
|
||||
stack.clear();
|
||||
|
||||
@@ -425,10 +421,10 @@ fn execute_interaction_rusqlite(
|
||||
return Ok(ExecutionContinuation::NextProperty);
|
||||
}
|
||||
}
|
||||
Interaction::Fault(_) => {
|
||||
InteractionType::Fault(_) => {
|
||||
interaction.execute_fault(env, connection_index)?;
|
||||
}
|
||||
Interaction::FaultyQuery(_) => {
|
||||
InteractionType::FaultyQuery(_) => {
|
||||
unimplemented!("cannot implement faulty query in rusqlite, as we do not control IO");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use turso_core::{Connection, LimboError, Result, StepResult};
|
||||
|
||||
use crate::generation::{
|
||||
Shadow as _,
|
||||
plan::{Interaction, InteractionPlan, InteractionPlanState, ResultSet},
|
||||
plan::{Interaction, InteractionPlan, InteractionPlanState, InteractionType, ResultSet},
|
||||
};
|
||||
|
||||
use super::env::{SimConnection, SimulatorEnv};
|
||||
@@ -183,8 +183,8 @@ pub(crate) fn execute_interaction(
|
||||
) -> Result<ExecutionContinuation> {
|
||||
// Leave this empty info! here to print the span of the execution
|
||||
tracing::info!("");
|
||||
match interaction {
|
||||
Interaction::Query(_) => {
|
||||
match &interaction.interaction {
|
||||
InteractionType::Query(_) => {
|
||||
let conn = match &mut env.connections[connection_index] {
|
||||
SimConnection::LimboConnection(conn) => conn,
|
||||
SimConnection::SQLiteConnection(_) => unreachable!(),
|
||||
@@ -198,7 +198,7 @@ pub(crate) fn execute_interaction(
|
||||
stack.push(results);
|
||||
limbo_integrity_check(conn)?;
|
||||
}
|
||||
Interaction::FsyncQuery(query) => {
|
||||
InteractionType::FsyncQuery(query) => {
|
||||
let conn = match &env.connections[connection_index] {
|
||||
SimConnection::LimboConnection(conn) => conn.clone(),
|
||||
SimConnection::SQLiteConnection(_) => unreachable!(),
|
||||
@@ -211,15 +211,18 @@ pub(crate) fn execute_interaction(
|
||||
}
|
||||
stack.push(results);
|
||||
|
||||
let query_interaction = Interaction::Query(query.clone());
|
||||
let query_interaction = Interaction::new(
|
||||
interaction.connection_index,
|
||||
InteractionType::Query(query.clone()),
|
||||
);
|
||||
|
||||
execute_interaction(env, connection_index, &query_interaction, stack)?;
|
||||
}
|
||||
Interaction::Assertion(_) => {
|
||||
InteractionType::Assertion(_) => {
|
||||
interaction.execute_assertion(stack, env)?;
|
||||
stack.clear();
|
||||
}
|
||||
Interaction::Assumption(_) => {
|
||||
InteractionType::Assumption(_) => {
|
||||
let assumption_result = interaction.execute_assumption(stack, env);
|
||||
stack.clear();
|
||||
|
||||
@@ -228,10 +231,10 @@ pub(crate) fn execute_interaction(
|
||||
return Ok(ExecutionContinuation::NextProperty);
|
||||
}
|
||||
}
|
||||
Interaction::Fault(_) => {
|
||||
InteractionType::Fault(_) => {
|
||||
interaction.execute_fault(env, connection_index)?;
|
||||
}
|
||||
Interaction::FaultyQuery(_) => {
|
||||
InteractionType::FaultyQuery(_) => {
|
||||
let conn = match &env.connections[connection_index] {
|
||||
SimConnection::LimboConnection(conn) => conn.clone(),
|
||||
SimConnection::SQLiteConnection(_) => unreachable!(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
SandboxedResult, SimulatorEnv,
|
||||
generation::{
|
||||
plan::{Interaction, InteractionPlan, Interactions, InteractionsType},
|
||||
plan::{InteractionPlan, InteractionType, Interactions, InteractionsType},
|
||||
property::Property,
|
||||
},
|
||||
model::Query,
|
||||
@@ -25,13 +25,13 @@ impl InteractionPlan {
|
||||
{
|
||||
let mut idx = failing_execution.secondary_index;
|
||||
loop {
|
||||
match &interactions[idx] {
|
||||
Interaction::Query(query) => {
|
||||
match &interactions[idx].interaction {
|
||||
InteractionType::Query(query) => {
|
||||
depending_tables = query.dependencies();
|
||||
break;
|
||||
}
|
||||
// Fault does not depend on
|
||||
Interaction::Fault(..) => break,
|
||||
InteractionType::Fault(..) => break,
|
||||
_ => {
|
||||
// In principle we should never fail this checked_sub.
|
||||
// But if there is a bug in how we count the secondary index
|
||||
@@ -156,9 +156,9 @@ impl InteractionPlan {
|
||||
{
|
||||
let mut idx = failing_execution.secondary_index;
|
||||
loop {
|
||||
match &interactions[idx] {
|
||||
match &interactions[idx].interaction {
|
||||
// Fault does not depend on
|
||||
Interaction::Fault(..) => break,
|
||||
InteractionType::Fault(..) => break,
|
||||
_ => {
|
||||
// In principle we should never fail this checked_sub.
|
||||
// But if there is a bug in how we count the secondary index
|
||||
|
||||
Reference in New Issue
Block a user