create test for full urhg for builder parsing

This commit is contained in:
2023-11-06 13:13:38 +01:00
parent 1c4f798788
commit 3f2e4eed05
5 changed files with 5251 additions and 194 deletions

View File

@ -1,27 +1,23 @@
use log::{debug, error};
use std::{
borrow::BorrowMut,
fmt,
rc::{Rc, Weak},
sync::{Arc, Mutex},
};
use log::debug;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::overview;
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub(crate) struct Law {
name: String, //ABGB, UrhG
header: Vec<Heading>,
}
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Heading {
name: String, //1. Hauptstück; 3. Theil; ...
desc: Option<String>,
content: HeadingContent, // 1. Theil; 1. Subtheil; ...
}
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum HeadingContent {
Paragraph(Vec<Section>),
Heading(Vec<Box<Heading>>),
@ -97,6 +93,9 @@ pub(crate) struct LawBuilder {
/// Stores the header of the next paragraph
pub(crate) next_para_header: Option<String>,
#[cfg(test)]
pub(crate) history: Vec<String>,
}
impl LawBuilder {
@ -138,6 +137,8 @@ impl LawBuilder {
classifiers,
next_para_header: None,
last_header_index,
#[cfg(test)]
history: Vec::new(),
}
}
@ -177,6 +178,8 @@ impl LawBuilder {
classifiers,
next_para_header: None,
last_header_index: None,
#[cfg(test)]
history: Vec::new(),
};
overview::parse(law_id.unwrap()).unwrap();
@ -186,6 +189,8 @@ impl LawBuilder {
/// Sets a new header.
pub(crate) fn new_header(&mut self, name: &str) {
#[cfg(test)]
self.history.push(format!("New_header: {name}"));
debug!("new_header={name}");
let classifier_index = self
.classifiers
@ -238,6 +243,9 @@ impl LawBuilder {
/// Sets a new description for the last classifier.
pub(crate) fn new_desc(&mut self, desc: &str) {
#[cfg(test)]
self.history.push(format!("New desc: {desc}"));
debug!("new_desc={desc}");
if let Some(index) = self.last_header_index {
self.classifiers[index].set_desc(desc);
@ -248,6 +256,11 @@ impl LawBuilder {
/// Adds a new paragraph.
pub(crate) fn new_par(&mut self, par: String, content: Content) {
#[cfg(test)]
self.history.push(format!(
"New_par: {par};{}",
serde_json::to_string(&content).unwrap()
));
debug!("new_par=par:{par};content:{content:#?}");
if let Some(index) = self.last_header_index {
let section = Section {
@ -264,6 +277,8 @@ impl LawBuilder {
/// Next paragraph has a header, store its name.
pub(crate) fn new_next_para_header(&mut self, header: &str) {
#[cfg(test)]
self.history.push(format!("New_new_para_header: {header}"));
debug!("new_next_para_header={header}");
self.next_para_header = Some(header.into());
}
@ -285,7 +300,7 @@ impl LawBuilder {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Section {
pub(crate) symb: String, // §"1", §"2", ...
pub(crate) par_header: Option<String>,
@ -414,73 +429,132 @@ impl Classifier {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) enum Content {
Text(String), //This is my direct law text
Item(Vec<Box<Content>>), //(1) This is general law. (2) This is more specific law
List(Vec<Box<Content>>),
}
//#[cfg(test)]
//mod tests {
// use super::*;
//
// //#[test]
// //fn atest() {
// // let mut builder = LawBuilder::test("test");
// // builder.new_header("h11");
// // builder.new_par("§11".into(), Content::Text("hi".into()));
// // builder.new_header("h21");
// // builder.new_header("h31");
// // builder.new_par("§211".into(), Content::Text("hi".into()));
// // builder.new_par("§212".into(), Content::Text("hi".into()));
// // builder.new_header("h22");
// // builder.new_par("§22".into(), Content::Text("hi".into()));
//
// // let law: Law = builder.into();
//
// // println!("{law:#?}");
// // assert!(false);
// //}
//
// // #[test]
// // fn test() {
// // let mut builder = LawBuilder::test("UrhG");
// //
// // builder.new_header("1. Hauptstück");
// // builder.new_header("2. Abschnitt");
// //
// // builder.new_par(Content::Text("Mein erster Paragraph".into()));
// //
// // let expected = LawBuilder {
// // name: "UrhG".into(),
// // classifiers: vec![
// // Classifier {
// // name: "Hauptstück".into(),
// // parent: None,
// // instances: vec![ClassifierInstance {
// // name: "1. Hauptstück".into(),
// // desc: None,
// // content: vec![],
// // }],
// // },
// // Classifier {
// // name: "Abschnitt".into(),
// // parent: Some(Box::new(Classifier {
// // name: "Hauptstück".into(),
// // parent: None,
// // instances: vec![],
// // })),
// // instances: vec![ClassifierInstance {
// // name: "2. Abschnitt".into(),
// // desc: None,
// // content: vec![Content::Text("Mein erster Paragraph".into())],
// // }],
// // },
// // ],
// // next_para_header: None,
// // last_header_index: Some(1),
// // };
// // assert_eq!(builder, expected);
// // }
//}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use std::{
fs::{self, File},
io::{self, BufRead, Read},
path::Path,
};
use super::*;
//#[test]
//fn atest() {
// let mut builder = LawBuilder::test("test");
// builder.new_header("h11");
// builder.new_par("§11".into(), Content::Text("hi".into()));
// builder.new_header("h21");
// builder.new_header("h31");
// builder.new_par("§211".into(), Content::Text("hi".into()));
// builder.new_par("§212".into(), Content::Text("hi".into()));
// builder.new_header("h22");
// builder.new_par("§22".into(), Content::Text("hi".into()));
fn read_lines<P>(filename: P) -> io::Result<Vec<String>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
let buf_reader = io::BufReader::new(file);
buf_reader.lines().collect()
}
// let law: Law = builder.into();
#[test]
fn test_builder_full_urhg() {
let mut builder = LawBuilder::test("UrhG");
// println!("{law:#?}");
// assert!(false);
//}
let path = Path::new("./data/urhg/par");
let input = read_lines(path.join("../par.result")).unwrap();
// #[test]
// fn test() {
// let mut builder = LawBuilder::test("UrhG");
//
// builder.new_header("1. Hauptstück");
// builder.new_header("2. Abschnitt");
//
// builder.new_par(Content::Text("Mein erster Paragraph".into()));
//
// let expected = LawBuilder {
// name: "UrhG".into(),
// classifiers: vec![
// Classifier {
// name: "Hauptstück".into(),
// parent: None,
// instances: vec![ClassifierInstance {
// name: "1. Hauptstück".into(),
// desc: None,
// content: vec![],
// }],
// },
// Classifier {
// name: "Abschnitt".into(),
// parent: Some(Box::new(Classifier {
// name: "Hauptstück".into(),
// parent: None,
// instances: vec![],
// })),
// instances: vec![ClassifierInstance {
// name: "2. Abschnitt".into(),
// desc: None,
// content: vec![Content::Text("Mein erster Paragraph".into())],
// }],
// },
// ],
// next_para_header: None,
// last_header_index: Some(1),
// };
// assert_eq!(builder, expected);
// }
for i in input {
let (command, content) = i.split_once(":").unwrap();
match command {
"New_header" => builder.new_header(content),
"New desc" => builder.new_desc(content),
"New_new_para_header" => builder.new_next_para_header(content),
"New_par" => {
let (par, real_content) = i.split_once(";").unwrap();
let real_content: Content = serde_json::from_str(real_content).unwrap();
builder.new_par(par.into(), real_content);
}
_ => {
panic!("Don't know command '{command}'");
}
}
}
let actual: Law = builder.into();
//println!("{}", serde_json::to_string(&law).unwrap());
let mut file = File::open(path.join("../builder.result")).unwrap();
let mut json = String::new();
file.read_to_string(&mut json).unwrap();
let expected = serde_json::from_str(&json).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -55,3 +55,55 @@ pub(crate) fn parse_from_str(xml: &str, builder: &mut LawBuilder) -> Result<bool
Ok(continue_parsing)
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use std::{
fs::{self, File},
io::{self, BufRead, Read},
path::Path,
};
use super::*;
fn read_lines<P>(filename: P) -> io::Result<Vec<String>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
let buf_reader = io::BufReader::new(file);
buf_reader.lines().collect()
}
#[test]
fn test_par_full_urhg() {
let mut builder = LawBuilder::test("UrhG");
let path = Path::new("./data/urhg/par");
let mut entries: Vec<_> = fs::read_dir(path)
.unwrap()
.filter_map(|entry| entry.ok())
.collect();
entries.sort_by_key(|entry| entry.file_name());
for entry in entries {
println!("{entry:?}");
let mut file = File::open(path.join(entry.file_name())).unwrap();
let mut json = String::new();
file.read_to_string(&mut json).unwrap();
let cont = parse_from_str(&json, &mut builder).unwrap();
if !cont {
break;
}
}
let expected = read_lines(path.join("../par.result")).unwrap();
for (actual, expected) in builder.history.iter().zip(&expected) {
assert_eq!(actual, expected);
}
}
}

View File

@ -145,7 +145,7 @@ impl Abschnitt {
if Liste::test(child) {
let liste = Liste::parse(c.next().unwrap());
absatze.push(Content::List(vec![
Content::Text(absatz.content).into(),
Content::Text(absatz.content.replace("\u{a0}", " ")).into(),
liste.get_content().into(),
]));
} else if Table::test(child) {
@ -155,22 +155,22 @@ impl Abschnitt {
if Absatz::test_with_typ(child, "erltext") {
let after_absatz = Absatz::parse(c.next().unwrap());
absatze.push(Content::List(vec![
Content::Text(absatz.content).into(),
Content::Text(absatz.content.replace("\u{a0}", " ")).into(),
Content::List(table.get_list()).into(),
Content::Text(after_absatz.content).into(),
]))
} else {
absatze.push(Content::List(vec![
Content::Text(absatz.content).into(),
Content::Text(absatz.content.replace("\u{a0}", " ")).into(),
Content::List(table.get_list()).into(),
]));
}
}
} else {
absatze.push(Content::Text(absatz.content.clone()));
absatze.push(Content::Text(absatz.content.replace("\u{a0}", " ").clone()));
}
} else {
absatze.push(Content::Text(absatz.content.clone()));
absatze.push(Content::Text(absatz.content.replace("\u{a0}", " ").clone()));
}
//TODO: Continue here, (2) and (3) is somehow skipped
@ -187,14 +187,14 @@ impl Abschnitt {
if Liste::test(&child) {
let liste = Liste::parse(c.next().unwrap());
absatze.push(Content::List(vec![
Content::Text(abs.content).into(),
Content::Text(abs.content.replace("\u{a0}", " ")).into(),
liste.get_content().into(),
]));
} else {
absatze.push(Content::Text(abs.content));
absatze.push(Content::Text(abs.content.replace("\u{a0}", " ")));
}
} else {
absatze.push(Content::Text(abs.content));
absatze.push(Content::Text(abs.content.replace("\u{a0}", " ")));
}
continue;
}
@ -308,7 +308,12 @@ impl Ziffernliste {
let mut elems = Vec::new();
for elem in &self.listelems {
elems.push(Content::Text(format!("{} {}", elem.symbol.content, elem.text)).into());
elems.push(
Content::Text(
format!("{} {}", elem.symbol.content, elem.text).replace("\u{a0}", " "),
)
.into(),
);
}
Content::List(elems)
@ -380,7 +385,9 @@ impl Table {
txt.push_str(&format!("{} ", td.absatz.content));
}
ret.push(Box::new(Content::Text(format!("- {txt}",))));
ret.push(Box::new(Content::Text(
format!("- {txt}",).replace("\u{a0}", " "),
)));
}
ret
@ -429,7 +436,11 @@ impl Liste {
if Ziffernliste::test(child) {
content.push(Ziffernliste::parse(c.next().unwrap()).get_content().into());
} else if Schlussteil::test(child) {
content.push(Content::Text(Schlussteil::parse(c.next().unwrap()).content));
content.push(Content::Text(
Schlussteil::parse(c.next().unwrap())
.content
.replace("\u{a0}", " "),
));
} else {
break;
}
@ -586,119 +597,3 @@ impl Layoutdaten {
Self {}
}
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::Read, sync::Arc};
use log::error;
use crate::law::{contains, Classifier, ClassifierInstance, Section};
use super::*;
#[test]
fn deserialize_wucher1_success() {
let mut file = File::open("data/par/wucher1.xml").unwrap();
let mut xml = String::new();
file.read_to_string(&mut xml).unwrap();
let mut builder = LawBuilder::test("no-headers");
let risdok = Risdok::from_str(&xml, &mut builder);
if risdok.is_err() {
error!("{:#?}", risdok.as_ref().err());
}
assert!(risdok.is_ok());
let expected = LawBuilder {
name: "no-headers".into(),
classifiers: vec![
Classifier {
used_for_fn: Arc::new(&contains),
name: "".into(),
parent_index: None,
instances: vec![
ClassifierInstance {
name: "".into(),
desc: None,
sections: vec![
Section {
symb: "§ 1.".into(),
par_header: Some(
"Nichtigkeit eines wucherischen Vertrages.".into(),
),
content: Content::Text(
"Ein Vertrag ist nichtig, wenn jemand den Leichtsinn, die Zwangslage, Verstandesschwäche, Unerfahrenheit oder Gemütsaufregung eines anderen dadurch ausbeutet, daß er sich oder einem Dritten für eine Leistung eine Gegenleistung versprechen oder gewähren läßt, deren Vermögenswert zu dem Werte seiner Leistung in auffallendem Mißverhältnis steht.".into(),
),
},
],
parent: None,
idx: 0,
},
],
},
],
last_header_index: Some(
0,
),
next_para_header: None,
};
assert_eq!(builder, expected);
}
#[test]
fn deserialize_wucher2_success() {
let mut file = File::open("data/par/wucher7.xml").unwrap();
let mut xml = String::new();
file.read_to_string(&mut xml).unwrap();
let mut builder = LawBuilder::test("no-headers");
let risdok = Risdok::from_str(&xml, &mut builder);
if risdok.is_err() {
println!("{:#?}", risdok.as_ref().err());
}
assert!(risdok.is_ok());
let expected = LawBuilder {
name: "no-headers".into(),
classifiers: vec![
Classifier {
used_for_fn: Arc::new(&contains),
name: "".into(),
parent_index: None,
instances: vec![
ClassifierInstance {
name: "".into(),
desc: None,
sections: vec![
Section {
symb: "§ 7.".into(),
par_header: Some(
"Rechtsfolgen der Nichtigkeit eines wucherischen Vertrages.".into(),
),
content: Content::Item(
vec![
Box::new(Content::Text(
"(1) Ist ein Vertrag nach den vorstehenden Bestimmungen nichtig, so hat jeder der beiden Teile alles zurückzustellen, was er aus dem nichtigen Geschäfte zu seinem Vorteil erhalten hat. Insbesondere sind Geldzahlungen mit den gesetzlichen Zinsen vom Empfangstage zurückzuerstatten, die übergebenen Sachen zurückzustellen oder deren Wert zur Zeit des Empfanges zu ersetzen, die auf die Sache gemachten notwendigen und nützlichen Verwendungen zu ersetzen und für die Benützung und die Entwertung der Sache in der Zwischenzeit eine angemessene Vergütung zu leisten. Ergibt sich aus der Berechnung der beiderseitigen Ansprüche ein Mehranspruch für einen der Vertragsteile, so haftet hiefür die für den vertragsmäßigen Anspruch erworbene Sicherstellung.".into(),
)),
Box::new(Content::Text(
"(2) Ist jedoch die Gewährung oder Verlängerung von Kredit nach den vorstehenden Bestimmungen nichtig, so hat der Benachteiligte für den erhaltenen Kreditbetrag vom Empfangstag bis zur Rückzahlung sofern im Vertrag nicht eine geringere Verzinsung vorgesehen ist - Zinsen in der Höhe des Zweifachen des im Zeitpunkt der Schließung des Vertrags geltenden Basiszinssatzes zu vergüten. Er kann für die Rückzahlung des Erhaltenen die im Vertrag vorgesehenen Zahlungsfristen in Anspruch nehmen. Bestimmungen, nach denen der Benachteiligte in besonderen Fällen weitergehende Rechte hat, bleiben unberührt.".into(),
)),
],
),
},
],
parent: None,
idx: 0,
},
],
},
],
last_header_index: Some(
0,
),
next_para_header: None,
};
assert_eq!(builder, expected);
}
}