Skip to content

Commit daa77fe

Browse files
committed
add assumptions to the interactions, where a failing assumption stops
the execution of the current property and switches to the next one.
1 parent 2a4d461 commit daa77fe

File tree

2 files changed

+108
-18
lines changed

2 files changed

+108
-18
lines changed

simulator/generation/plan.rs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ impl Display for InteractionPlan {
5757

5858
match interaction {
5959
Interaction::Query(query) => writeln!(f, "{};", query)?,
60+
Interaction::Assumption(assumption) => {
61+
writeln!(f, "-- ASSUME: {};", assumption.message)?
62+
}
6063
Interaction::Assertion(assertion) => {
6164
writeln!(f, "-- ASSERT: {};", assertion.message)?
6265
}
@@ -93,6 +96,7 @@ impl Display for InteractionStats {
9396

9497
pub(crate) enum Interaction {
9598
Query(Query),
99+
Assumption(Assertion),
96100
Assertion(Assertion),
97101
Fault(Fault),
98102
}
@@ -101,13 +105,14 @@ impl Display for Interaction {
101105
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102106
match self {
103107
Self::Query(query) => write!(f, "{}", query),
108+
Self::Assumption(assumption) => write!(f, "ASSUME: {}", assumption.message),
104109
Self::Assertion(assertion) => write!(f, "ASSERT: {}", assertion.message),
105110
Self::Fault(fault) => write!(f, "FAULT: {}", fault),
106111
}
107112
}
108113
}
109114

110-
type AssertionFunc = dyn Fn(&Vec<ResultSet>) -> bool;
115+
type AssertionFunc = dyn Fn(&Vec<ResultSet>, &SimulatorEnv) -> bool;
111116

112117
pub(crate) struct Assertion {
113118
pub(crate) func: Box<AssertionFunc>,
@@ -148,6 +153,7 @@ impl Property {
148153
Query::Select(_) => {}
149154
},
150155
Interaction::Assertion(_) => {}
156+
Interaction::Assumption(_) => {}
151157
Interaction::Fault(_) => {}
152158
}
153159
}
@@ -180,6 +186,7 @@ impl InteractionPlan {
180186
Query::Create(_) => create += 1,
181187
},
182188
Interaction::Assertion(_) => {}
189+
Interaction::Assumption(_) => {}
183190
Interaction::Fault(_) => {}
184191
}
185192
}
@@ -285,31 +292,67 @@ impl Interaction {
285292
Self::Assertion(_) => {
286293
unreachable!("unexpected: this function should only be called on queries")
287294
}
288-
Interaction::Fault(_) => {
295+
Self::Assumption(_) => {
296+
unreachable!("unexpected: this function should only be called on queries")
297+
}
298+
Self::Fault(_) => {
289299
unreachable!("unexpected: this function should only be called on queries")
290300
}
291301
}
292302
}
293303

294-
pub(crate) fn execute_assertion(&self, stack: &Vec<ResultSet>) -> Result<()> {
304+
pub(crate) fn execute_assertion(
305+
&self,
306+
stack: &Vec<ResultSet>,
307+
env: &SimulatorEnv,
308+
) -> Result<()> {
295309
match self {
296310
Self::Query(_) => {
297311
unreachable!("unexpected: this function should only be called on assertions")
298312
}
299313
Self::Assertion(assertion) => {
300-
if !assertion.func.as_ref()(stack) {
314+
if !assertion.func.as_ref()(stack, env) {
301315
return Err(limbo_core::LimboError::InternalError(
302316
assertion.message.clone(),
303317
));
304318
}
305319
Ok(())
306320
}
321+
Self::Assumption(_) => {
322+
unreachable!("unexpected: this function should only be called on assertions")
323+
}
307324
Self::Fault(_) => {
308325
unreachable!("unexpected: this function should only be called on assertions")
309326
}
310327
}
311328
}
312329

330+
pub(crate) fn execute_assumption(
331+
&self,
332+
stack: &Vec<ResultSet>,
333+
env: &SimulatorEnv,
334+
) -> Result<()> {
335+
match self {
336+
Self::Query(_) => {
337+
unreachable!("unexpected: this function should only be called on assumptions")
338+
}
339+
Self::Assertion(_) => {
340+
unreachable!("unexpected: this function should only be called on assumptions")
341+
}
342+
Self::Assumption(assumption) => {
343+
if !assumption.func.as_ref()(stack, env) {
344+
return Err(limbo_core::LimboError::InternalError(
345+
assumption.message.clone(),
346+
));
347+
}
348+
Ok(())
349+
}
350+
Self::Fault(_) => {
351+
unreachable!("unexpected: this function should only be called on assumptions")
352+
}
353+
}
354+
}
355+
313356
pub(crate) fn execute_fault(&self, env: &mut SimulatorEnv, conn_index: usize) -> Result<()> {
314357
match self {
315358
Self::Query(_) => {
@@ -318,6 +361,9 @@ impl Interaction {
318361
Self::Assertion(_) => {
319362
unreachable!("unexpected: this function should only be called on faults")
320363
}
364+
Self::Assumption(_) => {
365+
unreachable!("unexpected: this function should only be called on faults")
366+
}
321367
Self::Fault(fault) => {
322368
match fault {
323369
Fault::Disconnect => {
@@ -358,6 +404,18 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
358404
row.push(value);
359405
}
360406
}
407+
408+
// Check that the table exists
409+
let assumption = Interaction::Assumption(Assertion {
410+
message: format!("table {} exists", table.name),
411+
func: Box::new({
412+
let table_name = table.name.clone();
413+
move |_: &Vec<ResultSet>, env: &SimulatorEnv| {
414+
env.tables.iter().any(|t| t.name == table_name)
415+
}
416+
}),
417+
});
418+
361419
// Insert the row
362420
let insert_query = Interaction::Query(Query::Insert(Insert {
363421
table: table.name.clone(),
@@ -379,7 +437,7 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
379437
column.name,
380438
value,
381439
),
382-
func: Box::new(move |stack: &Vec<ResultSet>| {
440+
func: Box::new(move |stack: &Vec<ResultSet>, _: &SimulatorEnv| {
383441
let rows = stack.last().unwrap();
384442
match rows {
385443
Ok(rows) => rows.iter().any(|r| r == &row),
@@ -390,7 +448,7 @@ fn property_insert_select<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Prop
390448

391449
Property::new(
392450
Some("select contains inserted value".to_string()),
393-
vec![insert_query, select_query, assertion],
451+
vec![assumption, insert_query, select_query, assertion],
394452
)
395453
}
396454

@@ -404,7 +462,7 @@ fn property_double_create_failure<R: rand::Rng>(rng: &mut R, _env: &SimulatorEnv
404462
message:
405463
"creating two tables with the name should result in a failure for the second query"
406464
.to_string(),
407-
func: Box::new(move |stack: &Vec<ResultSet>| {
465+
func: Box::new(move |stack: &Vec<ResultSet>, _: &SimulatorEnv| {
408466
let last = stack.last().unwrap();
409467
match last {
410468
Ok(_) => false,

simulator/main.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,15 +273,27 @@ fn execute_plan(
273273
env.connections[connection_index] = SimConnection::Connected(env.db.connect());
274274
} else {
275275
match execute_interaction(env, connection_index, interaction, &mut plan.stack) {
276-
Ok(_) => {
276+
Ok(next_execution) => {
277277
log::debug!("connection {} processed", connection_index);
278-
if plan.secondary_pointer + 1
279-
>= plan.plan[plan.interaction_pointer].interactions.len()
280-
{
281-
plan.interaction_pointer += 1;
282-
plan.secondary_pointer = 0;
283-
} else {
284-
plan.secondary_pointer += 1;
278+
// Move to the next interaction or property
279+
match next_execution {
280+
ExecutionContinuation::NextInteraction => {
281+
if plan.secondary_pointer + 1
282+
>= plan.plan[plan.interaction_pointer].interactions.len()
283+
{
284+
// If we have reached the end of the interactions for this property, move to the next property
285+
plan.interaction_pointer += 1;
286+
plan.secondary_pointer = 0;
287+
} else {
288+
// Otherwise, move to the next interaction
289+
plan.secondary_pointer += 1;
290+
}
291+
}
292+
ExecutionContinuation::NextProperty => {
293+
// Skip to the next property
294+
plan.interaction_pointer += 1;
295+
plan.secondary_pointer = 0;
296+
}
285297
}
286298
}
287299
Err(err) => {
@@ -294,12 +306,23 @@ fn execute_plan(
294306
Ok(())
295307
}
296308

309+
/// The next point of control flow after executing an interaction.
310+
/// `execute_interaction` uses this type in conjunction with a result, where
311+
/// the `Err` case indicates a full-stop due to a bug, and the `Ok` case
312+
/// indicates the next step in the plan.
313+
enum ExecutionContinuation {
314+
/// Default continuation, execute the next interaction.
315+
NextInteraction,
316+
/// Typically used in the case of preconditions failures, skip to the next property.
317+
NextProperty,
318+
}
319+
297320
fn execute_interaction(
298321
env: &mut SimulatorEnv,
299322
connection_index: usize,
300323
interaction: &Interaction,
301324
stack: &mut Vec<ResultSet>,
302-
) -> Result<()> {
325+
) -> Result<ExecutionContinuation> {
303326
log::info!("executing: {}", interaction);
304327
match interaction {
305328
generation::plan::Interaction::Query(_) => {
@@ -314,15 +337,24 @@ fn execute_interaction(
314337
stack.push(results);
315338
}
316339
generation::plan::Interaction::Assertion(_) => {
317-
interaction.execute_assertion(stack)?;
340+
interaction.execute_assertion(stack, env)?;
318341
stack.clear();
319342
}
343+
generation::plan::Interaction::Assumption(_) => {
344+
let assumption_result = interaction.execute_assumption(stack, env);
345+
stack.clear();
346+
347+
if assumption_result.is_err() {
348+
log::warn!("assumption failed: {:?}", assumption_result);
349+
return Ok(ExecutionContinuation::NextProperty);
350+
}
351+
}
320352
Interaction::Fault(_) => {
321353
interaction.execute_fault(env, connection_index)?;
322354
}
323355
}
324356

325-
Ok(())
357+
Ok(ExecutionContinuation::NextInteraction)
326358
}
327359

328360
fn compare_equal_rows(a: &[Vec<Value>], b: &[Vec<Value>]) {

0 commit comments

Comments
 (0)