use oxirs_core::model::{Literal, NamedNode, Object, Predicate, Subject, Triple};
use oxirs_ttl::formats::turtle::TurtleSerializer;
#[test]
fn test_predicate_grouping() {
let subject = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let triple1 = Triple::new(
subject.clone(),
Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap()),
Object::Literal(Literal::new_simple_literal("Alice")),
);
let triple2 = Triple::new(
subject.clone(),
Predicate::NamedNode(NamedNode::new("http://example.org/age").unwrap()),
Object::Literal(Literal::new_typed_literal(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
);
let triple3 = Triple::new(
subject,
Predicate::NamedNode(NamedNode::new("http://example.org/email").unwrap()),
Object::Literal(Literal::new_simple_literal("alice@example.org")),
);
let triples = vec![triple1, triple2, triple3];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Optimized output:\n{}", result);
assert!(
result.contains(";"),
"Should use semicolon for predicate grouping"
);
let statement_dots = result.matches(" .\n").count()
+ result
.matches(" .")
.collect::<Vec<_>>()
.iter()
.filter(|_| result.ends_with(" ."))
.count();
assert!(
statement_dots == 1 || result.contains(" ."),
"Should have statement-ending dot"
);
}
#[test]
fn test_object_list_optimization() {
let subject = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let predicate = Predicate::NamedNode(NamedNode::new("http://example.org/knows").unwrap());
let triple1 = Triple::new(
subject.clone(),
predicate.clone(),
Object::NamedNode(NamedNode::new("http://example.org/bob").unwrap()),
);
let triple2 = Triple::new(
subject.clone(),
predicate.clone(),
Object::NamedNode(NamedNode::new("http://example.org/charlie").unwrap()),
);
let triple3 = Triple::new(
subject,
predicate,
Object::NamedNode(NamedNode::new("http://example.org/diana").unwrap()),
);
let triples = vec![triple1, triple2, triple3];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Optimized output:\n{}", result);
let comma_count = result.matches(',').count();
assert!(
comma_count >= 2,
"Should use comma for object list (found {} commas)",
comma_count
);
}
#[test]
fn test_combined_optimization() {
let alice = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let name_pred = Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap());
let age_pred = Predicate::NamedNode(NamedNode::new("http://example.org/age").unwrap());
let knows_pred = Predicate::NamedNode(NamedNode::new("http://example.org/knows").unwrap());
let triples = vec![
Triple::new(
alice.clone(),
name_pred,
Object::Literal(Literal::new_simple_literal("Alice")),
),
Triple::new(
alice.clone(),
age_pred,
Object::Literal(Literal::new_typed_literal(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
),
Triple::new(
alice.clone(),
knows_pred.clone(),
Object::NamedNode(NamedNode::new("http://example.org/bob").unwrap()),
),
Triple::new(
alice.clone(),
knows_pred.clone(),
Object::NamedNode(NamedNode::new("http://example.org/charlie").unwrap()),
),
Triple::new(
alice,
knows_pred,
Object::NamedNode(NamedNode::new("http://example.org/diana").unwrap()),
),
];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Combined optimization output:\n{}", result);
assert!(
result.contains(";"),
"Should use semicolons for predicate grouping"
);
assert!(result.contains(","), "Should use commas for object lists");
}
#[test]
fn test_multiple_subjects() {
let alice = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let bob = Subject::NamedNode(NamedNode::new("http://example.org/bob").unwrap());
let name_pred = Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap());
let age_pred = Predicate::NamedNode(NamedNode::new("http://example.org/age").unwrap());
let triples = vec![
Triple::new(
alice.clone(),
name_pred.clone(),
Object::Literal(Literal::new_simple_literal("Alice")),
),
Triple::new(
alice,
age_pred.clone(),
Object::Literal(Literal::new_typed_literal(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
),
Triple::new(
bob.clone(),
name_pred,
Object::Literal(Literal::new_simple_literal("Bob")),
),
Triple::new(
bob,
age_pred,
Object::Literal(Literal::new_typed_literal(
"25",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
),
];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Multiple subjects output:\n{}", result);
let statement_dots = result.matches(" .\n").count();
assert_eq!(
statement_dots, 2,
"Should have 2 statement-ending dots (one per subject group)"
);
}
#[test]
fn test_rdf_type_abbreviation() {
let subject = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let triple1 = Triple::new(
subject.clone(),
Predicate::NamedNode(
NamedNode::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").unwrap(),
),
Object::NamedNode(NamedNode::new("http://example.org/Person").unwrap()),
);
let triple2 = Triple::new(
subject,
Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap()),
Object::Literal(Literal::new_simple_literal("Alice")),
);
let triples = vec![triple1, triple2];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("RDF type abbreviation output:\n{}", result);
assert!(result.contains(" a "), "Should abbreviate rdf:type to 'a'");
}
#[test]
fn test_blank_nodes() {
use oxirs_core::model::BlankNode;
let subject = Subject::BlankNode(BlankNode::new("b1").unwrap());
let triple1 = Triple::new(
subject.clone(),
Predicate::NamedNode(NamedNode::new("http://example.org/prop1").unwrap()),
Object::Literal(Literal::new_simple_literal("value1")),
);
let triple2 = Triple::new(
subject,
Predicate::NamedNode(NamedNode::new("http://example.org/prop2").unwrap()),
Object::Literal(Literal::new_simple_literal("value2")),
);
let triples = vec![triple1, triple2];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Blank nodes output:\n{}", result);
assert!(result.contains("_:b1"), "Should contain blank node label");
assert!(
result.contains(";"),
"Should use semicolon for predicate grouping"
);
}
#[test]
fn test_round_trip_optimization() {
use oxirs_ttl::formats::turtle::TurtleParser;
let alice = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let name_pred = Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap());
let age_pred = Predicate::NamedNode(NamedNode::new("http://example.org/age").unwrap());
let triples = vec![
Triple::new(
alice.clone(),
name_pred,
Object::Literal(Literal::new_simple_literal("Alice")),
),
Triple::new(
alice,
age_pred,
Object::Literal(Literal::new_typed_literal(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
),
];
let serializer = TurtleSerializer::new();
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let turtle_str = String::from_utf8(output).unwrap();
println!("Serialized:\n{}", turtle_str);
let parser = TurtleParser::new();
let parsed_triples = parser.parse_document(&turtle_str).unwrap();
assert_eq!(
parsed_triples.len(),
triples.len(),
"Round-trip should preserve all triples"
);
}
#[test]
fn test_pretty_print_with_optimization() {
use oxirs_ttl::toolkit::SerializationConfig;
let alice = Subject::NamedNode(NamedNode::new("http://example.org/alice").unwrap());
let name_pred = Predicate::NamedNode(NamedNode::new("http://example.org/name").unwrap());
let age_pred = Predicate::NamedNode(NamedNode::new("http://example.org/age").unwrap());
let triples = vec![
Triple::new(
alice.clone(),
name_pred,
Object::Literal(Literal::new_simple_literal("Alice")),
),
Triple::new(
alice,
age_pred,
Object::Literal(Literal::new_typed_literal(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
)),
),
];
let config = SerializationConfig::default().with_pretty(true);
let serializer = TurtleSerializer::with_config(config);
let mut output = Vec::new();
serializer
.serialize_optimized(&triples, &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
println!("Pretty printed output:\n{}", result);
assert!(result.contains("\n"), "Should contain newlines");
}