use std::time::Duration;
use datafusion::arrow::array::RecordBatch;
use datafusion::assert_batches_eq;
use datafusion::execution::context::SessionContext;
use datafusion_dft::tui::execution::ExecutionResultsBatch;
use datafusion_dft::tui::AppEvent;
use itertools::Itertools;
use crate::tui_cases::TestApp;
async fn create_batch(sql: &str) -> RecordBatch {
let ctx = SessionContext::new();
let df = ctx.sql(sql).await.unwrap();
let batches = df.collect().await.unwrap();
batches[0].clone()
}
async fn create_execution_results(query: &str) -> ExecutionResultsBatch {
let duration = Duration::from_secs(1);
let batch = create_batch(query).await;
ExecutionResultsBatch::new(query.to_string(), batch, duration)
}
fn create_values_query(num: usize) -> String {
let base = "SELECT * FROM VALUES";
let vals = (0..num).map(|i| format!("({i})")).join(",");
format!("{base} {vals}")
}
fn create_values_query_offset(num: usize, offset: usize) -> String {
let base = "SELECT * FROM VALUES";
let vals = (offset..offset + num).map(|i| format!("({i})")).join(",");
format!("{base} {vals}")
}
#[tokio::test]
async fn single_page() {
let mut test_app = TestApp::new().await;
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
let res1 = create_execution_results("SELECT 1").await;
let event1 = AppEvent::ExecutionResultsNextBatch(res1);
test_app.handle_app_event(event1).unwrap();
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 0);
let batch = state.sql_tab.current_page_results();
assert!(batch.is_some());
let batch = batch.unwrap();
let batches = vec![batch];
let expected = [
"+----------+",
"| Int64(1) |",
"+----------+",
"| 1 |",
"+----------+",
];
assert_batches_eq!(expected, &batches);
let table_state = state.sql_tab.query_results_state();
assert!(table_state.is_some());
let table_state = table_state.as_ref().unwrap();
assert_eq!(table_state.borrow().selected(), None);
}
#[tokio::test]
async fn multiple_pages_forward_and_back() {
let mut test_app = TestApp::new().await;
let query = create_values_query(101);
let res1 = create_execution_results(&query).await;
let event1 = AppEvent::ExecutionResultsNextBatch(res1);
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
test_app.handle_app_event(event1).unwrap();
{
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 0);
}
let event2 = AppEvent::ExecutionResultsNextPage;
test_app.handle_app_event(event2).unwrap();
{
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 1);
}
{
let state = test_app.state();
let batch = state.sql_tab.current_page_results();
assert!(batch.is_some());
let batch = batch.unwrap();
let batches = vec![batch];
let expected = [
"+---------+",
"| column1 |",
"+---------+",
"| 100 |",
"+---------+",
];
assert_batches_eq!(expected, &batches);
}
let left_key = ratatui::crossterm::event::KeyEvent::new(
ratatui::crossterm::event::KeyCode::Left,
ratatui::crossterm::event::KeyModifiers::NONE,
);
let event3 = AppEvent::Key(left_key);
test_app.handle_app_event(event3).unwrap();
{
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 0);
}
{
let state = test_app.state();
let batch = state.sql_tab.current_page_results();
assert!(batch.is_some());
let batch = batch.unwrap();
assert_eq!(batch.num_rows(), 100);
}
}
#[tokio::test]
async fn multiple_pages_forward_and_back_and_forward() {
let mut test_app = TestApp::new().await;
let query = create_values_query(101);
let res1 = create_execution_results(&query).await;
let event1 = AppEvent::ExecutionResultsNextBatch(res1);
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
test_app.handle_app_event(event1).unwrap();
{
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 0);
}
let event2 = AppEvent::ExecutionResultsNextPage;
test_app.handle_app_event(event2).unwrap();
let left_key = ratatui::crossterm::event::KeyEvent::new(
ratatui::crossterm::event::KeyCode::Left,
ratatui::crossterm::event::KeyModifiers::NONE,
);
let event3 = AppEvent::Key(left_key);
test_app.handle_app_event(event3).unwrap();
let event4 = AppEvent::ExecutionResultsNextPage;
test_app.handle_app_event(event4).unwrap();
{
let state = test_app.state();
let page = state.sql_tab.current_page().unwrap();
assert_eq!(page, 1);
}
{
let state = test_app.state();
let batch = state.sql_tab.current_page_results();
assert!(batch.is_some());
let batch = batch.unwrap();
let batches = vec![batch];
let expected = [
"+---------+",
"| column1 |",
"+---------+",
"| 100 |",
"+---------+",
];
assert_batches_eq!(expected, &batches);
}
}
#[tokio::test]
async fn multiple_batches_lazy_loading() {
let mut test_app = TestApp::new().await;
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
let batch1 = create_execution_results(&create_values_query(60)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch1))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 60);
}
let batch2 = create_execution_results(&create_values_query_offset(60, 60)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch2))
.unwrap();
{
let state = test_app.state();
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 100);
}
test_app
.handle_app_event(AppEvent::ExecutionResultsNextPage)
.unwrap();
let batch3 = create_execution_results(&create_values_query_offset(20, 120)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch3))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 1);
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 40);
}
}
#[tokio::test]
async fn multiple_small_batches_auto_load() {
let mut test_app = TestApp::new().await;
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
let batch1 = create_execution_results(&create_values_query(100)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch1))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
assert_eq!(state.sql_tab.total_loaded_rows(), 100);
}
let batch2 = create_execution_results(&create_values_query_offset(30, 100)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch2))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
assert_eq!(state.sql_tab.total_loaded_rows(), 130);
assert!(state.sql_tab.needs_more_batches_for_page(1));
}
let batch3 = create_execution_results(&create_values_query_offset(30, 130)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch3))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
assert_eq!(state.sql_tab.total_loaded_rows(), 160);
assert!(state.sql_tab.needs_more_batches_for_page(1));
}
let batch4 = create_execution_results(&create_values_query_offset(40, 160)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch4))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.total_loaded_rows(), 200);
assert!(!state.sql_tab.needs_more_batches_for_page(1));
}
test_app
.handle_app_event(AppEvent::ExecutionResultsNextPage)
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 1);
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 100); }
}
#[tokio::test]
async fn exact_batches_for_page() {
let mut test_app = TestApp::new().await;
test_app.handle_app_event(AppEvent::NewExecution).unwrap();
let batch1 = create_execution_results(&create_values_query(50)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch1))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
assert_eq!(state.sql_tab.total_loaded_rows(), 50);
}
let batch2 = create_execution_results(&create_values_query_offset(50, 50)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch2))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 0);
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 100);
assert_eq!(state.sql_tab.total_loaded_rows(), 100);
}
let batch3 = create_execution_results(&create_values_query_offset(50, 100)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch3))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.total_loaded_rows(), 150);
assert!(state.sql_tab.needs_more_batches_for_page(1));
}
let batch4 = create_execution_results(&create_values_query_offset(50, 150)).await;
test_app
.handle_app_event(AppEvent::ExecutionResultsNextBatch(batch4))
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.total_loaded_rows(), 200);
assert!(!state.sql_tab.needs_more_batches_for_page(1));
}
test_app
.handle_app_event(AppEvent::ExecutionResultsNextPage)
.unwrap();
{
let state = test_app.state();
assert_eq!(state.sql_tab.current_page().unwrap(), 1);
let page_results = state.sql_tab.current_page_results().unwrap();
assert_eq!(page_results.num_rows(), 100);
}
}