Skip to content

Commit a8e1f2f

Browse files
timsauceralamb
andauthored
Chore/Add additional FFI unit tests (#14802)
* Add unit tests to FFI_ExecutionPlan * Add unit tests for FFI table source * Add round trip tests for volatility * Add unit tests for FFI insert op * Simplify string generation in unit test Co-authored-by: Andrew Lamb <[email protected]> * Fix drop of borrowed value --------- Co-authored-by: Andrew Lamb <[email protected]>
1 parent 3750dc9 commit a8e1f2f

File tree

4 files changed

+139
-7
lines changed

4 files changed

+139
-7
lines changed

datafusion/ffi/src/execution_plan.rs

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,17 @@ impl TryFrom<&FFI_ExecutionPlan> for ForeignExecutionPlan {
219219
let properties: PlanProperties = (plan.properties)(plan).try_into()?;
220220

221221
let children_rvec = (plan.children)(plan);
222-
let children: Result<Vec<_>> = children_rvec
222+
let children = children_rvec
223223
.iter()
224224
.map(ForeignExecutionPlan::try_from)
225225
.map(|child| child.map(|c| Arc::new(c) as Arc<dyn ExecutionPlan>))
226-
.collect();
226+
.collect::<Result<Vec<_>>>()?;
227227

228228
Ok(Self {
229229
name,
230230
plan: plan.clone(),
231231
properties,
232-
children: children?,
232+
children,
233233
})
234234
}
235235
}
@@ -281,6 +281,7 @@ impl ExecutionPlan for ForeignExecutionPlan {
281281

282282
#[cfg(test)]
283283
mod tests {
284+
use arrow::datatypes::{DataType, Field, Schema};
284285
use datafusion::{
285286
physical_plan::{
286287
execution_plan::{Boundedness, EmissionType},
@@ -294,6 +295,7 @@ mod tests {
294295
#[derive(Debug)]
295296
pub struct EmptyExec {
296297
props: PlanProperties,
298+
children: Vec<Arc<dyn ExecutionPlan>>,
297299
}
298300

299301
impl EmptyExec {
@@ -305,6 +307,7 @@ mod tests {
305307
EmissionType::Incremental,
306308
Boundedness::Bounded,
307309
),
310+
children: Vec::default(),
308311
}
309312
}
310313
}
@@ -333,14 +336,17 @@ mod tests {
333336
}
334337

335338
fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
336-
vec![]
339+
self.children.iter().collect()
337340
}
338341

339342
fn with_new_children(
340343
self: Arc<Self>,
341-
_: Vec<Arc<dyn ExecutionPlan>>,
344+
children: Vec<Arc<dyn ExecutionPlan>>,
342345
) -> Result<Arc<dyn ExecutionPlan>> {
343-
unimplemented!()
346+
Ok(Arc::new(EmptyExec {
347+
props: self.props.clone(),
348+
children,
349+
}))
344350
}
345351

346352
fn execute(
@@ -358,7 +364,6 @@ mod tests {
358364

359365
#[test]
360366
fn test_round_trip_ffi_execution_plan() -> Result<()> {
361-
use arrow::datatypes::{DataType, Field, Schema};
362367
let schema =
363368
Arc::new(Schema::new(vec![Field::new("a", DataType::Float32, false)]));
364369
let ctx = SessionContext::new();
@@ -372,6 +377,49 @@ mod tests {
372377

373378
assert!(original_name == foreign_plan.name());
374379

380+
let display = datafusion::physical_plan::display::DisplayableExecutionPlan::new(
381+
&foreign_plan,
382+
);
383+
384+
let buf = display.one_line().to_string();
385+
assert_eq!(buf.trim(), "FFI_ExecutionPlan(number_of_children=0)");
386+
387+
Ok(())
388+
}
389+
390+
#[test]
391+
fn test_ffi_execution_plan_children() -> Result<()> {
392+
let schema =
393+
Arc::new(Schema::new(vec![Field::new("a", DataType::Float32, false)]));
394+
let ctx = SessionContext::new();
395+
396+
// Version 1: Adding child to the foreign plan
397+
let child_plan = Arc::new(EmptyExec::new(Arc::clone(&schema)));
398+
let child_local = FFI_ExecutionPlan::new(child_plan, ctx.task_ctx(), None);
399+
let child_foreign = Arc::new(ForeignExecutionPlan::try_from(&child_local)?);
400+
401+
let parent_plan = Arc::new(EmptyExec::new(Arc::clone(&schema)));
402+
let parent_local = FFI_ExecutionPlan::new(parent_plan, ctx.task_ctx(), None);
403+
let parent_foreign = Arc::new(ForeignExecutionPlan::try_from(&parent_local)?);
404+
405+
assert_eq!(parent_foreign.children().len(), 0);
406+
assert_eq!(child_foreign.children().len(), 0);
407+
408+
let parent_foreign = parent_foreign.with_new_children(vec![child_foreign])?;
409+
assert_eq!(parent_foreign.children().len(), 1);
410+
411+
// Version 2: Adding child to the local plan
412+
let child_plan = Arc::new(EmptyExec::new(Arc::clone(&schema)));
413+
let child_local = FFI_ExecutionPlan::new(child_plan, ctx.task_ctx(), None);
414+
let child_foreign = Arc::new(ForeignExecutionPlan::try_from(&child_local)?);
415+
416+
let parent_plan = Arc::new(EmptyExec::new(Arc::clone(&schema)));
417+
let parent_plan = parent_plan.with_new_children(vec![child_foreign])?;
418+
let parent_local = FFI_ExecutionPlan::new(parent_plan, ctx.task_ctx(), None);
419+
let parent_foreign = Arc::new(ForeignExecutionPlan::try_from(&parent_local)?);
420+
421+
assert_eq!(parent_foreign.children().len(), 1);
422+
375423
Ok(())
376424
}
377425
}

datafusion/ffi/src/insert_op.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,26 @@ impl From<InsertOp> for FFI_InsertOp {
4747
}
4848
}
4949
}
50+
51+
#[cfg(test)]
52+
mod tests {
53+
use datafusion::logical_expr::dml::InsertOp;
54+
55+
use super::FFI_InsertOp;
56+
57+
fn test_round_trip_insert_op(insert_op: InsertOp) {
58+
let ffi_insert_op: FFI_InsertOp = insert_op.into();
59+
let round_trip: InsertOp = ffi_insert_op.into();
60+
61+
assert_eq!(insert_op, round_trip);
62+
}
63+
64+
/// This test ensures we have not accidentally mapped the FFI
65+
/// enums to the wrong internal enums values.
66+
#[test]
67+
fn test_all_round_trip_insert_ops() {
68+
test_round_trip_insert_op(InsertOp::Append);
69+
test_round_trip_insert_op(InsertOp::Overwrite);
70+
test_round_trip_insert_op(InsertOp::Replace);
71+
}
72+
}

datafusion/ffi/src/table_source.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,43 @@ impl From<TableType> for FFI_TableType {
8585
}
8686
}
8787
}
88+
89+
#[cfg(test)]
90+
mod tests {
91+
use super::*;
92+
use datafusion::error::Result;
93+
94+
fn round_trip_filter_pushdown(pushdown: TableProviderFilterPushDown) -> Result<()> {
95+
let ffi_pushdown: FFI_TableProviderFilterPushDown = (&pushdown).into();
96+
let round_trip: TableProviderFilterPushDown = (&ffi_pushdown).into();
97+
98+
assert_eq!(pushdown, round_trip);
99+
Ok(())
100+
}
101+
102+
#[test]
103+
fn round_trip_all_filter_pushdowns() -> Result<()> {
104+
round_trip_filter_pushdown(TableProviderFilterPushDown::Exact)?;
105+
round_trip_filter_pushdown(TableProviderFilterPushDown::Inexact)?;
106+
round_trip_filter_pushdown(TableProviderFilterPushDown::Unsupported)?;
107+
108+
Ok(())
109+
}
110+
111+
fn round_trip_table_type(table_type: TableType) -> Result<()> {
112+
let ffi_type: FFI_TableType = table_type.into();
113+
let round_trip_type: TableType = ffi_type.into();
114+
115+
assert_eq!(table_type, round_trip_type);
116+
Ok(())
117+
}
118+
119+
#[test]
120+
fn test_round_all_trip_table_type() -> Result<()> {
121+
round_trip_table_type(TableType::Base)?;
122+
round_trip_table_type(TableType::Temporary)?;
123+
round_trip_table_type(TableType::View)?;
124+
125+
Ok(())
126+
}
127+
}

datafusion/ffi/src/volatility.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,24 @@ impl From<&FFI_Volatility> for Volatility {
4646
}
4747
}
4848
}
49+
50+
#[cfg(test)]
51+
mod tests {
52+
use datafusion::logical_expr::Volatility;
53+
54+
use super::FFI_Volatility;
55+
56+
fn test_round_trip_volatility(volatility: Volatility) {
57+
let ffi_volatility: FFI_Volatility = volatility.into();
58+
let round_trip: Volatility = (&ffi_volatility).into();
59+
60+
assert_eq!(volatility, round_trip);
61+
}
62+
63+
#[test]
64+
fn test_all_round_trip_volatility() {
65+
test_round_trip_volatility(Volatility::Immutable);
66+
test_round_trip_volatility(Volatility::Stable);
67+
test_round_trip_volatility(Volatility::Volatile);
68+
}
69+
}

0 commit comments

Comments
 (0)