Skip to content

Commit

Permalink
Add full roundtrip textdump export and re-import test
Browse files Browse the repository at this point in the history
Adds a test that:

1. loads JHCore into DB "a"
2. dumps same DB into a textdump
3. reloads that textdump into DB "b"
4. compares all properties and verbs of  "a" and "b"

Test passes, but is disabled by default because it is expensive.
  • Loading branch information
rdaum committed Jan 13, 2024
1 parent 844977b commit 43c6bbc
Showing 1 changed file with 139 additions and 22 deletions.
161 changes: 139 additions & 22 deletions crates/kernel/tests/textdump.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#[cfg(test)]
mod test {
use moor_compiler::opcode::Program;
use moor_db::loader::LoaderInterface;
use moor_db::odb::RelBoxWorldState;
use moor_db::Database;
use moor_kernel::textdump::{make_textdump, read_textdump, textdump_load, TextdumpReader};
use moor_values::model::defset::{HasUuid, Named};
use moor_values::model::r#match::VerbArgsSpec;
use moor_values::model::verbs::VerbFlag;
use moor_values::model::world_state::WorldStateSource;
use moor_values::model::CommitResult;
use moor_values::util::slice_ref::SliceRef;
use moor_values::var::objid::Objid;
use moor_values::SYSTEM_OBJECT;
use moor_values::{AsByteBuffer, SYSTEM_OBJECT};
use std::collections::BTreeSet;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::PathBuf;
Expand All @@ -29,14 +33,10 @@ mod test {
assert_eq!(tx.commit().await.unwrap(), CommitResult::Success);
}

async fn write_textdump(db: Arc<RelBoxWorldState>) -> String {
async fn write_textdump(db: Arc<RelBoxWorldState>, version: &str) -> String {
let tx = db.clone().loader_client().unwrap();
let mut output = Vec::new();
let textdump = make_textdump(
tx.clone(),
Some("** LambdaMOO Database, Format Version 4 **"),
)
.await;
let textdump = make_textdump(tx.clone(), Some(version)).await;

let mut writer = moor_kernel::textdump::TextdumpWriter::new(&mut output);
writer

This comment has been minimized.

Copy link
@nnunley

nnunley Jan 13, 2024

Collaborator

Is there a reason for this line?

This comment has been minimized.

Copy link
@rdaum

rdaum Jan 13, 2024

Author Owner

The diff isn't showing the bit below this, stupid github code review tool. If you expand it you'll see the use of the writer

Expand All @@ -57,7 +57,7 @@ mod test {
let td = tdr.read_textdump().expect("Failed to read textdump");

// Version spec
assert_eq!(td.version, "** LambdaMOO Database, Format Version 4 **");
assert_eq!(td.version, "** LambdaMOO Database, Format Version 1 **");

// Minimal DB has 1 user, #3,
assert_eq!(td.users, vec![Objid(3)]);
Expand Down Expand Up @@ -217,7 +217,7 @@ mod test {
let input = String::from_utf8(br.bytes().map(|b| b.unwrap()).collect())
.expect("Failed to convert input to string");

let output = write_textdump(db).await;
let output = write_textdump(db, "** LambdaMOO Database, Format Version 1 **").await;

assert_diff(&input, &output, "", 0);
}
Expand All @@ -231,24 +231,141 @@ mod test {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let minimal_db = manifest_dir.join("../../JHCore-DEV-2.db");

let textdump = {
let (db, _) = RelBoxWorldState::open(None, 1 << 34).await;
let db = Arc::new(db);
load_textdump_file(
db.clone().loader_client().unwrap(),
minimal_db.to_str().unwrap(),
)
.await;
let (db1, _) = RelBoxWorldState::open(None, 1 << 34).await;
let db1 = Arc::new(db1);
load_textdump_file(
db1.clone().loader_client().unwrap(),
minimal_db.to_str().unwrap(),
)
.await;

write_textdump(db).await
};
let textdump =
write_textdump(db1.clone(), "** LambdaMOO Database, Format Version 4 **").await;

// Now load that same core back in to a new DB, and hope we don't blow up anywhere.
let (db, _) = RelBoxWorldState::open(None, 1 << 34).await;
let db = Arc::new(db);
let (db2, _) = RelBoxWorldState::open(None, 1 << 34).await;
let db2 = Arc::new(db2);
let buffered_string_reader = std::io::BufReader::new(textdump.as_bytes());
let _ = read_textdump(db.clone().loader_client().unwrap(), buffered_string_reader)
let lc = db2.clone().loader_client().unwrap();
let _ = read_textdump(lc.clone(), buffered_string_reader)
.await
.unwrap();
assert_eq!(lc.commit().await.unwrap(), CommitResult::Success);

// Now go through the properties and verbs of all the objects on db1, and verify that
// they're the same on db2.
let tx1 = db1.loader_client().unwrap();
let tx2 = db2.loader_client().unwrap();
let objects1 = tx1.get_objects().await.unwrap();
let objects2 = tx2.get_objects().await.unwrap();
let objects1 = objects1.iter().collect::<BTreeSet<_>>();
let objects2 = objects2.iter().collect::<BTreeSet<_>>();
assert_eq!(objects1, objects2);

for o in objects1 {
// set of properties should be the same
let o1_props = tx1.get_object_properties(o).await.unwrap();
let o2_props = tx2.get_object_properties(o).await.unwrap();
let mut o1_props = o1_props.iter().collect::<Vec<_>>();
let mut o2_props = o2_props.iter().collect::<Vec<_>>();

o1_props.sort_by(|a, b| a.name().cmp(b.name()));
o2_props.sort_by(|a, b| a.name().cmp(b.name()));

// We want to do equality testing, but ignore the UUID which can be different
// between textdump loads...
assert_eq!(o1_props.len(), o2_props.len());
let zipped = o1_props.iter().zip(o2_props.iter());
for (i, prop) in zipped.enumerate() {
let (p1, p2) = prop;

assert_eq!(p1.name(), p2.name(), "{}.{}, name mismatch", o, p1.name(),);

assert_eq!(
p1.definer(),
p2.definer(),
"{}.{}, definer mismatch ({} != {})",
o,
p1.name(),
p1.definer(),
p2.definer()
);
// location
assert_eq!(
p1.location(),
p2.location(),
"{}.{}, location mismatch",
o,
p1.name()
);
assert_eq!(
p1.flags(),
p2.flags(),
"{}.{}, flags mismatch",
o,
p1.name(),
);
assert_eq!(
p1.owner(),
p2.owner(),
"{}.{}, owner mismatch",
o,
p1.name(),
);

let value1 = tx1.get_property_value(o, p1.uuid()).await.unwrap();
let value2 = tx2.get_property_value(o, p2.uuid()).await.unwrap();

assert_eq!(
value1,
value2,
"{}.{}, value mismatch ({}th value checked)",
o,
p1.name(),
i
);
}

// Now compare verbdefs
let o1_verbs = tx1.get_object_verbs(o).await.unwrap();
let o2_verbs = tx2.get_object_verbs(o).await.unwrap();
let o1_verbs = o1_verbs.iter().collect::<Vec<_>>();
let o2_verbs = o2_verbs.iter().collect::<Vec<_>>();

assert_eq!(o1_verbs.len(), o2_verbs.len());
for (v1, v2) in o1_verbs.iter().zip(o2_verbs.iter()) {
let v1_name = v1.names().join(" ").to_string();
assert_eq!(v1.names(), v2.names(), "{}:{}, name mismatch", o, v1_name);

assert_eq!(v1.owner(), v2.owner(), "{}:{}, owner mismatch", o, v1_name);
assert_eq!(v1.flags(), v2.flags(), "{}:{}, flags mismatch", o, v1_name);
assert_eq!(v1.args(), v2.args(), "{}:{}, args mismatch", o, v1_name);

// We want to actually decode and compare the opcode streams rather than
// the binary, so that we can make meaningful error reports.
let binary1 = tx1.get_verb_binary(o, v1.uuid()).await.unwrap();
let binary2 = tx2.get_verb_binary(o, v2.uuid()).await.unwrap();

let program1 = moor_compiler::decompile::program_to_tree(&Program::from_sliceref(
SliceRef::from_vec(binary1),
))
.unwrap();
let program2 = moor_compiler::decompile::program_to_tree(&Program::from_sliceref(
SliceRef::from_vec(binary2),
))
.unwrap();

assert_eq!(
program1.names, program2.names,
"{}:{}, variable names mismatch",
o, v1_name
);
assert_eq!(
program1.stmts, program2.stmts,
"{}:{}, statements mismatch",
o, v1_name
);
}
}
}
}

0 comments on commit 43c6bbc

Please sign in to comment.