move tests

main
Ziyang Hu 2 years ago
parent 1eec1374ba
commit d57224aa87

@ -105,20 +105,4 @@ impl From<DataValue> for JsonValue {
}
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::data::json::JsonValue;
use crate::data::value::DataValue;
#[test]
fn bad_values() {
println!("{}", json!(f64::INFINITY));
println!("{}", JsonValue::from(DataValue::from(f64::INFINITY)));
println!("{}", JsonValue::from(DataValue::from(f64::NEG_INFINITY)));
println!("{}", JsonValue::from(DataValue::from(f64::NAN)));
}
}
}

@ -301,136 +301,3 @@ impl DataValue {
}
impl<T: Write> MemCmpEncoder for T {}
#[cfg(test)]
mod tests {
use smartstring::SmartString;
use uuid::Uuid;
use crate::data::memcmp::{decode_bytes, MemCmpEncoder};
use crate::data::value::{DataValue, Num, UuidWrapper};
#[test]
fn encode_decode_num() {
use rand::prelude::*;
let n = i64::MAX;
let mut collected = vec![];
let mut test_num = |n: Num| {
let mut encoder = vec![];
encoder.encode_num(n);
let (decoded, rest) = Num::decode_from_key(&encoder);
assert_eq!(decoded, n);
assert!(rest.is_empty());
collected.push(encoder);
};
for i in 0..54 {
for j in 0..1000 {
let vb = (n >> i) - j;
for v in [vb, -vb - 1] {
test_num(Num::Int(v));
}
}
}
test_num(Num::Float(f64::INFINITY));
test_num(Num::Float(f64::NEG_INFINITY));
test_num(Num::Float(f64::NAN));
for _ in 0..100000 {
let f = (thread_rng().gen::<f64>() - 0.5) * 2.0;
test_num(Num::Float(f));
test_num(Num::Float(1. / f));
}
let mut collected_copy = collected.clone();
collected.sort();
collected_copy.sort_by_key(|c| Num::decode_from_key(c).0);
assert_eq!(collected, collected_copy);
}
#[test]
fn test_encode_decode_uuid() {
let uuid = DataValue::Uuid(UuidWrapper(
Uuid::parse_str("dd85b19a-5fde-11ed-a88e-1774a7698039").unwrap(),
));
let mut encoder = vec![];
encoder.encode_datavalue(&uuid);
let (decoded, remaining) = DataValue::decode_from_key(&encoder);
assert_eq!(decoded, uuid);
assert!(remaining.is_empty());
}
#[test]
fn encode_decode_bytes() {
let target = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit...";
for i in 0..target.len() {
let bs = &target[i..];
let mut encoder: Vec<u8> = vec![];
encoder.encode_bytes(bs);
let (decoded, remaining) = decode_bytes(&encoder);
assert!(remaining.is_empty());
assert_eq!(bs, decoded);
let mut encoder: Vec<u8> = vec![];
encoder.encode_bytes(target);
encoder.encode_bytes(bs);
encoder.encode_bytes(bs);
encoder.encode_bytes(target);
let (decoded, remaining) = decode_bytes(&encoder);
assert_eq!(&target[..], decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(bs, decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(bs, decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(&target[..], decoded);
assert!(remaining.is_empty());
}
}
#[test]
fn specific_encode() {
let mut encoder = vec![];
encoder.encode_datavalue(&DataValue::from(2095));
// println!("e1 {:?}", encoder);
encoder.encode_datavalue(&DataValue::Str(SmartString::from("MSS")));
// println!("e2 {:?}", encoder);
let (a, remaining) = DataValue::decode_from_key(&encoder);
// println!("r {:?}", remaining);
let (b, remaining) = DataValue::decode_from_key(remaining);
assert!(remaining.is_empty());
assert_eq!(a, DataValue::from(2095));
assert_eq!(b, DataValue::Str(SmartString::from("MSS")));
}
#[test]
fn encode_decode_datavalues() {
let mut dv = vec![
DataValue::Null,
DataValue::Bool(false),
DataValue::Bool(true),
DataValue::from(1),
DataValue::from(1.0),
DataValue::from(i64::MAX),
DataValue::from(i64::MAX - 1),
DataValue::from(i64::MAX - 2),
DataValue::from(i64::MIN),
DataValue::from(i64::MIN + 1),
DataValue::from(i64::MIN + 2),
DataValue::from(f64::INFINITY),
DataValue::from(f64::NEG_INFINITY),
DataValue::List(vec![]),
];
dv.push(DataValue::List(dv.clone()));
dv.push(DataValue::List(dv.clone()));
let mut encoded = vec![];
let v = DataValue::List(dv);
encoded.encode_datavalue(&v);
let (decoded, remaining) = DataValue::decode_from_key(&encoded);
assert!(remaining.is_empty());
assert_eq!(decoded, v);
}
}

@ -200,6 +200,21 @@ impl InputInlineRulesOrFixed {
InputInlineRulesOrFixed::Fixed { fixed, .. } => fixed.span,
}
}
pub(crate) fn used_rule(&self, rule_name: &Symbol) -> bool {
match self {
InputInlineRulesOrFixed::Rules { rules, .. } => rules
.iter()
.any(|rule| rule.body.iter().any(|atom| atom.used_rule(rule_name))),
InputInlineRulesOrFixed::Fixed { fixed, .. } => fixed.rule_args.iter().any(|arg| {
if let FixedRuleArg::InMem { name, .. } = arg {
if name == rule_name {
return true;
}
}
false
}),
}
}
}
pub(crate) struct FixedRuleApply {
@ -503,6 +518,10 @@ struct EntryHeadNotExplicitlyDefinedError(#[label] SourceSpan);
pub(crate) struct NoEntryError;
impl InputProgram {
pub(crate) fn used_rule(&self, rule_name: &Symbol) -> bool {
self.prog.values().any(|rule| rule.used_rule(rule_name))
}
pub(crate) fn get_entry_arity(&self) -> Result<usize> {
if let Some(entry) = self.prog.get(&Symbol::new(PROG_ENTRY, SourceSpan(0, 0))) {
return match entry {
@ -950,6 +969,16 @@ impl Display for InputAtom {
}
impl InputAtom {
pub(crate) fn used_rule(&self, rule_name: &Symbol) -> bool {
match self {
InputAtom::Rule { inner } => inner.name == *rule_name,
InputAtom::Negation { inner, .. } => inner.used_rule(rule_name),
InputAtom::Conjunction { inner, .. } | InputAtom::Disjunction { inner, .. } => {
inner.iter().any(|a| a.used_rule(rule_name))
}
_ => false,
}
}
pub(crate) fn span(&self) -> SourceSpan {
match self {
InputAtom::Negation { span, .. }

@ -0,0 +1,21 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*
*/
use serde_json::json;
use crate::data::json::JsonValue;
use crate::data::value::DataValue;
#[test]
fn bad_values() {
println!("{}", json!(f64::INFINITY));
println!("{}", JsonValue::from(DataValue::from(f64::INFINITY)));
println!("{}", JsonValue::from(DataValue::from(f64::NEG_INFINITY)));
println!("{}", JsonValue::from(DataValue::from(f64::NAN)));
}

@ -0,0 +1,138 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*
*/
use smartstring::SmartString;
use uuid::Uuid;
use crate::data::memcmp::{decode_bytes, MemCmpEncoder};
use crate::data::value::{DataValue, Num, UuidWrapper};
#[test]
fn encode_decode_num() {
use rand::prelude::*;
let n = i64::MAX;
let mut collected = vec![];
let mut test_num = |n: Num| {
let mut encoder = vec![];
encoder.encode_num(n);
let (decoded, rest) = Num::decode_from_key(&encoder);
assert_eq!(decoded, n);
assert!(rest.is_empty());
collected.push(encoder);
};
for i in 0..54 {
for j in 0..1000 {
let vb = (n >> i) - j;
for v in [vb, -vb - 1] {
test_num(Num::Int(v));
}
}
}
test_num(Num::Float(f64::INFINITY));
test_num(Num::Float(f64::NEG_INFINITY));
test_num(Num::Float(f64::NAN));
for _ in 0..100000 {
let f = (thread_rng().gen::<f64>() - 0.5) * 2.0;
test_num(Num::Float(f));
test_num(Num::Float(1. / f));
}
let mut collected_copy = collected.clone();
collected.sort();
collected_copy.sort_by_key(|c| Num::decode_from_key(c).0);
assert_eq!(collected, collected_copy);
}
#[test]
fn test_encode_decode_uuid() {
let uuid = DataValue::Uuid(UuidWrapper(
Uuid::parse_str("dd85b19a-5fde-11ed-a88e-1774a7698039").unwrap(),
));
let mut encoder = vec![];
encoder.encode_datavalue(&uuid);
let (decoded, remaining) = DataValue::decode_from_key(&encoder);
assert_eq!(decoded, uuid);
assert!(remaining.is_empty());
}
#[test]
fn encode_decode_bytes() {
let target = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit...";
for i in 0..target.len() {
let bs = &target[i..];
let mut encoder: Vec<u8> = vec![];
encoder.encode_bytes(bs);
let (decoded, remaining) = decode_bytes(&encoder);
assert!(remaining.is_empty());
assert_eq!(bs, decoded);
let mut encoder: Vec<u8> = vec![];
encoder.encode_bytes(target);
encoder.encode_bytes(bs);
encoder.encode_bytes(bs);
encoder.encode_bytes(target);
let (decoded, remaining) = decode_bytes(&encoder);
assert_eq!(&target[..], decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(bs, decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(bs, decoded);
let (decoded, remaining) = decode_bytes(remaining);
assert_eq!(&target[..], decoded);
assert!(remaining.is_empty());
}
}
#[test]
fn specific_encode() {
let mut encoder = vec![];
encoder.encode_datavalue(&DataValue::from(2095));
// println!("e1 {:?}", encoder);
encoder.encode_datavalue(&DataValue::Str(SmartString::from("MSS")));
// println!("e2 {:?}", encoder);
let (a, remaining) = DataValue::decode_from_key(&encoder);
// println!("r {:?}", remaining);
let (b, remaining) = DataValue::decode_from_key(remaining);
assert!(remaining.is_empty());
assert_eq!(a, DataValue::from(2095));
assert_eq!(b, DataValue::Str(SmartString::from("MSS")));
}
#[test]
fn encode_decode_datavalues() {
let mut dv = vec![
DataValue::Null,
DataValue::Bool(false),
DataValue::Bool(true),
DataValue::from(1),
DataValue::from(1.0),
DataValue::from(i64::MAX),
DataValue::from(i64::MAX - 1),
DataValue::from(i64::MAX - 2),
DataValue::from(i64::MIN),
DataValue::from(i64::MIN + 1),
DataValue::from(i64::MIN + 2),
DataValue::from(f64::INFINITY),
DataValue::from(f64::NEG_INFINITY),
DataValue::List(vec![]),
];
dv.push(DataValue::List(dv.clone()));
dv.push(DataValue::List(dv.clone()));
let mut encoded = vec![];
let v = DataValue::List(dv);
encoded.encode_datavalue(&v);
let (decoded, remaining) = DataValue::decode_from_key(&encoded);
assert!(remaining.is_empty());
assert_eq!(decoded, v);
}

@ -8,4 +8,7 @@
mod functions;
mod aggrs;
mod validity;
mod validity;
mod json;
mod memcmp;
mod values;

@ -0,0 +1,59 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*
*/
use std::collections::{BTreeMap, HashMap};
use std::mem::size_of;
use smartstring::SmartString;
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
#[test]
fn show_size() {
dbg!(size_of::<DataValue>());
dbg!(size_of::<Symbol>());
dbg!(size_of::<String>());
dbg!(size_of::<HashMap<String, String>>());
dbg!(size_of::<BTreeMap<String, String>>());
}
#[test]
fn utf8() {
let c = char::from_u32(0x10FFFF).unwrap();
let mut s = String::new();
s.push(c);
println!("{}", s);
println!(
"{:b} {:b} {:b} {:b}",
s.as_bytes()[0],
s.as_bytes()[1],
s.as_bytes()[2],
s.as_bytes()[3]
);
dbg!(s);
}
#[test]
fn display_datavalues() {
println!("{}", DataValue::Null);
println!("{}", DataValue::Bool(true));
println!("{}", DataValue::from(-1));
println!("{}", DataValue::from(-1121212121.331212121));
println!("{}", DataValue::from(f64::NAN));
println!("{}", DataValue::from(f64::NEG_INFINITY));
println!(
"{}",
DataValue::List(vec![
DataValue::Bool(false),
DataValue::Str(SmartString::from(r###"abc"你"好'啊👌"###)),
DataValue::from(f64::NEG_INFINITY),
])
);
}

@ -337,57 +337,3 @@ impl DataValue {
}
pub(crate) const LARGEST_UTF_CHAR: char = '\u{10ffff}';
#[cfg(test)]
mod tests {
use std::collections::{BTreeMap, HashMap};
use std::mem::size_of;
use smartstring::SmartString;
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
#[test]
fn show_size() {
dbg!(size_of::<DataValue>());
dbg!(size_of::<Symbol>());
dbg!(size_of::<String>());
dbg!(size_of::<HashMap<String, String>>());
dbg!(size_of::<BTreeMap<String, String>>());
}
#[test]
fn utf8() {
let c = char::from_u32(0x10FFFF).unwrap();
let mut s = String::new();
s.push(c);
println!("{}", s);
println!(
"{:b} {:b} {:b} {:b}",
s.as_bytes()[0],
s.as_bytes()[1],
s.as_bytes()[2],
s.as_bytes()[3]
);
dbg!(s);
}
#[test]
fn display_datavalues() {
println!("{}", DataValue::Null);
println!("{}", DataValue::Bool(true));
println!("{}", DataValue::from(-1));
println!("{}", DataValue::from(-1121212121.331212121));
println!("{}", DataValue::from(f64::NAN));
println!("{}", DataValue::from(f64::NEG_INFINITY));
println!(
"{}",
DataValue::List(vec![
DataValue::Bool(false),
DataValue::Str(SmartString::from(r###"abc"你"好'啊👌"###)),
DataValue::from(f64::NEG_INFINITY),
])
);
}
}

@ -1215,8 +1215,11 @@ fn propagate_previous_results(
p: &mut InputProgram,
prev_results: &BTreeMap<Symbol, NamedRows>,
) -> Result<()> {
// OPTIMIZATION: insert only if needed
for (k, v) in prev_results {
if !p.used_rule(k) {
continue;
}
let replaced = p.prog.insert(
k.clone(),
InputInlineRulesOrFixed::Fixed {
@ -1261,331 +1264,3 @@ fn propagate_previous_results(
}
Ok(())
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use log::debug;
use serde_json::json;
use crate::new_cozo_mem;
#[test]
fn test_limit_offset() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[a] := a in [5,3,1,2,4] :limit 2", Default::default())
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([3, 5]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 1",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([1, 3]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 4",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([4]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 5",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([]));
}
#[test]
fn test_normal_aggr_empty() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[count(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(0)]]);
}
#[test]
fn test_meet_aggr_empty() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[min(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(null)]]);
let res = db
.run_script("?[min(a), count(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(null), json!(0)]]);
}
#[test]
fn test_layers() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
y[a] := a in [1,2,3]
x[sum(a)] := y[a]
x[sum(a)] := a in [4,5,6]
?[sum(a)] := x[a]
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res[0][0], json!(21.))
}
#[test]
fn test_conditions() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
db.run_script(
r#"
{
?[code] <- [['a'],['b'],['c']]
:create airport {code}
}
{
?[fr, to, dist] <- [['a', 'b', 1.1], ['a', 'c', 0.5], ['b', 'c', 9.1]]
:create route {fr, to => dist}
}
"#,
Default::default(),
)
.unwrap();
debug!("real test begins");
let res = db
.run_script(
r#"
r[code, dist] := *airport{code}, *route{fr: code, dist};
?[dist] := r['a', dist], dist > 0.5, dist <= 1.1;
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res[0][0], json!(1.1))
}
#[test]
fn test_classical() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
parent[] <- [['joseph', 'jakob'],
['jakob', 'issac'],
['issac', 'abraham']]
grandparent[gcld, gp] := parent[gcld, p], parent[p, gp]
?[who] := grandparent[who, 'abraham']
"#,
Default::default(),
)
.unwrap()
.rows;
println!("{:?}", res);
assert_eq!(res[0][0], json!("jakob"))
}
#[test]
fn default_columns() {
let db = new_cozo_mem().unwrap();
db.run_script(
r#"
:create status {uid: String, ts default now() => quitted: Bool, mood: String}
"#,
Default::default(),
)
.unwrap();
db.run_script(
r#"
?[uid, quitted, mood] <- [['z', true, 'x']]
:put status {uid => quitted, mood}
"#,
Default::default(),
)
.unwrap();
}
#[test]
fn rm_does_not_need_all_keys() {
let db = new_cozo_mem().unwrap();
db.run_script(":create status {uid => mood}", Default::default())
.unwrap();
assert!(db
.run_script(
"?[uid, mood] <- [[1, 2]] :put status {uid => mood}",
Default::default()
)
.is_ok());
assert!(db
.run_script(
"?[uid, mood] <- [[2]] :put status {uid}",
Default::default()
)
.is_err());
assert!(db
.run_script(
"?[uid, mood] <- [[3, 2]] :rm status {uid => mood}",
Default::default()
)
.is_ok());
assert!(db
.run_script("?[uid] <- [[1]] :rm status {uid}", Default::default())
.is_ok());
}
#[test]
fn strict_checks_for_fixed_rules_args() {
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[_, _])
"#,
Default::default(),
);
assert!(res.is_ok());
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[a, b])
"#,
Default::default(),
);
assert!(res.is_ok());
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[a, a])
"#,
Default::default(),
);
assert!(res.is_err());
}
#[test]
fn do_not_unify_underscore() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
r1[] <- [[1, 'a'], [2, 'b']]
r2[] <- [[2, 'B'], [3, 'C']]
?[l1, l2] := r1[_ , l1], r2[_ , l2]
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res.len(), 4);
let res = db.run_script(
r#"
?[_] := _ = 1
"#,
Default::default(),
);
assert!(res.is_err());
let res = db
.run_script(
r#"
?[x] := x = 1, _ = 1, _ = 2
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res.len(), 1);
}
#[test]
fn returning_relations() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
{
?[] <- [[1,2,3]]
:yield nxt
}
{
?[a,b,c] := nxt[a, b, c]
}
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(json!(res), json!([[1, 2, 3]]));
let res = db
.run_script(
r#"
{
?[a] <- [[1]]
:yield first_yield
}
{
?[a] := first_yield[b], a = b + 1
:yield second_yield
}
{
?[a] := first_yield[a]
?[a] := second_yield[a]
}
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(json!(res), json!([[1], [2]]));
let res = db.run_script(
r#"
{
?[] <- [[1,2,3]]
:yield nxt
}
{
nxt[] <- [[2, 3, 5]]
?[a,b,c] := nxt[a, b, c]
}
"#,
Default::default(),
);
assert!(res.is_err());
}
}

@ -10,3 +10,5 @@ pub(crate) mod db;
pub(crate) mod transact;
pub(crate) mod relation;
pub(crate) mod temp_store;
#[cfg(test)]
mod tests;

@ -499,49 +499,3 @@ pub(crate) struct InsufficientAccessLevel(
pub(crate) AccessLevel,
);
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::new_cozo_mem;
#[test]
fn test_trigger() {
let db = new_cozo_mem().unwrap();
db.run_script(":create friends {fr: Int, to: Int}", Default::default())
.unwrap();
db.run_script(":create friends.rev {to: Int, fr: Int}", Default::default())
.unwrap();
db.run_script(
r#"
::set_triggers friends
on put {
?[fr, to] := _new[fr, to]
:put friends.rev{ to, fr }
}
on rm {
?[fr, to] := _old[fr, to]
:rm friends.rev{ to, fr }
}
"#,
Default::default(),
)
.unwrap();
db.run_script(
r"?[fr, to] <- [[1,2]] :put friends {fr, to}",
Default::default(),
)
.unwrap();
let ret = db
.export_relations(["friends", "friends.rev"].into_iter())
.unwrap();
let frs = ret.get("friends").unwrap();
assert_eq!(vec![json!(1), json!(2)], frs.rows[0]);
let frs_rev = ret.get("friends.rev").unwrap();
assert_eq!(vec![json!(2), json!(1)], frs_rev.rows[0]);
}
}

@ -95,8 +95,6 @@ pub(crate) struct MeetAggrStore {
grouping_len: usize,
}
// optimization: MeetAggrStore can be used to simulate functional dependency
impl MeetAggrStore {
pub(crate) fn wrap(self) -> TempStore {
TempStore::MeetAggr(self)

@ -0,0 +1,373 @@
/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*
*/
use itertools::Itertools;
use log::debug;
use serde_json::json;
use crate::new_cozo_mem;
#[test]
fn test_limit_offset() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[a] := a in [5,3,1,2,4] :limit 2", Default::default())
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([3, 5]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 1",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([1, 3]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 4",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([4]));
let res = db
.run_script(
"?[a] := a in [5,3,1,2,4] :limit 2 :offset 5",
Default::default(),
)
.unwrap()
.rows
.into_iter()
.flatten()
.collect_vec();
assert_eq!(json!(res), json!([]));
}
#[test]
fn test_normal_aggr_empty() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[count(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(0)]]);
}
#[test]
fn test_meet_aggr_empty() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script("?[min(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(null)]]);
let res = db
.run_script("?[min(a), count(a)] := a in []", Default::default())
.unwrap()
.rows;
assert_eq!(res, vec![vec![json!(null), json!(0)]]);
}
#[test]
fn test_layers() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
y[a] := a in [1,2,3]
x[sum(a)] := y[a]
x[sum(a)] := a in [4,5,6]
?[sum(a)] := x[a]
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res[0][0], json!(21.))
}
#[test]
fn test_conditions() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
db.run_script(
r#"
{
?[code] <- [['a'],['b'],['c']]
:create airport {code}
}
{
?[fr, to, dist] <- [['a', 'b', 1.1], ['a', 'c', 0.5], ['b', 'c', 9.1]]
:create route {fr, to => dist}
}
"#,
Default::default(),
)
.unwrap();
debug!("real test begins");
let res = db
.run_script(
r#"
r[code, dist] := *airport{code}, *route{fr: code, dist};
?[dist] := r['a', dist], dist > 0.5, dist <= 1.1;
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res[0][0], json!(1.1))
}
#[test]
fn test_classical() {
let _ = env_logger::builder().is_test(true).try_init();
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
parent[] <- [['joseph', 'jakob'],
['jakob', 'issac'],
['issac', 'abraham']]
grandparent[gcld, gp] := parent[gcld, p], parent[p, gp]
?[who] := grandparent[who, 'abraham']
"#,
Default::default(),
)
.unwrap()
.rows;
println!("{:?}", res);
assert_eq!(res[0][0], json!("jakob"))
}
#[test]
fn default_columns() {
let db = new_cozo_mem().unwrap();
db.run_script(
r#"
:create status {uid: String, ts default now() => quitted: Bool, mood: String}
"#,
Default::default(),
)
.unwrap();
db.run_script(
r#"
?[uid, quitted, mood] <- [['z', true, 'x']]
:put status {uid => quitted, mood}
"#,
Default::default(),
)
.unwrap();
}
#[test]
fn rm_does_not_need_all_keys() {
let db = new_cozo_mem().unwrap();
db.run_script(":create status {uid => mood}", Default::default())
.unwrap();
assert!(db
.run_script(
"?[uid, mood] <- [[1, 2]] :put status {uid => mood}",
Default::default()
)
.is_ok());
assert!(db
.run_script(
"?[uid, mood] <- [[2]] :put status {uid}",
Default::default()
)
.is_err());
assert!(db
.run_script(
"?[uid, mood] <- [[3, 2]] :rm status {uid => mood}",
Default::default()
)
.is_ok());
assert!(db
.run_script("?[uid] <- [[1]] :rm status {uid}", Default::default())
.is_ok());
}
#[test]
fn strict_checks_for_fixed_rules_args() {
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[_, _])
"#,
Default::default(),
);
assert!(res.is_ok());
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[a, b])
"#,
Default::default(),
);
assert!(res.is_ok());
let db = new_cozo_mem().unwrap();
let res = db.run_script(
r#"
r[] <- [[1, 2]]
?[] <~ PageRank(r[a, a])
"#,
Default::default(),
);
assert!(res.is_err());
}
#[test]
fn do_not_unify_underscore() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
r1[] <- [[1, 'a'], [2, 'b']]
r2[] <- [[2, 'B'], [3, 'C']]
?[l1, l2] := r1[_ , l1], r2[_ , l2]
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res.len(), 4);
let res = db.run_script(
r#"
?[_] := _ = 1
"#,
Default::default(),
);
assert!(res.is_err());
let res = db
.run_script(
r#"
?[x] := x = 1, _ = 1, _ = 2
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(res.len(), 1);
}
#[test]
fn returning_relations() {
let db = new_cozo_mem().unwrap();
let res = db
.run_script(
r#"
{
?[] <- [[1,2,3]]
:yield nxt
}
{
?[a,b,c] := nxt[a, b, c]
}
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(json!(res), json!([[1, 2, 3]]));
let res = db
.run_script(
r#"
{
?[a] <- [[1]]
:yield first_yield
}
{
?[a] := first_yield[b], a = b + 1
:yield second_yield
}
{
?[a] := first_yield[a]
?[a] := second_yield[a]
}
"#,
Default::default(),
)
.unwrap()
.rows;
assert_eq!(json!(res), json!([[1], [2]]));
let res = db.run_script(
r#"
{
?[] <- [[1,2,3]]
:yield nxt
}
{
nxt[] <- [[2, 3, 5]]
?[a,b,c] := nxt[a, b, c]
}
"#,
Default::default(),
);
assert!(res.is_err());
}
#[test]
fn test_trigger() {
let db = new_cozo_mem().unwrap();
db.run_script(":create friends {fr: Int, to: Int}", Default::default())
.unwrap();
db.run_script(":create friends.rev {to: Int, fr: Int}", Default::default())
.unwrap();
db.run_script(
r#"
::set_triggers friends
on put {
?[fr, to] := _new[fr, to]
:put friends.rev{ to, fr }
}
on rm {
?[fr, to] := _old[fr, to]
:rm friends.rev{ to, fr }
}
"#,
Default::default(),
)
.unwrap();
db.run_script(
r"?[fr, to] <- [[1,2]] :put friends {fr, to}",
Default::default(),
)
.unwrap();
let ret = db
.export_relations(["friends", "friends.rev"].into_iter())
.unwrap();
let frs = ret.get("friends").unwrap();
assert_eq!(vec![json!(1), json!(2)], frs.rows[0]);
let frs_rev = ret.get("friends.rev").unwrap();
assert_eq!(vec![json!(2), json!(1)], frs_rev.rows[0]);
}
Loading…
Cancel
Save