use std::{collections::HashMap, hash::Hash};
use crate::*;
enum MergeAction {
MergeEqual,
MergeUnequal(Element),
AOnly,
BOnly(usize),
}
impl AutosarModel {
#[must_use]
pub fn new() -> AutosarModel {
let version = AutosarVersion::LATEST;
let xsi_schemalocation =
CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename()));
let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string());
let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string());
let root_attributes = smallvec::smallvec![
Attribute {
attrname: AttributeName::xsiSchemalocation,
content: xsi_schemalocation
},
Attribute {
attrname: AttributeName::xmlns,
content: xmlns
},
Attribute {
attrname: AttributeName::xmlnsXsi,
content: xmlns_xsi
},
];
let root_elem = ElementRaw {
parent: ElementOrModel::None,
elemname: ElementName::Autosar,
elemtype: ElementType::ROOT,
content: SmallVec::new(),
attributes: root_attributes,
file_membership: HashSet::with_capacity(0),
comment: None,
}
.wrap();
let model = AutosarModelRaw {
files: Vec::new(),
identifiables: FxIndexMap::default(),
reference_origins: FxHashMap::default(),
root_element: root_elem.clone(),
}
.wrap();
root_elem.set_parent(ElementOrModel::Model(model.downgrade()));
model
}
pub fn create_file<P: AsRef<Path>>(
&self,
filename: P,
version: AutosarVersion,
) -> Result<ArxmlFile, AutosarDataError> {
let mut data = self.0.write();
if data.files.iter().any(|af| af.filename() == filename.as_ref()) {
return Err(AutosarDataError::DuplicateFilenameError {
verb: "create",
filename: filename.as_ref().to_path_buf(),
});
}
let new_file = ArxmlFile::new(filename, version, self);
data.files.push(new_file.clone());
let _ = data.root_element.add_to_file_restricted(&new_file);
Ok(new_file)
}
pub fn load_buffer<P: AsRef<Path>>(
&self,
buffer: &[u8],
filename: P,
strict: bool,
) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
self.load_buffer_internal(buffer, filename.as_ref().to_path_buf(), strict)
}
fn load_buffer_internal(
&self,
buffer: &[u8],
filename: PathBuf,
strict: bool,
) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
if self.files().any(|file| file.filename() == filename) {
return Err(AutosarDataError::DuplicateFilenameError { verb: "load", filename });
}
let mut parser = ArxmlParser::new(filename.clone(), buffer, strict);
let root_element = parser.parse_arxml()?;
let version = parser.get_fileversion();
let arxml_file = ArxmlFileRaw {
version,
model: self.downgrade(),
filename: filename.clone(),
xml_standalone: parser.get_standalone(),
}
.wrap();
if self.0.read().files.is_empty() {
root_element.set_parent(ElementOrModel::Model(self.downgrade()));
root_element.0.write().file_membership.insert(arxml_file.downgrade());
self.0.write().root_element = root_element;
} else {
let result = self.merge_file_data(&root_element, arxml_file.downgrade());
if let Err(error) = result {
let _ = self.root_element().remove_from_file(&arxml_file);
return Err(error);
}
}
let mut data = self.0.write();
data.identifiables.reserve(parser.identifiables.len());
for (key, value) in parser.identifiables {
if let Some(existing_element) = data.identifiables.get(&key).and_then(WeakElement::upgrade) {
if let Some(new_element) = value.upgrade() {
if existing_element.element_name() != new_element.element_name() {
return Err(AutosarDataError::OverlappingDataError {
filename,
path: new_element.xml_path(),
});
}
}
} else {
data.identifiables.insert(key, value);
}
}
data.reference_origins.reserve(parser.references.len());
for (refpath, referring_element) in parser.references {
if let Some(xref) = data.reference_origins.get_mut(&refpath) {
xref.push(referring_element);
} else {
data.reference_origins.insert(refpath, vec![referring_element]);
}
}
data.files.push(arxml_file.clone());
Ok((arxml_file, parser.warnings))
}
fn merge_file_data(&self, new_root: &Element, new_file: WeakArxmlFile) -> Result<(), AutosarDataError> {
let root = self.root_element();
let files: HashSet<WeakArxmlFile> = self.files().map(|f| f.downgrade()).collect();
Self::merge_element(&root, &files, new_root, &new_file)?;
self.root_element().0.write().file_membership.insert(new_file);
Ok(())
}
fn merge_element(
parent_a: &Element,
files: &HashSet<WeakArxmlFile>,
parent_b: &Element,
new_file: &WeakArxmlFile,
) -> Result<(), AutosarDataError> {
let mut iter_a = parent_a.sub_elements().enumerate();
let mut iter_b = parent_b.sub_elements();
let mut item_a = iter_a.next();
let mut item_b = iter_b.next();
let mut elements_a_only = Vec::<Element>::new();
let mut elements_b_only = Vec::<(Element, usize)>::new();
let mut elements_merge = Vec::<(Element, Element)>::new();
let min_ver_a = files
.iter()
.filter_map(|weak| weak.upgrade().map(|f| f.version()))
.min()
.unwrap_or(AutosarVersion::LATEST);
let min_ver_b = new_file.upgrade().map_or(AutosarVersion::LATEST, |f| f.version());
let version = std::cmp::min(min_ver_a, min_ver_b);
let splitable = parent_a.element_type().splittable_in(version);
while let (Some((pos_a, elem_a)), Some(elem_b)) = (&item_a, &item_b) {
let merge_action = if elem_a.element_name() == elem_b.element_name() {
if elem_a.is_identifiable() {
Self::calc_identifiables_merge(parent_a, parent_b, elem_a, elem_b, splitable)?
} else {
Self::calc_element_merge(parent_b, elem_a, elem_b)
}
} else {
let parent_type = parent_a.element_type();
let (_, indices_a) = parent_type.find_sub_element(elem_a.element_name(), u32::MAX).unwrap();
let (_, indices_b) = parent_type.find_sub_element(elem_b.element_name(), u32::MAX).unwrap();
if indices_a < indices_b {
MergeAction::AOnly
} else {
MergeAction::BOnly(*pos_a)
}
};
match merge_action {
MergeAction::MergeEqual => {
elements_merge.push((elem_a.clone(), elem_b.clone()));
item_a = iter_a.next();
item_b = iter_b.next();
}
MergeAction::MergeUnequal(other_b) => {
elements_merge.push((elem_a.clone(), other_b));
item_a = iter_a.next();
}
MergeAction::AOnly => {
elements_a_only.push(elem_a.clone());
item_a = iter_a.next();
}
MergeAction::BOnly(position) => {
if !elements_merge.iter().any(|(_, merge_b)| merge_b == elem_b) {
elements_b_only.push((elem_b.clone(), position));
}
item_b = iter_b.next();
}
}
}
if let Some((_, elem_a)) = item_a {
elements_a_only.push(elem_a);
for (_, elem_a) in iter_a {
elements_a_only.push(elem_a);
}
}
if let Some(elem_b) = item_b {
let elem_count = parent_a.0.read().content.len();
if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
elements_b_only.push((elem_b, elem_count));
}
for elem_b in iter_b {
if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
elements_b_only.push((elem_b, elem_count));
}
}
}
for element in elements_a_only {
let mut elem_locked = element.0.write();
if elem_locked.file_membership.is_empty() {
files.clone_into(&mut elem_locked.file_membership);
}
}
Self::import_new_items(parent_a, elements_b_only, new_file, min_ver_b)?;
Self::merge_sub_elements(elements_merge, files, new_file)?;
Ok(())
}
fn calc_identifiables_merge(
parent_a: &Element,
parent_b: &Element,
elem_a: &Element,
elem_b: &Element,
splitable: bool,
) -> Result<MergeAction, AutosarDataError> {
Ok(if elem_a.item_name() == elem_b.item_name() {
MergeAction::MergeEqual
} else {
if let Some(sibling) = parent_b
.sub_elements()
.find(|e| e.element_name() == elem_a.element_name() && e.item_name() == elem_a.item_name())
{
MergeAction::MergeUnequal(sibling)
} else {
if splitable {
MergeAction::AOnly
} else {
return Err(AutosarDataError::InvalidFileMerge {
path: parent_a.xml_path(),
});
}
}
})
}
fn calc_element_merge(parent_b: &Element, elem_a: &Element, elem_b: &Element) -> MergeAction {
let defref_a = elem_a
.get_sub_element(ElementName::DefinitionRef)
.and_then(|dr| dr.character_data())
.and_then(|cdata| cdata.string_value());
let defref_b = elem_b
.get_sub_element(ElementName::DefinitionRef)
.and_then(|dr| dr.character_data())
.and_then(|cdata| cdata.string_value());
if defref_a == defref_b {
MergeAction::MergeEqual
} else {
if let Some(sibling) = parent_b
.sub_elements()
.filter(|e| e.element_name() == elem_a.element_name())
.find(|e| {
e.get_sub_element(ElementName::DefinitionRef)
.and_then(|dr| dr.character_data())
.and_then(|cdata| cdata.string_value())
== defref_a
})
{
MergeAction::MergeUnequal(sibling)
} else {
MergeAction::AOnly
}
}
}
fn import_new_items(
parent_a: &Element,
elements_b_only: Vec<(Element, usize)>,
new_file: &WeakArxmlFile,
min_ver_b: AutosarVersion,
) -> Result<(), AutosarDataError> {
let mut parent_a_locked = parent_a.0.write();
for (idx, (new_element, insert_pos)) in elements_b_only.into_iter().enumerate() {
new_element.set_parent(ElementOrModel::Element(parent_a.downgrade()));
new_element.0.write().file_membership.insert(new_file.clone());
let (first_pos, last_pos) = parent_a_locked
.calc_element_insert_range(new_element.element_name(), min_ver_b)
.map_err(|_| AutosarDataError::InvalidFileMerge {
path: new_element.element_name().to_string(),
})?;
let dest = insert_pos + idx;
let dest = dest.max(first_pos).min(last_pos);
parent_a_locked
.content
.insert(dest, ElementContent::Element(new_element));
}
Ok(())
}
fn merge_sub_elements(
elements_merge: Vec<(Element, Element)>,
files: &HashSet<WeakArxmlFile>,
new_file: &WeakArxmlFile,
) -> Result<(), AutosarDataError> {
for (elem_a, elem_b) in elements_merge {
let files = if !elem_a.0.read().file_membership.is_empty() {
elem_a.0.read().file_membership.clone()
} else {
files.clone()
};
AutosarModel::merge_element(&elem_a, &files, &elem_b, new_file)?;
let mut elem_a_locked = elem_a.0.write();
if !elem_a_locked.file_membership.is_empty() {
elem_a_locked.file_membership.insert(new_file.clone());
}
}
Ok(())
}
pub fn load_file<P: AsRef<Path>>(
&self,
filename: P,
strict: bool,
) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
let filename_buf = filename.as_ref().to_path_buf();
let buffer = std::fs::read(&filename_buf).map_err(|err| AutosarDataError::IoErrorRead {
filename: filename_buf.clone(),
ioerror: err,
})?;
self.load_buffer(&buffer, &filename_buf, strict)
}
pub fn remove_file(&self, file: &ArxmlFile) {
let mut locked_model = self.0.write();
let find_result = locked_model
.files
.iter()
.enumerate()
.find(|(_, f)| *f == file)
.map(|(pos, _)| pos);
if let Some(pos) = find_result {
locked_model.files.swap_remove(pos);
if locked_model.files.is_empty() {
locked_model.root_element.0.write().content.clear();
locked_model.root_element.set_file_membership(HashSet::new());
locked_model.identifiables.clear();
locked_model.reference_origins.clear();
} else {
drop(locked_model);
let _ = self.root_element().remove_from_file(file);
}
}
}
#[must_use]
pub fn serialize_files(&self) -> HashMap<PathBuf, String> {
let mut result = HashMap::new();
for file in self.files() {
if let Ok(data) = file.serialize() {
result.insert(file.filename(), data);
}
}
result
}
pub fn write(&self) -> Result<(), AutosarDataError> {
for (pathbuf, filedata) in self.serialize_files() {
std::fs::write(pathbuf.clone(), filedata).map_err(|err| AutosarDataError::IoErrorWrite {
filename: pathbuf,
ioerror: err,
})?;
}
Ok(())
}
#[must_use]
pub fn files(&self) -> ArxmlFileIterator {
ArxmlFileIterator::new(self.clone())
}
#[must_use]
pub fn root_element(&self) -> Element {
let locked_model = self.0.read();
locked_model.root_element.clone()
}
#[must_use]
pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
let model = self.0.read();
model.identifiables.get(path).and_then(WeakElement::upgrade)
}
pub fn duplicate(&self) -> Result<AutosarModel, AutosarDataError> {
let copy = Self::new();
let mut filemap = HashMap::new();
for orig_file in self.files() {
let filename = orig_file.filename();
let new_file = copy.create_file(filename.clone(), orig_file.version())?;
new_file.0.write().xml_standalone = orig_file.0.read().xml_standalone;
filemap.insert(filename, new_file.downgrade());
}
for element in self.root_element().sub_elements() {
copy.root_element().create_copied_sub_element(&element)?;
}
let orig_iter = self.elements_dfs();
let copy_iter = copy.elements_dfs();
let combined = std::iter::zip(orig_iter, copy_iter);
for ((_, orig_elem), (_, copy_elem)) in combined {
let mut locked_copy = copy_elem.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
locked_copy.file_membership.clear();
for orig_file in orig_elem.0.read().file_membership.iter().filter_map(|w| w.upgrade()) {
if let Some(copy_file) = filemap.get(&orig_file.filename()) {
locked_copy.file_membership.insert(copy_file.clone());
}
}
}
Ok(copy)
}
#[must_use]
pub fn elements_dfs(&self) -> ElementsDfsIterator {
self.root_element().elements_dfs()
}
#[must_use]
pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ElementsDfsIterator {
self.root_element().elements_dfs_with_max_depth(max_depth)
}
pub fn sort(&self) {
self.root_element().sort();
}
#[must_use]
pub fn identifiable_elements(&self) -> IdentifiablesIterator {
IdentifiablesIterator::new(self)
}
#[must_use]
pub fn get_references_to(&self, target_path: &str) -> Vec<WeakElement> {
if let Some(origins) = self.0.read().reference_origins.get(target_path) {
origins.clone()
} else {
Vec::new()
}
}
#[must_use]
pub fn check_references(&self) -> Vec<WeakElement> {
let mut broken_refs = Vec::new();
let model = self.0.read();
for (path, element_list) in &model.reference_origins {
if let Some(target_elem_weak) = model.identifiables.get(path) {
if let Some(target_elem) = target_elem_weak.upgrade() {
for referring_elem_weak in element_list {
if let Some(referring_elem) = referring_elem_weak.upgrade() {
if let Some(CharacterData::Enum(dest_value)) =
referring_elem.attribute_value(AttributeName::Dest)
{
if target_elem.element_type().verify_reference_dest(dest_value) {
broken_refs.push(referring_elem_weak.clone());
}
} else {
broken_refs.push(referring_elem_weak.clone());
}
}
}
} else {
broken_refs.extend(element_list.iter().cloned());
}
} else {
broken_refs.extend(element_list.iter().cloned());
}
}
broken_refs
}
pub(crate) fn downgrade(&self) -> WeakAutosarModel {
WeakAutosarModel(Arc::downgrade(&self.0))
}
pub(crate) fn add_identifiable(&self, new_path: String, elem: WeakElement) {
let mut model = self.0.write();
model.identifiables.insert(new_path, elem);
}
pub(crate) fn fix_identifiables(&self, old_path: &str, new_path: &str) {
let mut model = self.0.write();
let keys: Vec<String> = model.identifiables.keys().cloned().collect();
for key in keys {
if let Some(suffix) = key.strip_prefix(old_path) {
if suffix.is_empty() || suffix.starts_with('/') {
let new_key = format!("{new_path}{suffix}");
if let Some(entry) = model.identifiables.swap_remove(&key) {
model.identifiables.insert(new_key, entry);
}
}
}
}
}
pub(crate) fn remove_identifiable(&self, path: &str) {
let mut model = self.0.write();
model.identifiables.swap_remove(path);
}
pub(crate) fn add_reference_origin(&self, new_ref: &str, origin: WeakElement) {
let mut data = self.0.write();
if let Some(referrer_list) = data.reference_origins.get_mut(new_ref) {
referrer_list.push(origin);
} else {
data.reference_origins.insert(new_ref.to_owned(), vec![origin]);
}
}
pub(crate) fn fix_reference_origins(&self, old_ref: &str, new_ref: &str, origin: WeakElement) {
if old_ref != new_ref {
let mut data = self.0.write();
let mut remove_list = false;
if let Some(referrer_list) = data.reference_origins.get_mut(old_ref) {
if let Some(index) = referrer_list.iter().position(|x| *x == origin) {
referrer_list.swap_remove(index);
remove_list = referrer_list.is_empty();
}
}
if remove_list {
data.reference_origins.remove(old_ref);
}
if let Some(referrer_list) = data.reference_origins.get_mut(new_ref) {
referrer_list.push(origin);
} else {
data.reference_origins.insert(new_ref.to_owned(), vec![origin]);
}
}
}
pub(crate) fn remove_reference_origin(&self, reference: &str, element: WeakElement) {
let mut data = self.0.write();
let mut count = 1;
if let Some(referrer_list) = data.reference_origins.get_mut(reference) {
if let Some(index) = referrer_list.iter().position(|x| *x == element) {
referrer_list.swap_remove(index);
}
count = referrer_list.len();
}
if count == 0 {
data.reference_origins.remove(reference);
}
}
}
impl AutosarModelRaw {
pub(crate) fn set_version(&mut self, new_ver: AutosarVersion) {
let attribute_value = CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename()));
let _ = self.root_element.0.write().set_attribute_internal(
AttributeName::xsiSchemalocation,
attribute_value,
new_ver,
);
}
pub(crate) fn wrap(self) -> AutosarModel {
AutosarModel(Arc::new(RwLock::new(self)))
}
}
impl std::fmt::Debug for AutosarModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let model = self.0.read();
let rootelem = model.root_element.clone();
let mut dbgstruct = f.debug_struct("AutosarModel");
dbgstruct.field("root_element", &rootelem);
dbgstruct.field("files", &model.files);
dbgstruct.field("identifiables", &model.identifiables);
dbgstruct.field("reference_origins", &model.reference_origins);
dbgstruct.finish()
}
}
impl Default for AutosarModel {
fn default() -> Self {
Self::new()
}
}
impl PartialEq for AutosarModel {
fn eq(&self, other: &Self) -> bool {
Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
}
}
impl Eq for AutosarModel {}
impl Hash for AutosarModel {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_usize(Arc::as_ptr(&self.0) as usize);
}
}
impl WeakAutosarModel {
pub(crate) fn upgrade(&self) -> Option<AutosarModel> {
Weak::upgrade(&self.0).map(AutosarModel)
}
}
impl std::fmt::Debug for WeakAutosarModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("AutosarModel:WeakRef {:p}", Weak::as_ptr(&self.0)))
}
}
#[cfg(test)]
mod test {
use super::*;
use tempfile::tempdir;
#[test]
fn create_file() {
let model = AutosarModel::new();
let file = model.create_file("test", AutosarVersion::Autosar_00050);
assert!(file.is_ok());
let file = model.create_file("test", AutosarVersion::Autosar_00050);
assert!(file.is_err());
}
#[test]
fn load_buffer() {
const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE>
<SHORT-NAME>Pkg</SHORT-NAME>
<ELEMENTS>
<SYSTEM><SHORT-NAME>Thing</SHORT-NAME></SYSTEM>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>OtherPkg</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE>
<SHORT-NAME>Pkg</SHORT-NAME>
<ELEMENTS>
<APPLICATION-PRIMITIVE-DATA-TYPE><SHORT-NAME>Thing</SHORT-NAME></APPLICATION-PRIMITIVE-DATA-TYPE>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
const NON_ARXML: &str = "The quick brown fox jumps over the lazy dog";
let model = AutosarModel::new();
let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
assert!(result.is_ok());
let result = model.load_buffer(FILEBUF2.as_bytes(), "other", true);
assert!(result.is_ok());
let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
assert!(result.is_err());
let result = model.load_buffer(FILEBUF3.as_bytes(), "test2", true);
assert!(result.is_err());
let result = model.load_buffer(NON_ARXML.as_bytes(), "nonsense", true);
assert!(result.is_err());
}
#[test]
fn load_file() {
let dir = tempdir().unwrap();
let model = AutosarModel::new();
let filename = dir.path().with_file_name("nonexistent.arxml");
assert!(model.load_file(&filename, true).is_err());
let filename = dir.path().with_file_name("test.arxml");
model.create_file(&filename, AutosarVersion::LATEST).unwrap();
model
.root_element()
.create_sub_element(ElementName::ArPackages)
.and_then(|ap| ap.create_named_sub_element(ElementName::ArPackage, "Pkg"))
.unwrap();
model.write().unwrap();
assert!(filename.exists());
let model = AutosarModel::new();
model.load_file(&filename, true).unwrap();
let el_pkg = model.get_element_by_path("/Pkg");
assert!(el_pkg.is_some());
}
#[test]
fn data_merge() {
const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
<ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
<SHORT-NAME>BswModuleValues</SHORT-NAME>
<PARAMETER-VALUES>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
</ELEMENTS></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#.as_bytes();
const FILEBUF2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
<ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
<SHORT-NAME>BswModuleValues</SHORT-NAME>
<PARAMETER-VALUES>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
</ELEMENTS></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#.as_bytes();
let model = AutosarModel::new();
let (file1, _) = model.load_buffer(FILEBUF1, "test1", true).unwrap();
let file1_elemcount = file1.elements_dfs().count();
let (file2, _) = model.load_buffer(FILEBUF2, "test2", true).unwrap();
let file2_elemcount = file2.elements_dfs().count();
let model_elemcount = model.elements_dfs().count();
assert_eq!(file1_elemcount, model_elemcount);
assert!(file1_elemcount > file2_elemcount);
let (local, fileset) = model.root_element().file_membership().unwrap();
assert!(local);
assert_eq!(fileset.len(), 2);
let el_pkg_c = model.get_element_by_path("/Pkg_C").unwrap();
let (local, fileset) = el_pkg_c.file_membership().unwrap();
assert!(local);
assert_eq!(fileset.len(), 1);
let el_npv2 = model
.get_element_by_path("/Pkg_A/BswModule/BswModuleValues")
.and_then(|bmv| bmv.get_sub_element(ElementName::ParameterValues))
.and_then(|pv| pv.get_sub_element_at(2))
.unwrap();
let (loc, fm) = el_npv2.file_membership().unwrap();
assert!(loc);
assert_eq!(fm.len(), 1);
const ERRFILE1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
<ELEMENTS>
<SYSTEM-TIMING>
<SHORT-NAME>SystemTimings</SHORT-NAME>
<CATEGORY>CAT</CATEGORY>
<TIMING-RESOURCE>
<SHORT-NAME>Name_One</SHORT-NAME>
</TIMING-RESOURCE>
</SYSTEM-TIMING>
</ELEMENTS>
</AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
const ERRFILE2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
<ELEMENTS>
<SYSTEM-TIMING>
<SHORT-NAME>SystemTimings</SHORT-NAME>
<TIMING-RESOURCE>
<SHORT-NAME>Name_Two</SHORT-NAME>
</TIMING-RESOURCE>
</SYSTEM-TIMING>
</ELEMENTS>
</AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
let model = AutosarModel::new();
let result = model.load_buffer(ERRFILE1, "test1", true);
assert!(result.is_ok());
let result = model.load_buffer(ERRFILE2, "test2", true);
let error = result.unwrap_err();
assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
const ERRFILE3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
<ELEMENTS>
<COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
<COMPU-INTERNAL-TO-PHYS>
<COMPU-SCALES>
<COMPU-SCALE><COMPU-CONST></COMPU-CONST></COMPU-SCALE>
</COMPU-SCALES>
</COMPU-INTERNAL-TO-PHYS>
</COMPU-METHOD>
</ELEMENTS>
</AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
const ERRFILE4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
<ELEMENTS>
<COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
<COMPU-INTERNAL-TO-PHYS>
<COMPU-SCALES>
<COMPU-SCALE><COMPU-RATIONAL-COEFFS></COMPU-RATIONAL-COEFFS></COMPU-SCALE>
</COMPU-SCALES>
</COMPU-INTERNAL-TO-PHYS>
</COMPU-METHOD>
</ELEMENTS>
</AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
let model = AutosarModel::new();
let result = model.load_buffer(ERRFILE3, "test3", true);
assert!(result.is_ok());
let result = model.load_buffer(ERRFILE4, "test4", true);
let error = result.unwrap_err();
assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
const FILEBUF3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#.as_bytes();
const FILEBUF4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
</AR-PACKAGES></AUTOSAR>"#.as_bytes();
let model_a = AutosarModel::new();
model_a.load_buffer(FILEBUF3, "test5", true).unwrap();
model_a.load_buffer(FILEBUF4, "test6", true).unwrap();
let model_b = AutosarModel::new();
model_b.load_buffer(FILEBUF4, "test5", true).unwrap();
model_b.load_buffer(FILEBUF3, "test6", true).unwrap();
model_a.sort();
let model_a_txt = model_a.root_element().serialize();
model_b.sort();
let model_b_txt = model_b.root_element().serialize();
assert_eq!(model_a_txt, model_b_txt);
}
#[test]
fn remove_file() {
const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00049.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
<ELEMENTS><CAN-CLUSTER><SHORT-NAME>CAN_Cluster</SHORT-NAME></CAN-CLUSTER></ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME>
<ELEMENTS><SYSTEM><SHORT-NAME>System</SHORT-NAME>
<FIBEX-ELEMENTS><FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF DEST="CAN-CLUSTER">/Package/CAN_Cluster</FIBEX-ELEMENT-REF>
</FIBEX-ELEMENT-REF-CONDITIONAL></FIBEX-ELEMENTS>
</SYSTEM></ELEMENTS></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
let model = AutosarModel::new();
let (file, _) = model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
assert_eq!(model.files().count(), 1);
assert_eq!(model.identifiable_elements().count(), 1);
model.remove_file(&file);
assert_eq!(model.files().count(), 0);
assert_eq!(model.identifiable_elements().count(), 0);
let model = AutosarModel::new();
model.load_buffer(FILEBUF.as_bytes(), "test1", true).unwrap();
assert_eq!(model.files().count(), 1);
let modeltxt_1 = model.root_element().serialize();
let (file2, _) = model.load_buffer(FILEBUF2.as_bytes(), "test2", true).unwrap();
assert_eq!(model.files().count(), 2);
let modeltxt_1_2 = model.root_element().serialize();
assert_ne!(modeltxt_1, modeltxt_1_2);
let (file3, _) = model.load_buffer(FILEBUF3.as_bytes(), "test3", true).unwrap();
assert_eq!(model.files().count(), 3);
let modeltxt_1_2_3 = model.root_element().serialize();
assert_ne!(modeltxt_1_2, modeltxt_1_2_3);
model.get_element_by_path("/Package2/System").unwrap();
model.remove_file(&file3);
let modeltxt_1_2_x = model.root_element().serialize();
assert_eq!(modeltxt_1_2, modeltxt_1_2_x);
model.remove_file(&file2);
let modeltxt_1_x_x = model.root_element().serialize();
assert_eq!(modeltxt_1, modeltxt_1_x_x);
assert_eq!(model.files().count(), 1);
}
#[test]
fn refcount() {
let model = AutosarModel::default();
let weak = model.downgrade();
let project2 = weak.upgrade();
assert_eq!(Arc::strong_count(&model.0), 2);
assert_eq!(model, project2.unwrap());
}
#[test]
fn identifiables_iterator() {
const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>OuterPackage1</SHORT-NAME>
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES>
</AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>OuterPackage2</SHORT-NAME>
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES>
</AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
let model = AutosarModel::new();
model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
let mut identifiable_elements = model.identifiable_elements().collect::<Vec<_>>();
identifiable_elements.sort_by(|a, b| a.0.cmp(&b.0));
assert_eq!(identifiable_elements[0].0, "/OuterPackage1");
assert_eq!(identifiable_elements[1].0, "/OuterPackage1/InnerPackage1");
assert_eq!(identifiable_elements[2].0, "/OuterPackage1/InnerPackage2");
assert_eq!(identifiable_elements[3].0, "/OuterPackage2");
assert_eq!(identifiable_elements[4].0, "/OuterPackage2/InnerPackage1");
assert_eq!(identifiable_elements[5].0, "/OuterPackage2/InnerPackage2");
}
#[test]
fn check_references() {
const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
<ELEMENTS>
<SYSTEM><SHORT-NAME>System</SHORT-NAME>
<FIBEX-ELEMENTS>
<FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF DEST="ECU-INSTANCE">/Pkg/EcuInstance</FIBEX-ELEMENT-REF>
</FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/Some/Invalid/Path</FIBEX-ELEMENT-REF>
</FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF-CONDITIONAL>
<FIBEX-ELEMENT-REF DEST="I-SIGNAL">/Pkg/System</FIBEX-ELEMENT-REF>
</FIBEX-ELEMENT-REF-CONDITIONAL>
</FIBEX-ELEMENTS>
</SYSTEM>
<ECU-INSTANCE><SHORT-NAME>EcuInstance</SHORT-NAME></ECU-INSTANCE>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#;
let model = AutosarModel::new();
model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
let el_fibex_elements = model
.get_element_by_path("/Pkg/System")
.and_then(|sys| sys.get_sub_element(ElementName::FibexElements))
.unwrap();
let el_fibex_element_ref = el_fibex_elements
.create_sub_element(ElementName::FibexElementRefConditional)
.and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
.unwrap();
el_fibex_element_ref.set_character_data("/Pkg/System").unwrap();
let invalid_refs = model.check_references();
assert_eq!(invalid_refs.len(), 3);
let ref0 = invalid_refs[0].upgrade().unwrap();
assert_eq!(ref0.element_name(), ElementName::FibexElementRef);
let refpath = ref0.character_data().and_then(|cdata| cdata.string_value()).unwrap();
assert!(refpath == "/Pkg/System" || refpath == "/Some/Invalid/Path");
model.get_element_by_path("/Pkg/EcuInstance").unwrap();
let refs = model.get_references_to("/Pkg/EcuInstance");
assert_eq!(refs.len(), 1);
let refs = model.get_references_to("nonexistent");
assert!(refs.is_empty());
}
#[test]
fn serialize_files() {
let model = AutosarModel::default();
let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
let result = model.serialize_files();
assert_eq!(result.len(), 2);
assert_eq!(
result.get(&PathBuf::from("filename1")).unwrap(),
&file1.serialize().unwrap()
);
assert_eq!(
result.get(&PathBuf::from("filename2")).unwrap(),
&file2.serialize().unwrap()
);
}
#[test]
fn duplicate() {
let model = AutosarModel::new();
let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
let el_ar_packages = model
.root_element()
.create_sub_element(ElementName::ArPackages)
.unwrap();
let el_pkg1 = el_ar_packages
.create_named_sub_element(ElementName::ArPackage, "pkg1")
.unwrap();
let el_pkg2 = el_ar_packages
.create_named_sub_element(ElementName::ArPackage, "pkg2")
.unwrap();
assert_eq!(el_ar_packages.file_membership().unwrap().1.len(), 2);
el_pkg1.remove_from_file(&file2).unwrap();
assert_eq!(el_pkg1.file_membership().unwrap().1.len(), 1);
el_pkg2.remove_from_file(&file1).unwrap();
assert_eq!(el_pkg2.file_membership().unwrap().1.len(), 1);
let model2 = model.duplicate().unwrap();
assert_eq!(model2.files().count(), 2);
let mut files_iter = model2.files();
let mut model2_file1 = files_iter.next().unwrap();
let mut model2_file2 = files_iter.next().unwrap();
if model2_file1.filename() != file1.filename() {
std::mem::swap(&mut model2_file1, &mut model2_file2);
}
assert_eq!(file1.filename(), model2_file1.filename());
assert_eq!(file2.filename(), model2_file2.filename());
assert_eq!(file1.serialize().unwrap(), model2_file1.serialize().unwrap());
assert_eq!(file2.serialize().unwrap(), model2_file2.serialize().unwrap());
}
#[test]
fn write() {
let model = AutosarModel::default();
model.write().unwrap();
let dir = tempdir().unwrap();
let filename = dir.path().with_file_name("new.arxml");
model.create_file(&filename, AutosarVersion::LATEST).unwrap();
model.write().unwrap();
assert!(filename.exists());
let filename = PathBuf::from("nonexistent/dir/some_file.arxml");
let model = AutosarModel::default();
model.create_file(&filename, AutosarVersion::LATEST).unwrap();
let result = model.write();
assert!(result.is_err());
}
#[test]
fn traits() {
let model = AutosarModel::new();
let model_cloned = model.clone();
assert_eq!(model, model_cloned);
assert_eq!(format!("{model:#?}"), format!("{model_cloned:#?}"));
let mut hashset = HashSet::<AutosarModel>::new();
hashset.insert(model);
let inserted = hashset.insert(model_cloned);
assert!(!inserted);
let cdata = CharacterData::String("x".to_string());
let cdata2 = cdata.clone();
assert_eq!(cdata, cdata2);
assert_eq!(format!("{cdata:#?}"), format!("{cdata2:#?}"));
let ct: ContentType = ContentType::Elements;
let ct2 = ct;
assert_eq!(ct, ct2);
assert_eq!(format!("{ct:#?}"), format!("{ct2:#?}"));
}
#[test]
fn elements_dfs_with_max_depth() {
const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AR-PACKAGES>
<AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
<ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
<SHORT-NAME>BswModuleValues</SHORT-NAME>
<PARAMETER-VALUES>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
</ECUC-NUMERICAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
</ELEMENTS></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
<AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
</AR-PACKAGES></AUTOSAR>"#.as_bytes();
let model = AutosarModel::new();
let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
let all_count = model.elements_dfs().count();
let lvl2_count = model.elements_dfs_with_max_depth(2).count();
assert!(all_count > lvl2_count);
for elem in model.elements_dfs_with_max_depth(2) {
assert!(elem.0 <= 2);
}
}
#[test]
fn model_merge() {
const FILE_A: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
<AR-PACKAGES>
<AR-PACKAGE>
<SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
<ELEMENTS>
<ECUC-MODULE-CONFIGURATION-VALUES>
<SHORT-NAME>A</SHORT-NAME>
<DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
<CONTAINERS>
<ECUC-CONTAINER-VALUE>
<SHORT-NAME>AB</SHORT-NAME>
<DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
<PARAMETER-VALUES>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
<VALUE>0.01</VALUE>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-TEXTUAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-ENUMERATION-PARAM-DEF">/AUTOSAR_A/B/E</DEFINITION-REF>
<VALUE>ABC42</VALUE>
</ECUC-TEXTUAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE>
</CONTAINERS>
</ECUC-MODULE-CONFIGURATION-VALUES>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES>
</AUTOSAR>
"#;
const FILE_B: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
<AR-PACKAGES>
<AR-PACKAGE>
<SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
<ELEMENTS>
<ECUC-MODULE-CONFIGURATION-VALUES>
<SHORT-NAME>A</SHORT-NAME>
<DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
<CONTAINERS>
<ECUC-CONTAINER-VALUE>
<SHORT-NAME>AB</SHORT-NAME>
<DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
<PARAMETER-VALUES>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-INTEGER-PARAM-DEF">/AUTOSAR_A/B/C</DEFINITION-REF>
<VALUE>0</VALUE>
</ECUC-NUMERICAL-PARAM-VALUE>
<ECUC-NUMERICAL-PARAM-VALUE>
<DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
<VALUE>0.01</VALUE>
</ECUC-NUMERICAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE>
</CONTAINERS>
</ECUC-MODULE-CONFIGURATION-VALUES>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES>
</AUTOSAR>"#;
let model = AutosarModel::new();
let (_, _) = model.load_buffer(FILE_A, "file_a", true).unwrap();
let (_, _) = model.load_buffer(FILE_B, "file_b", true).unwrap();
model.sort();
let model_txt = model.root_element().serialize();
let model2 = AutosarModel::new();
let (_, _) = model2.load_buffer(FILE_B, "file_b", true).unwrap();
let (_, _) = model2.load_buffer(FILE_A, "file_a", true).unwrap();
model2.sort();
let model2_txt = model2.root_element().serialize();
println!("{}\n\n=======================\n{}\n\n", model_txt, model2_txt);
assert_eq!(model_txt, model2_txt);
}
}