Add more tests for variable and fixed size keys

next
Sayan Nandan 7 months ago
parent baaa36d336
commit 525dc46f86
No known key found for this signature in database
GPG Key ID: 0EBD769024B24F0A

@ -27,7 +27,7 @@
use { use {
crate::{ crate::{
engine::{ engine::{
core::{dml, model::Model, space::Space, EntityIDRef}, core::{dml, index::RowData, model::Model, space::Space, EntityID, EntityIDRef},
data::lit::Lit, data::lit::Lit,
error::QueryResult, error::QueryResult,
fractal::{test_utils::TestGlobal, GlobalInstanceLike}, fractal::{test_utils::TestGlobal, GlobalInstanceLike},
@ -44,15 +44,42 @@ use {
crossbeam_epoch::pin, crossbeam_epoch::pin,
}; };
fn create_model_and_space(global: &TestGlobal<VirtualFS>, create_model: &str) -> QueryResult<()> { const TEST_DATASET_SIZE: usize = 1000;
const TEST_UPDATE_DATASET_SIZE: usize = 8200; // this peculiar size to force the buffer to flush
fn create_test_kv_strings(change_count: usize) -> Vec<(String, String)> {
(1..=change_count)
.map(|i| {
(
format!("user-{i:0>change_count$}"),
format!("password-{i:0>change_count$}"),
)
})
.collect()
}
fn create_test_kv_int(change_count: usize) -> Vec<(u64, String)> {
(0..change_count)
.map(|i| (i as u64, format!("password-{i:0>change_count$}")))
.collect()
}
fn create_model_and_space(
global: &TestGlobal<VirtualFS>,
create_model: &str,
) -> QueryResult<EntityID> {
let tokens = lex_insecure(create_model.as_bytes()).unwrap(); let tokens = lex_insecure(create_model.as_bytes()).unwrap();
let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap(); let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap();
let mdl_name = EntityID::new(
create_model.model_name.space(),
create_model.model_name.entity(),
);
// first create space // first create space
let create_space_str = format!("create space {}", create_model.model_name.space()); let create_space_str = format!("create space {}", create_model.model_name.space());
let create_space_tokens = lex_insecure(create_space_str.as_bytes()).unwrap(); let create_space_tokens = lex_insecure(create_space_str.as_bytes()).unwrap();
let create_space: CreateSpace = ast::parse_ast_node_full(&create_space_tokens[2..]).unwrap(); let create_space: CreateSpace = ast::parse_ast_node_full(&create_space_tokens[2..]).unwrap();
Space::transactional_exec_create(global, create_space)?; Space::transactional_exec_create(global, create_space)?;
Model::transactional_exec_create(global, create_model).map(|_| ()) Model::transactional_exec_create(global, create_model).map(|_| mdl_name)
} }
fn run_insert(global: &TestGlobal<VirtualFS>, insert: &str) -> QueryResult<()> { fn run_insert(global: &TestGlobal<VirtualFS>, insert: &str) -> QueryResult<()> {
@ -67,17 +94,24 @@ fn run_update(global: &TestGlobal<VirtualFS>, update: &str) -> QueryResult<()> {
dml::update(global, insert) dml::update(global, insert)
} }
#[test] fn auto_hook<T>(msg: &str, f: impl Fn() -> T) -> T {
fn empty_model_data() { let hook = std::panic::take_hook();
test_utils::with_variable("empty_model_data", |log_name| { let decl_owned = msg.to_owned();
std::panic::set_hook(Box::new(move |pinfo| {
eprintln!("panic due to `{decl_owned}`: {pinfo}")
}));
let r = f();
std::panic::set_hook(hook);
r
}
fn create_and_close(log_name: &str, decl: &str) {
auto_hook(decl, || {
test_utils::with_variable(log_name, |log_name| {
// create and close // create and close
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_vfs_driver(log_name);
let _ = create_model_and_space( let _ = create_model_and_space(&global, decl).unwrap();
&global,
"create model milky_way.solar_system(planet_name: string, population: uint64)",
)
.unwrap();
} }
// open // open
{ {
@ -85,38 +119,27 @@ fn empty_model_data() {
drop(global); drop(global);
} }
}) })
}
fn create_test_kv(change_count: usize) -> Vec<(String, String)> {
(1..=change_count)
.map(|i| {
(
format!("user-{i:0>change_count$}"),
format!("password-{i:0>change_count$}"),
)
}) })
.collect()
} }
#[test] fn run_sample_inserts<K, V>(
fn model_data_inserts() { log_name: &str,
test_utils::with_variable(("model_data_inserts", 1000), |(log_name, change_count)| { decl: &str,
let key_values = create_test_kv(change_count); key_values: Vec<(K, V)>,
make_insert_query: impl Fn(&K, &V) -> String,
as_pk: for<'a> fn(&'a K) -> Lit<'a>,
check_row: impl Fn(&K, &V, &RowData),
) {
auto_hook(decl, || {
test_utils::with_variable(log_name, |log_name| {
// create, insert and close // create, insert and close
let mdl_name;
{ {
let mut global = TestGlobal::new_with_vfs_driver(log_name); let mut global = TestGlobal::new_with_vfs_driver(log_name);
global.set_max_data_pressure(change_count); global.set_max_data_pressure(key_values.len());
let _ = create_model_and_space( mdl_name = create_model_and_space(&global, decl).unwrap();
&global,
"create model apps.social(user_name: string, password: string)",
)
.unwrap();
for (username, password) in key_values.iter() { for (username, password) in key_values.iter() {
run_insert( run_insert(&global, &make_insert_query(username, password)).unwrap();
&global,
&format!("insert into apps.social('{username}', '{password}')"),
)
.unwrap();
} }
} }
// reopen and verify 100 times // reopen and verify 100 times
@ -125,54 +148,52 @@ fn model_data_inserts() {
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
global global
.state() .state()
.with_model(EntityIDRef::new("apps", "social"), |model| { .with_model(
EntityIDRef::new(mdl_name.space(), mdl_name.entity()),
|model| {
let g = pin(); let g = pin();
for (username, password) in key_values.iter() { for (username, password) in key_values.iter() {
assert_eq!( let row = model
model
.primary_index() .primary_index()
.select(Lit::new_str(username.as_str()), &g) .select(as_pk(username), &g)
.unwrap() .unwrap()
.d_data() .d_data()
.read() .read();
.fields() check_row(username, password, &row)
.get("password")
.unwrap()
.str(),
password.as_str()
)
} }
Ok(()) Ok(())
}) },
)
.unwrap() .unwrap()
}) })
}) })
})
} }
#[test] fn run_sample_updates<K, V>(
fn model_data_updates() { log_name: &str,
test_utils::with_variable(("model_data_updates", 8200), |(log_name, n)| { decl: &str,
let key_values = create_test_kv(n); key_values: Vec<(K, V)>,
make_insert_query: impl Fn(&K, &V) -> String,
make_update_query: impl Fn(&K, &V) -> String,
as_pk: for<'a> fn(&'a K) -> Lit<'a>,
check_row: impl Fn(&K, &V, &RowData),
) {
auto_hook(decl, || {
test_utils::with_variable((log_name, TEST_UPDATE_DATASET_SIZE), |(log_name, n)| {
/* /*
- we first open the log and then insert n values - we first open the log and then insert n values
- we then reopen the log 100 times, changing n / 100 values every time (we set the string to an empty one) - we then reopen the log 100 times, changing n / 100 values every time (we set the string to an empty one)
- we finally reopen the log and check if all the keys have empty string as the password - we finally reopen the log and check if all the keys have empty string as the password
*/ */
let mdl_name;
{ {
// insert n values // insert n values
let mut global = TestGlobal::new_with_vfs_driver(log_name); let mut global = TestGlobal::new_with_vfs_driver(log_name);
global.set_max_data_pressure(n); global.set_max_data_pressure(n);
let _ = create_model_and_space( mdl_name = create_model_and_space(&global, decl).unwrap();
&global,
"create model apps.social(user_name: string, password: string)",
)
.unwrap();
for (username, password) in key_values.iter() { for (username, password) in key_values.iter() {
run_insert( run_insert(&global, &make_insert_query(username, password)).unwrap();
&global,
&format!("insert into apps.social('{username}', '{password}')"),
)
.unwrap();
} }
} }
{ {
@ -188,14 +209,8 @@ fn model_data_updates() {
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
let mut j = 0; let mut j = 0;
for _ in 0..changes_per_cycle { for _ in 0..changes_per_cycle {
let (username, _) = &key_values[actual_position]; let (username, pass) = &key_values[actual_position];
run_update( run_update(&global, &make_update_query(username, pass)).unwrap();
&global,
&format!(
"update apps.social set password = '' where user_name = '{username}'"
),
)
.unwrap();
actual_position += 1; actual_position += 1;
j += 1; j += 1;
} }
@ -207,31 +222,101 @@ fn model_data_updates() {
{ {
let global = TestGlobal::new_with_vfs_driver(log_name); let global = TestGlobal::new_with_vfs_driver(log_name);
global.load_model_drivers().unwrap(); global.load_model_drivers().unwrap();
for (txn_id, (username, _)) in key_values for (txn_id, (username, password)) in key_values
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, x)| ((i + n) as u64, x)) .map(|(i, x)| ((i + n) as u64, x))
{ {
global global
.state() .state()
.with_model(EntityIDRef::new("apps", "social"), |model| { .with_model(
EntityIDRef::new(mdl_name.space(), mdl_name.entity()),
|model| {
let g = pin(); let g = pin();
let row = model let row = model
.primary_index() .primary_index()
.select(Lit::new_str(username.as_str()), &g) .select(as_pk(username), &g)
.unwrap() .unwrap()
.d_data() .d_data()
.read(); .read();
let pass = row.fields().get("password").unwrap().str(); check_row(username, password, &row);
assert!(
pass.is_empty(),
"failed for {username} because pass is {pass}",
);
assert_eq!(row.get_txn_revised().value_u64(), txn_id); assert_eq!(row.get_txn_revised().value_u64(), txn_id);
Ok(()) Ok(())
}) },
)
.unwrap(); .unwrap();
} }
} }
}) })
})
}
/*
test runs
*/
#[test]
fn empty_model_data() {
create_and_close(
"empty_model_data_variable_index_key",
"create model milky_way.solar_system(planet_name: string, population: uint64)",
);
create_and_close(
"empty_model_data_fixed_index_key",
"create model milky_way.solar_system(planet_id: uint64, population: uint64)",
);
}
#[test]
fn model_data_inserts() {
run_sample_inserts(
"model_data_inserts_variable_pk",
"create model apps.social(user_name: string, password: string)",
create_test_kv_strings(TEST_DATASET_SIZE),
|k, v| format!("insert into apps.social('{k}', '{v}')"),
|k| Lit::new_str(k),
|_, v, row| assert_eq!(row.fields().get("password").unwrap().str(), v),
);
run_sample_inserts(
"model_data_inserts_fixed_pk",
"create model apps.social(user_id: uint64, password: string)",
create_test_kv_int(TEST_DATASET_SIZE),
|k, v| format!("insert into apps.social({k}, '{v}')"),
|k| Lit::new_uint(*k),
|_, v, row| assert_eq!(row.fields().get("password").unwrap().str(), v),
)
}
#[test]
fn model_data_updates() {
run_sample_updates(
"model_data_updates_variable_key",
"create model apps.social(user_name: string, password: string)",
create_test_kv_strings(TEST_UPDATE_DATASET_SIZE),
|k, v| format!("insert into apps.social('{k}', '{v}')"),
|k, _| format!("update apps.social set password = '' where user_name = '{k}'"),
|k| Lit::new_str(k),
|username, _, row| {
let pass = row.fields().get("password").unwrap().str();
assert!(
pass.is_empty(),
"failed for {username} because pass is {pass}",
);
},
);
run_sample_updates(
"model_data_updates_fixed_key",
"create model apps.social(user_name: uint64, password: string)",
create_test_kv_int(TEST_UPDATE_DATASET_SIZE),
|k, v| format!("insert into apps.social({k}, '{v}')"),
|k, _| format!("update apps.social set password = '' where user_name = {k}"),
|k| Lit::new_uint(*k),
|username, _, row| {
let pass = row.fields().get("password").unwrap().str();
assert!(
pass.is_empty(),
"failed for {username} because pass is {pass}",
);
},
);
} }

Loading…
Cancel
Save