-
Notifications
You must be signed in to change notification settings - Fork 299
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
centralize Rust integration and regression tests
- Loading branch information
sonhmai
committed
Jan 20, 2025
1 parent
9369f06
commit c530885
Showing
16 changed files
with
735 additions
and
718 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
Currently the best way to run these tests are like this due to long running tests: | ||
|
||
Integration and regression test suite. | ||
|
||
```bash | ||
|
||
# run all tests | ||
cargo test | ||
|
||
# run individual test | ||
cargo test test_sequential_write -- --nocapture | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use limbo_core::{CheckpointStatus, Connection, Database, IO}; | ||
use std::path::PathBuf; | ||
use std::rc::Rc; | ||
use std::sync::Arc; | ||
use tempfile::TempDir; | ||
|
||
#[allow(dead_code)] | ||
pub struct TempDatabase { | ||
pub path: PathBuf, | ||
pub io: Arc<dyn IO>, | ||
} | ||
|
||
#[allow(dead_code, clippy::arc_with_non_send_sync)] | ||
impl TempDatabase { | ||
pub fn new(table_sql: &str) -> Self { | ||
let mut path = TempDir::new().unwrap().into_path(); | ||
path.push("test.db"); | ||
{ | ||
let connection = rusqlite::Connection::open(&path).unwrap(); | ||
connection | ||
.pragma_update(None, "journal_mode", "wal") | ||
.unwrap(); | ||
connection.execute(table_sql, ()).unwrap(); | ||
} | ||
let io: Arc<dyn limbo_core::IO> = Arc::new(limbo_core::PlatformIO::new().unwrap()); | ||
|
||
Self { path, io } | ||
} | ||
|
||
pub fn connect_limbo(&self) -> Rc<limbo_core::Connection> { | ||
log::debug!("conneting to limbo"); | ||
let db = Database::open_file(self.io.clone(), self.path.to_str().unwrap()).unwrap(); | ||
|
||
let conn = db.connect(); | ||
log::debug!("connected to limbo"); | ||
conn | ||
} | ||
} | ||
|
||
pub(crate) fn do_flush(conn: &Rc<Connection>, tmp_db: &TempDatabase) -> anyhow::Result<()> { | ||
loop { | ||
match conn.cacheflush()? { | ||
CheckpointStatus::Done => { | ||
break; | ||
} | ||
CheckpointStatus::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub(crate) fn compare_string(a: &String, b: &String) { | ||
assert_eq!(a.len(), b.len(), "Strings are not equal in size!"); | ||
let a = a.as_bytes(); | ||
let b = b.as_bytes(); | ||
|
||
let len = a.len(); | ||
for i in 0..len { | ||
if a[i] != b[i] { | ||
println!( | ||
"Bytes differ \n\t at index: dec -> {} hex -> {:#02x} \n\t values dec -> {}!={} hex -> {:#02x}!={:#02x}", | ||
i, i, a[i], b[i], a[i], b[i] | ||
); | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mod test_rowid_functions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use crate::common::{do_flush, TempDatabase}; | ||
use limbo_core::{StepResult, Value}; | ||
|
||
#[test] | ||
fn test_last_insert_rowid_basic() -> anyhow::Result<()> { | ||
let _ = env_logger::try_init(); | ||
let tmp_db = TempDatabase::new("CREATE TABLE test_rowid (id INTEGER PRIMARY KEY, val TEXT);"); | ||
let conn = tmp_db.connect_limbo(); | ||
|
||
// Simple insert | ||
let mut insert_query = conn.query("INSERT INTO test_rowid (id, val) VALUES (NULL, 'test1')")?; | ||
if let Some(ref mut rows) = insert_query { | ||
loop { | ||
match rows.next_row()? { | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Done => break, | ||
_ => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
// Check last_insert_rowid separately | ||
let mut select_query = conn.query("SELECT last_insert_rowid()")?; | ||
if let Some(ref mut rows) = select_query { | ||
loop { | ||
match rows.next_row()? { | ||
StepResult::Row(row) => { | ||
if let Value::Integer(id) = row.values[0] { | ||
assert_eq!(id, 1, "First insert should have rowid 1"); | ||
} | ||
} | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Interrupt => break, | ||
StepResult::Done => break, | ||
StepResult::Busy => panic!("Database is busy"), | ||
} | ||
} | ||
} | ||
|
||
// Test explicit rowid | ||
match conn.query("INSERT INTO test_rowid (id, val) VALUES (5, 'test2')") { | ||
Ok(Some(ref mut rows)) => loop { | ||
match rows.next_row()? { | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Done => break, | ||
_ => unreachable!(), | ||
} | ||
}, | ||
Ok(None) => {} | ||
Err(err) => eprintln!("{}", err), | ||
}; | ||
|
||
// Check last_insert_rowid after explicit id | ||
let mut last_id = 0; | ||
match conn.query("SELECT last_insert_rowid()") { | ||
Ok(Some(ref mut rows)) => loop { | ||
match rows.next_row()? { | ||
StepResult::Row(row) => { | ||
if let Value::Integer(id) = row.values[0] { | ||
last_id = id; | ||
} | ||
} | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Interrupt => break, | ||
StepResult::Done => break, | ||
StepResult::Busy => panic!("Database is busy"), | ||
} | ||
}, | ||
Ok(None) => {} | ||
Err(err) => eprintln!("{}", err), | ||
}; | ||
assert_eq!(last_id, 5, "Explicit insert should have rowid 5"); | ||
do_flush(&conn, &tmp_db)?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
mod test_journal_through_cli; | ||
mod test_wal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
use crate::common::{do_flush, TempDatabase}; | ||
use limbo_core::{Connection, StepResult, Value}; | ||
use log::debug; | ||
use std::rc::Rc; | ||
|
||
#[test] | ||
#[ignore] | ||
fn test_wal_checkpoint() -> anyhow::Result<()> { | ||
let _ = env_logger::try_init(); | ||
let tmp_db = TempDatabase::new("CREATE TABLE test (x INTEGER PRIMARY KEY);"); | ||
// threshold is 1000 by default | ||
let iterations = 1001_usize; | ||
let conn = tmp_db.connect_limbo(); | ||
|
||
for i in 0..iterations { | ||
let insert_query = format!("INSERT INTO test VALUES ({})", i); | ||
do_flush(&conn, &tmp_db)?; | ||
conn.checkpoint()?; | ||
match conn.query(insert_query) { | ||
Ok(Some(ref mut rows)) => loop { | ||
match rows.next_row()? { | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Done => break, | ||
_ => unreachable!(), | ||
} | ||
}, | ||
Ok(None) => {} | ||
Err(err) => { | ||
eprintln!("{}", err); | ||
} | ||
}; | ||
} | ||
|
||
do_flush(&conn, &tmp_db)?; | ||
conn.clear_page_cache()?; | ||
let list_query = "SELECT * FROM test LIMIT 1"; | ||
let mut current_index = 0; | ||
match conn.query(list_query) { | ||
Ok(Some(ref mut rows)) => loop { | ||
match rows.next_row()? { | ||
StepResult::Row(row) => { | ||
let first_value = &row.values[0]; | ||
let id = match first_value { | ||
Value::Integer(i) => *i as i32, | ||
Value::Float(f) => *f as i32, | ||
_ => unreachable!(), | ||
}; | ||
assert_eq!(current_index, id as usize); | ||
current_index += 1; | ||
} | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Interrupt => break, | ||
StepResult::Done => break, | ||
StepResult::Busy => unreachable!(), | ||
} | ||
}, | ||
Ok(None) => {} | ||
Err(err) => { | ||
eprintln!("{}", err); | ||
} | ||
} | ||
do_flush(&conn, &tmp_db)?; | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_wal_restart() -> anyhow::Result<()> { | ||
let _ = env_logger::try_init(); | ||
let tmp_db = TempDatabase::new("CREATE TABLE test (x INTEGER PRIMARY KEY);"); | ||
// threshold is 1000 by default | ||
|
||
fn insert(i: usize, conn: &Rc<Connection>, tmp_db: &TempDatabase) -> anyhow::Result<()> { | ||
debug!("inserting {}", i); | ||
let insert_query = format!("INSERT INTO test VALUES ({})", i); | ||
match conn.query(insert_query) { | ||
Ok(Some(ref mut rows)) => loop { | ||
match rows.next_row()? { | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Done => break, | ||
_ => unreachable!(), | ||
} | ||
}, | ||
Ok(None) => {} | ||
Err(err) => { | ||
eprintln!("{}", err); | ||
} | ||
}; | ||
debug!("inserted {}", i); | ||
tmp_db.io.run_once()?; | ||
Ok(()) | ||
} | ||
|
||
fn count(conn: &Rc<Connection>, tmp_db: &TempDatabase) -> anyhow::Result<usize> { | ||
debug!("counting"); | ||
let list_query = "SELECT count(x) FROM test"; | ||
loop { | ||
if let Some(ref mut rows) = conn.query(list_query)? { | ||
loop { | ||
match rows.next_row()? { | ||
StepResult::Row(row) => { | ||
let first_value = &row.values[0]; | ||
let count = match first_value { | ||
Value::Integer(i) => *i as i32, | ||
_ => unreachable!(), | ||
}; | ||
debug!("counted {}", count); | ||
return Ok(count as usize); | ||
} | ||
StepResult::IO => { | ||
tmp_db.io.run_once()?; | ||
} | ||
StepResult::Interrupt => break, | ||
StepResult::Done => break, | ||
StepResult::Busy => panic!("Database is busy"), | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
{ | ||
let conn = tmp_db.connect_limbo(); | ||
insert(1, &conn, &tmp_db)?; | ||
assert_eq!(count(&conn, &tmp_db)?, 1); | ||
conn.close()?; | ||
} | ||
{ | ||
let conn = tmp_db.connect_limbo(); | ||
assert_eq!( | ||
count(&conn, &tmp_db)?, | ||
1, | ||
"failed to read from wal from another connection" | ||
); | ||
conn.close()?; | ||
} | ||
Ok(()) | ||
} |
Oops, something went wrong.