1use std::{collections::HashMap, hash::Hash};
2
3use crate::*;
4
5#[derive(Debug)]
6enum MergeAction {
8 MergeEqual,
9 MergeUnequal(Element),
10 AOnly,
11 BOnly(usize),
12}
13
14enum MergeUndo {
22 Imported {
24 new_parent: Element,
25 element: Element,
26 old_parent: ElementOrModel,
27 old_file_membership: HashSet<WeakArxmlFile>,
28 },
29 FileMembership {
31 element: Element,
32 old_file_membership: HashSet<WeakArxmlFile>,
33 },
34 FileMembershipExtended {
36 element: Element,
37 file: WeakArxmlFile,
38 },
39}
40
41pub(crate) fn replace_path_prefix(path: &str, old_prefix: &str, new_prefix: &str) -> Option<String> {
47 let suffix = path.strip_prefix(old_prefix)?;
48 if suffix.is_empty() || suffix.starts_with('/') {
49 Some(format!("{new_prefix}{suffix}"))
50 } else {
51 None
52 }
53}
54
55pub(crate) struct PathRemap(Vec<(String, String)>);
62
63impl PathRemap {
64 pub(crate) fn single(old: String, new: String) -> Self {
66 PathRemap(vec![(old, new)])
67 }
68
69 pub(crate) fn new(remap: Vec<(String, String)>) -> Self {
71 PathRemap(remap)
72 }
73
74 pub(crate) fn map(&self, path: &str) -> Option<String> {
78 self.0.iter().find_map(|(old, new)| replace_path_prefix(path, old, new))
79 }
80
81 pub(crate) fn is_noop(&self) -> bool {
83 self.0.iter().all(|(old, new)| old == new)
84 }
85}
86
87impl AutosarModel {
88 #[must_use]
100 pub fn new() -> AutosarModel {
101 let version = AutosarVersion::LATEST;
102 let xsi_schemalocation =
103 CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename()));
104 let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string());
105 let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string());
106 let root_attributes = smallvec::smallvec![
107 Attribute {
108 attrname: AttributeName::xsiSchemalocation,
109 content: xsi_schemalocation
110 },
111 Attribute {
112 attrname: AttributeName::xmlns,
113 content: xmlns
114 },
115 Attribute {
116 attrname: AttributeName::xmlnsXsi,
117 content: xmlns_xsi
118 },
119 ];
120 let root_elem = ElementRaw {
121 parent: ElementOrModel::None,
122 elemname: ElementName::Autosar,
123 elemtype: ElementType::ROOT,
124 content: SmallVec::new(),
125 attributes: root_attributes,
126 file_membership: None,
127 comment: None,
128 }
129 .wrap();
130 let model = AutosarModelRaw {
131 files: Arc::new(parking_lot::Mutex::new(Vec::new())),
132 identifiables: FxIndexMap::default(),
133 reference_origins: FxHashMap::default(),
134 relative_references: FxHashMap::default(),
135 root_element: root_elem.clone(),
136 }
137 .wrap();
138 root_elem.set_parent(ElementOrModel::Model(model.downgrade()));
139 model
140 }
141
142 pub fn create_file<P: AsRef<Path>>(
170 &self,
171 filename: P,
172 version: AutosarVersion,
173 ) -> Result<ArxmlFile, AutosarDataError> {
174 let file_list = self.file_list();
177 let mut locked_file_list = file_list.lock();
178
179 if locked_file_list.iter().any(|af| af.filename() == filename.as_ref()) {
180 return Err(AutosarDataError::DuplicateFilenameError {
181 verb: "create",
182 filename: filename.as_ref().to_path_buf(),
183 });
184 }
185
186 let new_file = ArxmlFile::new(filename, version, self);
187
188 locked_file_list.push(new_file.clone());
189
190 let _ = self.root_element().add_to_file_restricted(&new_file);
192
193 Ok(new_file)
194 }
195
196 pub fn load_buffer<P: AsRef<Path>>(
227 &self,
228 buffer: &[u8],
229 filename: P,
230 strict: bool,
231 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
232 self.load_buffer_internal(buffer, filename.as_ref().to_path_buf(), strict)
233 }
234
235 fn load_buffer_internal(
236 &self,
237 buffer: &[u8],
238 filename: PathBuf,
239 strict: bool,
240 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
241 if self.file_list().lock().iter().any(|file| file.filename() == filename) {
245 return Err(AutosarDataError::DuplicateFilenameError { verb: "load", filename });
246 }
247
248 let mut parser = ArxmlParser::new(filename.clone(), buffer, strict);
250 parser.model = self.downgrade();
251 let root_element = parser.parse_arxml()?;
252 let version = parser.get_fileversion();
253 let arxml_file = ArxmlFileRaw {
254 version,
255 model: self.downgrade(),
256 filename: filename.clone(),
257 xml_standalone: parser.get_standalone(),
258 }
259 .wrap();
260
261 let file_list = self.file_list();
264 let mut locked_file_list = file_list.lock();
265
266 if locked_file_list.iter().any(|file| file.filename() == filename) {
267 return Err(AutosarDataError::DuplicateFilenameError { verb: "load", filename });
268 }
269
270 if locked_file_list.is_empty() {
271 root_element.set_parent(ElementOrModel::Model(self.downgrade()));
272 root_element.0.write().insert_file_membership(arxml_file.downgrade());
273 self.0.write().root_element = root_element;
274 } else {
275 let result = self.merge_file_data(&root_element, arxml_file.downgrade(), &locked_file_list);
276 if let Err(error) = result {
277 let _ = self.root_element().remove_from_file(&arxml_file);
278 return Err(error);
279 }
280 }
281
282 let mut data = self.0.write();
283 data.identifiables.reserve(parser.identifiables.len());
285 let mut overlap_path = None;
286 for (key, value) in parser.identifiables {
287 if let Some(existing_element) = data.identifiables.get(&key).and_then(WeakElement::upgrade) {
290 if let Some(new_element) = value.upgrade()
292 && existing_element.element_name() != new_element.element_name()
293 {
294 overlap_path = Some(new_element.xml_path());
296 break;
297 }
298 } else {
299 data.identifiables.insert(key, value);
300 }
301 }
302 if let Some(path) = overlap_path {
303 drop(data);
305 let _ = self.root_element().remove_from_file(&arxml_file);
306 return Err(AutosarDataError::OverlappingDataError { filename, path });
307 }
308
309 data.reference_origins.reserve(parser.references.len());
313 for (refpath, referring_element, base) in parser.references {
314 if base.is_some() {
315 data.relative_references.insert(referring_element, None);
316 } else {
317 data.reference_origins
318 .entry(refpath)
319 .or_default()
320 .push(referring_element);
321 }
322 }
323
324 locked_file_list.push(arxml_file.clone());
325 drop(data);
326
327 self.resolve_relative_references();
332
333 Ok((arxml_file, parser.warnings))
334 }
335
336 fn merge_file_data(
345 &self,
346 new_root: &Element,
347 new_file: WeakArxmlFile,
348 file_list: &[ArxmlFile],
349 ) -> Result<(), AutosarDataError> {
350 let root = self.root_element();
351 let files: HashSet<WeakArxmlFile> = file_list.iter().map(ArxmlFile::downgrade).collect();
352
353 let mut undo = Vec::new();
356
357 Self::merge_element(&root, &files, new_root, &new_file, &mut undo).map_err(|e| {
358 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
360 AutosarDataError::InvalidFileMerge {
361 path: parent_path.clone(),
362 }
363 } else {
364 e
365 }
366 })?;
367
368 self.root_element().0.write().insert_file_membership(new_file);
369
370 Ok(())
371 }
372
373 fn merge_element(
374 parent_a: &Element,
375 files: &HashSet<WeakArxmlFile>,
376 parent_b: &Element,
377 new_file: &WeakArxmlFile,
378 undo: &mut Vec<MergeUndo>,
379 ) -> Result<(), AutosarDataError> {
380 let mut iter_a = parent_a.sub_elements().enumerate();
381 let mut iter_b = parent_b.sub_elements();
382 let mut item_a = iter_a.next();
383 let mut item_b = iter_b.next();
384 let mut elements_a_only = Vec::<Element>::new();
385 let mut elements_b_only = Vec::<(Element, usize)>::new();
386 let mut elements_merge = Vec::<(Element, Element)>::new();
387 let mut b_item_name_map: Option<HashMap<(ElementName, Option<String>), Element>> = None;
391 let mut b_defref_map: Option<HashMap<(ElementName, Option<String>), Element>> = None;
392 let mut merged_b_elements: HashSet<WeakElement> = HashSet::new();
393 let min_ver_a = files
394 .iter()
395 .filter_map(|weak| weak.upgrade().map(|f| f.version()))
396 .min()
397 .unwrap_or(AutosarVersion::LATEST);
398 let min_ver_b = new_file.upgrade().map_or(AutosarVersion::LATEST, |f| f.version());
399 let version = std::cmp::min(min_ver_a, min_ver_b);
400 let splitable = parent_a.element_type().splittable_in(version);
401
402 while let (Some((pos_a, elem_a)), Some(elem_b)) = (&item_a, &item_b) {
403 let merge_action = if elem_a.element_name() == elem_b.element_name() {
404 if elem_a.is_identifiable() {
405 Self::calc_identifiables_merge(parent_a, parent_b, elem_a, elem_b, splitable, &mut b_item_name_map)?
406 } else {
407 Self::calc_element_merge(parent_b, elem_a, elem_b, &mut b_defref_map)
408 }
409 } else {
410 let parent_type = parent_a.element_type();
412 let (_, indices_a) = parent_type.find_sub_element(elem_a.element_name(), u32::MAX).unwrap();
418 let (_, indices_b) = parent_type.find_sub_element(elem_b.element_name(), u32::MAX).unwrap();
419 if indices_a < indices_b {
420 MergeAction::AOnly
424 } else {
425 MergeAction::BOnly(*pos_a)
429 }
430 };
431
432 match merge_action {
433 MergeAction::MergeEqual => {
434 elements_merge.push((elem_a.clone(), elem_b.clone()));
435 item_a = iter_a.next();
436 item_b = iter_b.next();
437 }
438 MergeAction::MergeUnequal(other_b) => {
439 merged_b_elements.insert(other_b.downgrade());
440 elements_merge.push((elem_a.clone(), other_b));
441 item_a = iter_a.next();
442 }
443 MergeAction::AOnly => {
444 elements_a_only.push(elem_a.clone());
445 item_a = iter_a.next();
446 }
447 MergeAction::BOnly(position) => {
448 if !merged_b_elements.contains(&elem_b.downgrade()) {
449 elements_b_only.push((elem_b.clone(), position));
450 }
451 item_b = iter_b.next();
452 }
453 }
454 }
455 if let Some((_, elem_a)) = item_a {
458 elements_a_only.push(elem_a);
459 for (_, elem_a) in iter_a {
460 elements_a_only.push(elem_a);
461 }
462 }
463 if let Some(elem_b) = item_b {
464 let elem_count = parent_a.0.read().content.len();
465 if !merged_b_elements.contains(&elem_b.downgrade()) {
466 elements_b_only.push((elem_b, elem_count));
467 }
468 for elem_b in iter_b {
469 if !merged_b_elements.contains(&elem_b.downgrade()) {
470 elements_b_only.push((elem_b, elem_count));
471 }
472 }
473 }
474
475 for element in elements_a_only {
477 let restricted = {
479 let mut elem_locked = element.0.write();
480 let restricted = elem_locked.file_membership.is_none();
481 if restricted {
482 elem_locked.set_file_membership(files.clone());
483 }
484 restricted
485 };
486 if restricted {
487 undo.push(MergeUndo::FileMembership {
488 element,
489 old_file_membership: HashSet::new(),
490 });
491 }
492 }
493
494 Self::import_new_items(parent_a, elements_b_only, new_file, min_ver_b, undo)?;
497
498 Self::merge_sub_elements(parent_a, elements_merge, files, new_file, version, undo)?;
500
501 Ok(())
502 }
503
504 fn calc_identifiables_merge(
507 parent_a: &Element,
508 parent_b: &Element,
509 elem_a: &Element,
510 elem_b: &Element,
511 splitable: bool,
512 b_item_name_map: &mut Option<HashMap<(ElementName, Option<String>), Element>>,
513 ) -> Result<MergeAction, AutosarDataError> {
514 Ok(if elem_a.item_name() == elem_b.item_name() {
515 MergeAction::MergeEqual
518 } else {
519 let map = b_item_name_map.get_or_insert_with(|| {
523 let mut map = HashMap::new();
524 for e in parent_b.sub_elements() {
525 map.entry((e.element_name(), e.item_name())).or_insert(e);
527 }
528 map
529 });
530 if let Some(sibling) = map.get(&(elem_a.element_name(), elem_a.item_name())) {
531 MergeAction::MergeUnequal(sibling.clone())
533 } else {
534 if splitable {
536 MergeAction::AOnly
537 } else {
538 return Err(AutosarDataError::InvalidFileMerge {
539 path: parent_a.xml_path(),
540 });
541 }
542 }
543 })
544 }
545
546 fn calc_element_merge(
549 parent_b: &Element,
550 elem_a: &Element,
551 elem_b: &Element,
552 b_defref_map: &mut Option<HashMap<(ElementName, Option<String>), Element>>,
553 ) -> MergeAction {
554 let defref_a = elem_a
556 .get_sub_element(ElementName::DefinitionRef)
557 .and_then(|dr| dr.character_data())
558 .and_then(|cdata| cdata.string_value());
559 let defref_b = elem_b
560 .get_sub_element(ElementName::DefinitionRef)
561 .and_then(|dr| dr.character_data())
562 .and_then(|cdata| cdata.string_value());
563 if defref_a == defref_b {
566 if elem_a.character_data() != elem_b.character_data() {
568 MergeAction::AOnly
571 } else {
572 MergeAction::MergeEqual
575 }
576 } else {
577 let map = b_defref_map.get_or_insert_with(|| {
582 let mut map = HashMap::new();
583 for e in parent_b.sub_elements() {
584 let defref = e
585 .get_sub_element(ElementName::DefinitionRef)
586 .and_then(|dr| dr.character_data())
587 .and_then(|cdata| cdata.string_value());
588 map.entry((e.element_name(), defref)).or_insert(e);
590 }
591 map
592 });
593 if let Some(sibling) = map.get(&(elem_a.element_name(), defref_a)) {
594 MergeAction::MergeUnequal(sibling.clone())
596 } else {
597 MergeAction::AOnly
600 }
601 }
602 }
603
604 fn import_new_items(
605 parent_a: &Element,
606 elements_b_only: Vec<(Element, usize)>,
607 new_file: &WeakArxmlFile,
608 version: AutosarVersion,
609 undo: &mut Vec<MergeUndo>,
610 ) -> Result<(), AutosarDataError> {
611 for (idx, (new_element, insert_pos)) in elements_b_only.into_iter().enumerate() {
613 let dest = insert_pos + idx;
615
616 Self::import_single_item(parent_a, new_element, dest, new_file, version, undo)?;
617 }
618 Ok(())
619 }
620
621 fn import_single_item(
622 parent_a: &Element,
623 new_element: Element,
624 dest: usize,
625 new_file: &WeakArxmlFile,
626 version: AutosarVersion,
627 undo: &mut Vec<MergeUndo>,
628 ) -> Result<(), AutosarDataError> {
629 let mut parent_a_locked = parent_a.0.write();
630
631 let (first_pos, last_pos) = parent_a_locked.calc_element_insert_range(new_element.element_name(), version)?;
635
636 let dest = dest.max(first_pos).min(last_pos);
638
639 let (old_parent, old_file_membership) = {
640 let mut new_elem_locked = new_element.0.write();
641 let old_parent = std::mem::replace(
642 &mut new_elem_locked.parent,
643 ElementOrModel::Element(parent_a.downgrade(), parent_a_locked.weak_model()),
644 );
645 let old_file_membership = new_elem_locked.file_membership_cloned();
647 new_elem_locked.insert_file_membership(new_file.clone());
648 (old_parent, old_file_membership)
649 };
650 undo.push(MergeUndo::Imported {
651 new_parent: parent_a.clone(),
652 element: new_element.clone(),
653 old_parent,
654 old_file_membership,
655 });
656
657 parent_a_locked
659 .content
660 .insert(dest, ElementContent::Element(new_element));
661
662 Ok(())
663 }
664
665 fn rollback_merge(undo: &mut Vec<MergeUndo>, mark: usize) {
667 for action in undo.drain(mark..).rev() {
668 match action {
669 MergeUndo::Imported {
670 new_parent,
671 element,
672 old_parent,
673 old_file_membership,
674 } => {
675 let mut new_parent_locked = new_parent.0.write();
677 if let Some(pos) = new_parent_locked
678 .content
679 .iter()
680 .position(|item| matches!(item, ElementContent::Element(e) if *e == element))
681 {
682 new_parent_locked.content.remove(pos);
683 }
684 drop(new_parent_locked);
685 let mut elem_locked = element.0.write();
688 elem_locked.set_parent(old_parent);
689 elem_locked.set_file_membership(old_file_membership);
690 }
691 MergeUndo::FileMembership {
692 element,
693 old_file_membership,
694 } => {
695 element.0.write().set_file_membership(old_file_membership);
696 }
697 MergeUndo::FileMembershipExtended { element, file } => {
698 element.0.write().remove_file_membership(&file);
699 }
700 }
701 }
702 }
703
704 fn merge_sub_elements(
705 parent_a: &Element,
706 elements_merge: Vec<(Element, Element)>,
707 files: &HashSet<WeakArxmlFile>,
708 new_file: &WeakArxmlFile,
709 version: AutosarVersion,
710 undo: &mut Vec<MergeUndo>,
711 ) -> Result<(), AutosarDataError> {
712 for (elem_a, elem_b) in elements_merge {
713 let files = match elem_a.0.read().file_membership.as_deref() {
715 Some(elem_files) => elem_files.clone(),
716 None => files.clone(),
717 };
718
719 let undo_mark = undo.len();
722 let result = AutosarModel::merge_element(&elem_a, &files, &elem_b, new_file, undo);
723 match result {
724 Ok(()) => {
725 let mut elem_a_locked = elem_a.0.write();
727 if elem_a_locked.file_membership.is_some() && elem_a_locked.insert_file_membership(new_file.clone())
728 {
729 drop(elem_a_locked);
730 undo.push(MergeUndo::FileMembershipExtended {
731 element: elem_a.clone(),
732 file: new_file.clone(),
733 });
734 }
735 }
736 Err(e) => {
737 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
738 if elem_a.is_identifiable() {
740 return Err(AutosarDataError::InvalidFileMerge {
742 path: parent_path.clone(),
743 });
744 } else if elem_a.get_sub_element(ElementName::DefinitionRef).is_some() {
745 return Err(AutosarDataError::InvalidFileMerge {
747 path: parent_path.clone(),
748 });
749 } else if parent_a.element_type().splittable_in(version) {
750 Self::rollback_merge(undo, undo_mark);
754
755 let old_file_membership = elem_a.0.read().file_membership_cloned();
756 elem_a.set_file_membership(files);
757 undo.push(MergeUndo::FileMembership {
758 element: elem_a.clone(),
759 old_file_membership,
760 });
761
762 let dest = elem_a.position().unwrap_or_default() + 1;
764 Self::import_single_item(parent_a, elem_b, dest, new_file, version, undo).map_err(|_| e)?;
765 continue;
767 }
768 }
769
770 return Err(e);
772 }
773 }
774 }
775 Ok(())
776 }
777
778 pub fn load_file<P: AsRef<Path>>(
806 &self,
807 filename: P,
808 strict: bool,
809 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
810 let filename_buf = filename.as_ref().to_path_buf();
811 let buffer = std::fs::read(&filename_buf).map_err(|err| AutosarDataError::IoErrorRead {
812 filename: filename_buf.clone(),
813 ioerror: err,
814 })?;
815
816 self.load_buffer(&buffer, &filename_buf, strict)
817 }
818
819 pub fn remove_file(&self, file: &ArxmlFile) {
837 let file_list = self.file_list();
840 let mut locked_file_list = file_list.lock();
841
842 let find_result = locked_file_list
843 .iter()
844 .enumerate()
845 .find(|(_, f)| *f == file)
846 .map(|(pos, _)| pos);
847 if let Some(pos) = find_result {
848 locked_file_list.swap_remove(pos);
849 if locked_file_list.is_empty() {
850 let mut locked_model = self.0.write();
852 for elem in locked_model.root_element.sub_elements() {
854 elem.detach_recursive();
855 }
856 locked_model.root_element.0.write().content.clear();
857 locked_model.root_element.set_file_membership(HashSet::new());
858 locked_model.identifiables.clear();
859 locked_model.reference_origins.clear();
860 locked_model.relative_references.clear();
861 } else {
862 let _ = self.root_element().remove_from_file(file);
864 }
865 }
866 }
867
868 #[must_use]
886 pub fn serialize_files(&self) -> HashMap<PathBuf, String> {
887 let mut result = HashMap::new();
888 for file in self.files() {
889 if let Ok(data) = file.serialize() {
890 result.insert(file.filename(), data);
891 }
892 }
893 result
894 }
895
896 pub fn write(&self) -> Result<(), AutosarDataError> {
919 for (pathbuf, filedata) in self.serialize_files() {
920 std::fs::write(pathbuf.clone(), filedata).map_err(|err| AutosarDataError::IoErrorWrite {
921 filename: pathbuf,
922 ioerror: err,
923 })?;
924 }
925 Ok(())
926 }
927
928 #[must_use]
944 pub fn files(&self) -> ArxmlFileIterator {
945 ArxmlFileIterator::new(self.clone())
946 }
947
948 pub(crate) fn file_list(&self) -> Arc<parking_lot::Mutex<Vec<ArxmlFile>>> {
953 self.0.read().files.clone()
954 }
955
956 #[must_use]
967 pub fn root_element(&self) -> Element {
968 let locked_model = self.0.read();
969 locked_model.root_element.clone()
970 }
971
972 #[must_use]
994 pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
995 let model = self.0.read();
996 model.identifiables.get(path).and_then(WeakElement::upgrade)
997 }
998
999 pub fn duplicate(&self) -> Result<AutosarModel, AutosarDataError> {
1026 let copy = Self::new();
1027 let mut filemap = HashMap::new();
1028
1029 for orig_file in self.files() {
1030 let filename = orig_file.filename();
1031 let new_file = copy.create_file(filename.clone(), orig_file.version())?;
1032 new_file.0.write().xml_standalone = orig_file.0.read().xml_standalone;
1033 filemap.insert(filename, new_file.downgrade());
1034 }
1035
1036 for element in self.root_element().sub_elements() {
1039 let copy_root = copy.root_element();
1040 let root_weak = copy_root.downgrade();
1041 copy_root
1042 .0
1043 .write()
1044 .create_copied_sub_element_unfiltered(root_weak, &element, ©)?;
1045 }
1046
1047 copy.resolve_relative_references();
1050
1051 let orig_iter = self.elements_dfs();
1054 let copy_iter = copy.elements_dfs();
1055 let combined = std::iter::zip(orig_iter, copy_iter);
1056 for ((_, orig_elem), (_, copy_elem)) in combined {
1057 debug_assert_eq!(orig_elem.element_name(), copy_elem.element_name());
1061 let mut locked_copy = copy_elem.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
1062 locked_copy.file_membership = None;
1063
1064 let orig_files = orig_elem.0.read().file_membership_cloned();
1065 for orig_file in orig_files.iter().filter_map(WeakArxmlFile::upgrade) {
1066 if let Some(copy_file) = filemap.get(&orig_file.filename()) {
1067 locked_copy.insert_file_membership(copy_file.clone());
1068 }
1069 }
1070 }
1071
1072 Ok(copy)
1073 }
1074
1075 #[must_use]
1105 pub fn elements_dfs(&self) -> ElementsDfsIterator {
1106 self.root_element().elements_dfs()
1107 }
1108
1109 #[must_use]
1132 pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ElementsDfsIterator {
1133 self.root_element().elements_dfs_with_max_depth(max_depth)
1134 }
1135
1136 pub fn sort(&self) {
1153 self.root_element().sort();
1154 }
1155
1156 #[must_use]
1176 pub fn identifiable_elements(&self) -> IdentifiablesIterator {
1177 IdentifiablesIterator::new(self)
1178 }
1179
1180 #[must_use]
1203 pub fn get_references_to(&self, target_path: &str) -> Vec<WeakElement> {
1204 let locked_model = self.0.read();
1205 locked_model
1208 .reference_origins
1209 .get(target_path)
1210 .cloned()
1211 .unwrap_or_default()
1212 }
1213
1214 #[must_use]
1238 pub fn check_references(&self) -> Vec<WeakElement> {
1239 let mut broken_refs = Vec::new();
1240
1241 let model = self.0.read();
1242 for (path, element_list) in &model.reference_origins {
1243 if let Some(target_elem_weak) = model.identifiables.get(path) {
1244 if let Some(target_elem) = target_elem_weak.upgrade() {
1246 for referring_elem_weak in element_list {
1249 if let Some(referring_elem) = referring_elem_weak.upgrade() {
1250 if let Some(CharacterData::Enum(dest_value)) =
1251 referring_elem.attribute_value(AttributeName::Dest)
1252 {
1253 if !target_elem.element_type().verify_reference_dest(dest_value) {
1254 broken_refs.push(referring_elem_weak.clone());
1256 }
1257 } else {
1258 broken_refs.push(referring_elem_weak.clone());
1260 }
1261 }
1262 }
1263 } else {
1264 broken_refs.extend(element_list.iter().cloned());
1267 }
1268 } else {
1269 broken_refs.extend(element_list.iter().cloned());
1271 }
1272 }
1273
1274 for (referring_elem_weak, target_path) in &model.relative_references {
1277 if target_path.is_none() && referring_elem_weak.upgrade().is_some() {
1278 broken_refs.push(referring_elem_weak.clone());
1279 }
1280 }
1281
1282 broken_refs
1283 }
1284
1285 pub(crate) fn single_file_version(&self) -> Option<AutosarVersion> {
1290 let file_list = self.file_list();
1291 let locked_file_list = file_list.lock();
1292 match locked_file_list.as_slice() {
1293 [file] => Some(file.version()),
1294 _ => None,
1295 }
1296 }
1297
1298 pub(crate) fn downgrade(&self) -> WeakAutosarModel {
1300 WeakAutosarModel(Arc::downgrade(&self.0))
1301 }
1302
1303 pub(crate) fn add_identifiable(&self, new_path: String, elem: WeakElement) {
1305 let mut model = self.0.write();
1306 model.identifiables.insert(new_path, elem);
1307 }
1308
1309 pub(crate) fn fix_element_paths(&self, remap: &PathRemap) {
1313 if remap.is_noop() {
1314 return;
1315 }
1316 let mut model = self.0.write();
1317
1318 let keys: Vec<String> = model.identifiables.keys().cloned().collect();
1320 for key in keys {
1321 if let Some(new_key) = remap.map(&key) {
1323 if let Some(entry) = model.identifiables.swap_remove(&key) {
1325 model.identifiables.insert(new_key, entry);
1326 }
1327 }
1328 }
1329 }
1330
1331 pub(crate) fn fix_reference_paths(
1338 &self,
1339 remap: &PathRemap,
1340 version: AutosarVersion,
1341 ) -> Result<(), AutosarDataError> {
1342 if remap.is_noop() {
1343 return Ok(());
1344 }
1345 let mut model = self.0.write();
1346
1347 let refpaths = model.reference_origins.keys().cloned().collect::<Vec<String>>();
1351 for refpath in refpaths {
1352 let Some(refpath_new) = remap.map(&refpath).filter(|new| *new != refpath) else {
1354 continue;
1355 };
1356 let Some(reflist) = model.reference_origins.remove(&refpath) else {
1357 continue;
1358 };
1359 let mut updated = Vec::with_capacity(reflist.len());
1360 let mut unchanged = Vec::new();
1361 for weak_ref_elem in reflist {
1362 let is_relative = model.relative_references.contains_key(&weak_ref_elem);
1363 let new_content = match weak_ref_elem.upgrade() {
1364 Some(ref_elem) => {
1365 let new_content = if is_relative {
1366 model.fix_relative_reference_content(&ref_elem, &refpath, &refpath_new, remap)
1367 } else {
1368 Some(refpath_new.clone())
1369 };
1370 if let Some(new_content) = &new_content {
1371 ref_elem.0.write().set_character_data(new_content.clone(), version)?;
1373 }
1374 new_content
1375 }
1376 None => Some(refpath_new.clone()),
1378 };
1379 if new_content.is_some() {
1380 if is_relative {
1381 model
1382 .relative_references
1383 .insert(weak_ref_elem.clone(), Some(refpath_new.clone()));
1384 }
1385 updated.push(weak_ref_elem);
1386 } else {
1387 unchanged.push(weak_ref_elem);
1392 }
1393 }
1394 if !updated.is_empty() {
1395 model.reference_origins.insert(refpath_new, updated);
1396 }
1397 if !unchanged.is_empty() {
1398 model.reference_origins.insert(refpath, unchanged);
1399 }
1400 }
1401
1402 Ok(())
1403 }
1404
1405 pub(crate) fn remove_identifiable(&self, path: &str) {
1407 let mut model = self.0.write();
1408 model.identifiables.swap_remove(path);
1409 }
1410
1411 pub(crate) fn add_reference_origin(&self, new_ref: &str, base: Option<&str>, origin: WeakElement) {
1420 let mut data = self.0.write();
1421 if base.is_some() {
1422 data.relative_references.insert(origin, None);
1423 } else {
1424 data.reference_origins
1425 .entry(new_ref.to_owned())
1426 .or_default()
1427 .push(origin);
1428 }
1429 }
1430
1431 pub(crate) fn remove_reference_origin(&self, reference: &str, element: WeakElement) {
1438 let mut data = self.0.write();
1439 let key = match data.relative_references.remove(&element) {
1440 Some(relative_key) => relative_key,
1441 None => Some(reference.to_owned()),
1442 };
1443 if let Some(key) = key {
1444 data.remove_reference_origin_by_key(&key, &element);
1445 }
1446 }
1447
1448 pub(crate) fn fix_reference_origins(
1452 &self,
1453 old_ref: &str,
1454 new_ref: &str,
1455 new_base: Option<&str>,
1456 origin: WeakElement,
1457 ) {
1458 self.remove_reference_origin(old_ref, origin.clone());
1459 self.add_reference_origin(new_ref, new_base, origin);
1460 }
1461
1462 pub(crate) fn resolve_relative_references(&self) {
1472 let mut model = self.0.write();
1473 if model.relative_references.is_empty() {
1474 return;
1475 }
1476 let origins: Vec<WeakElement> = model.relative_references.keys().cloned().collect();
1477 for origin in origins {
1478 let new_key = origin
1479 .upgrade()
1480 .and_then(|origin_elem| origin_elem.resolve_relative_target());
1481 let old_key = model
1482 .relative_references
1483 .insert(origin.clone(), new_key.clone())
1484 .flatten();
1485 if old_key == new_key {
1486 continue;
1487 }
1488 if let Some(old_key) = old_key {
1489 model.remove_reference_origin_by_key(&old_key, &origin);
1490 }
1491 match new_key {
1492 Some(new_key) => model.reference_origins.entry(new_key).or_default().push(origin),
1493 None => {
1496 if origin.upgrade().is_none() {
1497 model.relative_references.remove(&origin);
1499 }
1500 }
1501 }
1502 }
1503 }
1504
1505 pub(crate) fn relative_reference_target(&self, element: &WeakElement) -> Option<String> {
1510 self.0.read().relative_references.get(element).cloned().flatten()
1511 }
1512}
1513
1514impl AutosarModelRaw {
1515 pub(crate) fn wrap(self) -> AutosarModel {
1516 AutosarModel(Arc::new(RwLock::new(self)))
1517 }
1518
1519 fn fix_relative_reference_content(
1526 &mut self,
1527 element: &Element,
1528 old_target: &str,
1529 new_target: &str,
1530 remap: &PathRemap,
1531 ) -> Option<String> {
1532 let old_content = element.character_data()?.string_value()?;
1533 let base_path = old_target.strip_suffix(&old_content)?.strip_suffix('/')?;
1536 let new_base_path = remap.map(base_path);
1538 let new_base_path = new_base_path.as_deref().unwrap_or(base_path);
1539 let new_content = replace_path_prefix(new_target, new_base_path, "")?
1540 .strip_prefix('/')?
1541 .to_owned();
1542
1543 Some(new_content)
1544 }
1545
1546 fn remove_reference_origin_by_key(&mut self, key: &str, element: &WeakElement) {
1548 if let Some(origins) = self.reference_origins.get_mut(key) {
1549 if let Some(index) = origins.iter().position(|origin| origin == element) {
1550 origins.swap_remove(index);
1551 }
1552 if origins.is_empty() {
1553 self.reference_origins.remove(key);
1554 }
1555 }
1556 }
1557}
1558
1559impl std::fmt::Debug for AutosarModel {
1560 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1561 let files = self.file_list().lock().clone();
1563 let model = self.0.read();
1564 let rootelem = model.root_element.clone();
1567 let mut dbgstruct = f.debug_struct("AutosarModel");
1568 dbgstruct.field("root_element", &rootelem);
1569 dbgstruct.field("files", &files);
1570 dbgstruct.field("identifiables", &model.identifiables);
1571 dbgstruct.field("reference_origins", &model.reference_origins);
1572 dbgstruct.field("relative_references", &model.relative_references);
1573 dbgstruct.finish()
1574 }
1575}
1576
1577impl Default for AutosarModel {
1578 fn default() -> Self {
1579 Self::new()
1580 }
1581}
1582
1583impl PartialEq for AutosarModel {
1584 fn eq(&self, other: &Self) -> bool {
1585 Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
1586 }
1587}
1588
1589impl Eq for AutosarModel {}
1590
1591impl Hash for AutosarModel {
1592 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1593 state.write_usize(Arc::as_ptr(&self.0) as usize);
1594 }
1595}
1596
1597impl WeakAutosarModel {
1598 pub(crate) fn upgrade(&self) -> Option<AutosarModel> {
1599 Weak::upgrade(&self.0).map(AutosarModel)
1600 }
1601}
1602
1603impl std::fmt::Debug for WeakAutosarModel {
1604 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1605 f.write_fmt(format_args!("AutosarModel:WeakRef {:p}", Weak::as_ptr(&self.0)))
1606 }
1607}
1608
1609#[cfg(test)]
1616impl AutosarModel {
1617 fn verify_element_invariants(&self) -> Result<(), String> {
1624 for (_, element) in self.root_element().elements_dfs() {
1625 match element.model() {
1626 Ok(model) if model == *self => {}
1627 Ok(_) => return Err(format!("element {} points at a different model", element.xml_path())),
1628 Err(error) => return Err(format!("element {} has no model: {error}", element.xml_path())),
1629 }
1630 if element
1633 .0
1634 .read()
1635 .file_membership
1636 .as_deref()
1637 .is_some_and(HashSet::is_empty)
1638 {
1639 return Err(format!(
1640 "element {} stores an empty file membership instead of None",
1641 element.xml_path()
1642 ));
1643 }
1644 }
1645 Ok(())
1646 }
1647
1648 #[allow(clippy::mutable_key_type)]
1656 pub(crate) fn verify_reference_caches(&self) -> Result<(), String> {
1657 self.verify_element_invariants()?;
1660
1661 let mut tree_elements = std::collections::HashSet::new();
1662 let mut absolute_refs: Vec<(String, Element)> = Vec::new();
1664 let mut relative_refs: Vec<Element> = Vec::new();
1666
1667 for (_, element) in self.root_element().elements_dfs() {
1669 tree_elements.insert(element.clone());
1670 if element.is_reference()
1671 && let Some(CharacterData::String(text)) = element.character_data()
1672 {
1673 if element.attribute_value(AttributeName::Base).is_some() {
1674 relative_refs.push(element.clone());
1675 } else {
1676 absolute_refs.push((text, element.clone()));
1677 }
1678 }
1679 }
1680
1681 let model = self.0.read();
1682
1683 for (text, element) in &absolute_refs {
1685 if model.relative_references.contains_key(&element.downgrade()) {
1686 return Err(format!(
1687 "{} has no BASE attribute, but is registered as a relative reference",
1688 element.xml_path()
1689 ));
1690 }
1691 if !model
1692 .reference_origins
1693 .get(text)
1694 .is_some_and(|origins| origins.contains(&element.downgrade()))
1695 {
1696 return Err(format!(
1697 "{} references \"{text}\", but is missing from reference_origins[\"{text}\"]",
1698 element.xml_path()
1699 ));
1700 }
1701 }
1702
1703 for element in &relative_refs {
1706 let expected_target = element.resolve_relative_target();
1707 let Some(cached_target) = model.relative_references.get(&element.downgrade()) else {
1708 return Err(format!(
1709 "the relative reference {} is missing from relative_references",
1710 element.xml_path()
1711 ));
1712 };
1713 if *cached_target != expected_target {
1714 return Err(format!(
1715 "the relative reference {} resolves to {expected_target:?}, but relative_references says {cached_target:?}",
1716 element.xml_path()
1717 ));
1718 }
1719 if let Some(target) = cached_target
1720 && !model
1721 .reference_origins
1722 .get(target)
1723 .is_some_and(|origins| origins.contains(&element.downgrade()))
1724 {
1725 return Err(format!(
1726 "the relative reference {} resolves to \"{target}\", but is missing from reference_origins[\"{target}\"]",
1727 element.xml_path()
1728 ));
1729 }
1730 }
1731
1732 for (key, origins) in &model.reference_origins {
1734 for weak_origin in origins {
1735 let Some(element) = weak_origin.upgrade() else {
1736 continue;
1737 };
1738 if !tree_elements.contains(&element) {
1739 return Err(format!(
1740 "reference_origins[\"{key}\"] contains {}, which is not in the element tree",
1741 element.xml_path()
1742 ));
1743 }
1744 let registered_correctly = match model.relative_references.get(weak_origin) {
1745 Some(target) => target.as_deref() == Some(key.as_str()),
1747 None => absolute_refs.iter().any(|(text, elem)| elem == &element && text == key),
1749 };
1750 if !registered_correctly {
1751 return Err(format!(
1752 "reference_origins[\"{key}\"] contains {}, whose reference is {:?} with BASE={:?} and reverse index entry {:?}",
1753 element.xml_path(),
1754 element.character_data().and_then(|cdata| cdata.string_value()),
1755 element
1756 .attribute_value(AttributeName::Base)
1757 .and_then(|cdata| cdata.string_value()),
1758 model.relative_references.get(weak_origin)
1759 ));
1760 }
1761 }
1762 }
1763 for weak_origin in model.relative_references.keys() {
1764 let Some(element) = weak_origin.upgrade() else {
1765 continue;
1766 };
1767 if !relative_refs.contains(&element) {
1768 return Err(format!(
1769 "relative_references contains {}, which is not a relative reference in the element tree",
1770 element.xml_path()
1771 ));
1772 }
1773 }
1774
1775 Ok(())
1776 }
1777}
1778
1779#[cfg(test)]
1780mod test {
1781 use super::*;
1782 use tempfile::tempdir;
1783
1784 #[test]
1785 fn create_file() {
1786 let model = AutosarModel::new();
1787 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1788 assert!(file.is_ok());
1789 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1791 assert!(file.is_err());
1792
1793 let filename = PathBuf::from("test");
1795 let file = model.create_file(&filename, AutosarVersion::Autosar_00050);
1796 assert!(matches!(
1797 file,
1798 Err(AutosarDataError::DuplicateFilenameError { verb: "create", .. })
1799 ));
1800 let file = model.create_file(Path::new("test2"), AutosarVersion::Autosar_00050);
1801 assert!(file.is_ok());
1802 }
1803
1804 #[test]
1805 fn concurrent_load_buffer() {
1806 fn make_buf(pkg: &str) -> Vec<u8> {
1807 format!(
1808 r#"<?xml version="1.0" encoding="utf-8"?>
1809 <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">
1810 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>{pkg}</SHORT-NAME></AR-PACKAGE></AR-PACKAGES>
1811 </AUTOSAR>"#
1812 )
1813 .into_bytes()
1814 }
1815 for _ in 0..100 {
1818 let model = AutosarModel::new();
1819 let (m1, m2) = (model.clone(), model.clone());
1820 let t1 = std::thread::spawn(move || m1.load_buffer(&make_buf("PkgA"), "file1.arxml", true).is_ok());
1821 let t2 = std::thread::spawn(move || m2.load_buffer(&make_buf("PkgB"), "file2.arxml", true).is_ok());
1822 assert!(t1.join().unwrap());
1823 assert!(t2.join().unwrap());
1824 assert_eq!(model.files().count(), 2);
1825 assert!(model.get_element_by_path("/PkgA").is_some());
1826 assert!(model.get_element_by_path("/PkgB").is_some());
1827 }
1828
1829 for _ in 0..100 {
1832 let model = AutosarModel::new();
1833 let (m1, m2) = (model.clone(), model.clone());
1834 let t1 = std::thread::spawn(move || m1.load_buffer(&make_buf("PkgA"), "file1.arxml", true).is_ok());
1835 let t2 = std::thread::spawn(move || m2.load_buffer(&make_buf("PkgB"), "file1.arxml", true).is_ok());
1836 let ok1 = t1.join().unwrap();
1837 let ok2 = t2.join().unwrap();
1838 assert!(ok1 != ok2, "exactly one of the two loads must succeed");
1839 assert_eq!(model.files().count(), 1);
1840 }
1841 }
1842
1843 #[test]
1844 fn load_buffer() {
1845 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1846 <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">
1847 <AR-PACKAGES>
1848 <AR-PACKAGE>
1849 <SHORT-NAME>Pkg</SHORT-NAME>
1850 <ELEMENTS>
1851 <SYSTEM><SHORT-NAME>Thing</SHORT-NAME></SYSTEM>
1852 </ELEMENTS>
1853 </AR-PACKAGE>
1854 </AR-PACKAGES></AUTOSAR>"#;
1855 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1856 <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">
1857 <AR-PACKAGES>
1858 <AR-PACKAGE><SHORT-NAME>OtherPkg</SHORT-NAME></AR-PACKAGE>
1859 </AR-PACKAGES></AUTOSAR>"#;
1860 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1861 <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">
1862 <AR-PACKAGES>
1863 <AR-PACKAGE>
1864 <SHORT-NAME>Pkg</SHORT-NAME>
1865 <ELEMENTS>
1866 <APPLICATION-PRIMITIVE-DATA-TYPE><SHORT-NAME>Thing</SHORT-NAME></APPLICATION-PRIMITIVE-DATA-TYPE>
1867 </ELEMENTS>
1868 </AR-PACKAGE>
1869 </AR-PACKAGES></AUTOSAR>"#;
1870 const NON_ARXML: &str = "The quick brown fox jumps over the lazy dog";
1871 let model = AutosarModel::new();
1872 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1874 assert!(result.is_ok());
1875 let result = model.load_buffer(FILEBUF2.as_bytes(), "other", true);
1877 assert!(result.is_ok());
1878 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1880 assert!(result.is_err());
1881 let result = model.load_buffer(FILEBUF3.as_bytes(), "test2", true);
1883 assert!(result.is_err());
1884 let result = model.load_buffer(NON_ARXML.as_bytes(), "nonsense", true);
1886 assert!(result.is_err());
1887 }
1888
1889 #[test]
1890 fn load_file() {
1891 let dir = tempdir().unwrap();
1892
1893 let model = AutosarModel::new();
1894 let filename = dir.path().with_file_name("nonexistent.arxml");
1895 assert!(model.load_file(&filename, true).is_err());
1896
1897 let filename = dir.path().with_file_name("test.arxml");
1898 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1899 model
1900 .root_element()
1901 .create_sub_element(ElementName::ArPackages)
1902 .and_then(|ap| ap.create_named_sub_element(ElementName::ArPackage, "Pkg"))
1903 .unwrap();
1904 model.write().unwrap();
1905
1906 assert!(filename.exists());
1907
1908 let model = AutosarModel::new();
1910 model.load_file(&filename, true).unwrap();
1911 let el_pkg = model.get_element_by_path("/Pkg");
1912 assert!(el_pkg.is_some());
1913 }
1914
1915 #[test]
1916 fn data_merge() {
1917 const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1918 <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">
1919 <AR-PACKAGES>
1920 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1921 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1922 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1923 <PARAMETER-VALUES>
1924 <ECUC-NUMERICAL-PARAM-VALUE>
1925 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1926 </ECUC-NUMERICAL-PARAM-VALUE>
1927 <ECUC-NUMERICAL-PARAM-VALUE>
1928 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1929 </ECUC-NUMERICAL-PARAM-VALUE>
1930 <ECUC-NUMERICAL-PARAM-VALUE>
1931 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
1932 </ECUC-NUMERICAL-PARAM-VALUE>
1933 </PARAMETER-VALUES>
1934 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1935 </ELEMENTS></AR-PACKAGE>
1936 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1937 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
1938 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1939 const FILEBUF2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1940 <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">
1941 <AR-PACKAGES>
1942 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1943 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1944 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1945 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1946 <PARAMETER-VALUES>
1947 <ECUC-NUMERICAL-PARAM-VALUE>
1948 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1949 </ECUC-NUMERICAL-PARAM-VALUE>
1950 <ECUC-NUMERICAL-PARAM-VALUE>
1951 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1952 </ECUC-NUMERICAL-PARAM-VALUE>
1953 </PARAMETER-VALUES>
1954 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1955 </ELEMENTS></AR-PACKAGE>
1956 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1957 let model = AutosarModel::new();
1960 let (file1, _) = model.load_buffer(FILEBUF1, "test1", true).unwrap();
1961 let file1_elemcount = file1.elements_dfs().count();
1962 let (file2, _) = model.load_buffer(FILEBUF2, "test2", true).unwrap();
1963 let file2_elemcount = file2.elements_dfs().count();
1964 let model_elemcount = model.elements_dfs().count();
1965 assert_eq!(file1_elemcount, model_elemcount);
1966 assert!(file1_elemcount > file2_elemcount);
1967 let (local, fileset) = model.root_element().file_membership().unwrap();
1969 assert!(local);
1970 assert_eq!(fileset.len(), 2);
1971
1972 let el_pkg_c = model.get_element_by_path("/Pkg_C").unwrap();
1973 let (local, fileset) = el_pkg_c.file_membership().unwrap();
1974 assert!(local);
1975 assert_eq!(fileset.len(), 1);
1976 let el_npv2 = model
1977 .get_element_by_path("/Pkg_A/BswModule/BswModuleValues")
1978 .and_then(|bmv| bmv.get_sub_element(ElementName::ParameterValues))
1979 .and_then(|pv| pv.get_sub_element_at(2))
1980 .unwrap();
1981 let (loc, fm) = el_npv2.file_membership().unwrap();
1982 assert!(loc);
1983 assert_eq!(fm.len(), 1);
1984
1985 const ERRFILE1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1988 <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">
1989 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1990 <ELEMENTS>
1991 <SYSTEM-TIMING>
1992 <SHORT-NAME>SystemTimings</SHORT-NAME>
1993 <CATEGORY>CAT</CATEGORY>
1994 <TIMING-RESOURCE>
1995 <SHORT-NAME>Name_One</SHORT-NAME>
1996 </TIMING-RESOURCE>
1997 </SYSTEM-TIMING>
1998 </ELEMENTS>
1999 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2000 const ERRFILE2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2001 <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">
2002 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
2003 <ELEMENTS>
2004 <SYSTEM-TIMING>
2005 <SHORT-NAME>SystemTimings</SHORT-NAME>
2006 <TIMING-RESOURCE>
2007 <SHORT-NAME>Name_Two</SHORT-NAME>
2008 </TIMING-RESOURCE>
2009 </SYSTEM-TIMING>
2010 </ELEMENTS>
2011 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2012 let model = AutosarModel::new();
2013 let result = model.load_buffer(ERRFILE1, "test1", true);
2014 assert!(result.is_ok());
2015 let result = model.load_buffer(ERRFILE2, "test2", true);
2016 let error = result.unwrap_err();
2017 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
2018
2019 const ERRFILE3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2023 <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">
2024 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
2025 <ELEMENTS>
2026 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
2027 <COMPU-INTERNAL-TO-PHYS>
2028 <COMPU-SCALES>
2029 <COMPU-SCALE><COMPU-CONST></COMPU-CONST></COMPU-SCALE>
2030 </COMPU-SCALES>
2031 </COMPU-INTERNAL-TO-PHYS>
2032 </COMPU-METHOD>
2033 </ELEMENTS>
2034 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2035 const ERRFILE4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2036 <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">
2037 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
2038 <ELEMENTS>
2039 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
2040 <COMPU-INTERNAL-TO-PHYS>
2041 <COMPU-SCALES>
2042 <COMPU-SCALE><COMPU-RATIONAL-COEFFS></COMPU-RATIONAL-COEFFS></COMPU-SCALE>
2043 </COMPU-SCALES>
2044 </COMPU-INTERNAL-TO-PHYS>
2045 </COMPU-METHOD>
2046 </ELEMENTS>
2047 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2048 let model = AutosarModel::new();
2049 let result = model.load_buffer(ERRFILE3, "test3", true);
2050 assert!(result.is_ok());
2051 let result = model.load_buffer(ERRFILE4, "test4", true);
2052 let error = result.unwrap_err();
2053 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
2054
2055 const FILEBUF3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2057 <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">
2058 <AR-PACKAGES>
2059 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
2060 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME></AR-PACKAGE>
2061 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
2062 const FILEBUF4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2063 <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">
2064 <AR-PACKAGES>
2065 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
2066 let model_a = AutosarModel::new();
2067 model_a.load_buffer(FILEBUF3, "test5", true).unwrap();
2068 model_a.load_buffer(FILEBUF4, "test6", true).unwrap();
2069 let model_b = AutosarModel::new();
2071 model_b.load_buffer(FILEBUF4, "test5", true).unwrap();
2072 model_b.load_buffer(FILEBUF3, "test6", true).unwrap();
2073 model_a.sort();
2075 let model_a_txt = model_a.root_element().serialize();
2076 model_b.sort();
2077 let model_b_txt = model_b.root_element().serialize();
2078 assert_eq!(model_a_txt, model_b_txt);
2079 }
2080
2081 #[test]
2082 fn remove_file() {
2083 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2084 <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">
2085 <AR-PACKAGES>
2086 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
2087 </AR-PACKAGES></AUTOSAR>"#;
2088 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2089 <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">
2090 <AR-PACKAGES>
2091 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
2092 <ELEMENTS><CAN-CLUSTER><SHORT-NAME>CAN_Cluster</SHORT-NAME></CAN-CLUSTER></ELEMENTS>
2093 </AR-PACKAGE>
2094 </AR-PACKAGES></AUTOSAR>"#;
2095 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2096 <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">
2097 <AR-PACKAGES>
2098 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME>
2099 <ELEMENTS><SYSTEM><SHORT-NAME>System</SHORT-NAME>
2100 <FIBEX-ELEMENTS><FIBEX-ELEMENT-REF-CONDITIONAL>
2101 <FIBEX-ELEMENT-REF DEST="CAN-CLUSTER">/Package/CAN_Cluster</FIBEX-ELEMENT-REF>
2102 </FIBEX-ELEMENT-REF-CONDITIONAL></FIBEX-ELEMENTS>
2103 </SYSTEM></ELEMENTS></AR-PACKAGE>
2104 </AR-PACKAGES></AUTOSAR>"#;
2105 let model = AutosarModel::new();
2107 let (file, _) = model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
2108 assert_eq!(model.files().count(), 1);
2109 assert_eq!(model.identifiable_elements().count(), 1);
2110 model.remove_file(&file);
2111 assert_eq!(model.files().count(), 0);
2112 assert_eq!(model.identifiable_elements().count(), 0);
2113 let model = AutosarModel::new();
2115 model.load_buffer(FILEBUF.as_bytes(), "test1", true).unwrap();
2116 assert_eq!(model.files().count(), 1);
2117 let modeltxt_1 = model.root_element().serialize();
2118 let (file2, _) = model.load_buffer(FILEBUF2.as_bytes(), "test2", true).unwrap();
2119 assert_eq!(model.files().count(), 2);
2120 let modeltxt_1_2 = model.root_element().serialize();
2121 assert_ne!(modeltxt_1, modeltxt_1_2);
2122 let (file3, _) = model.load_buffer(FILEBUF3.as_bytes(), "test3", true).unwrap();
2123 assert_eq!(model.files().count(), 3);
2124 let modeltxt_1_2_3 = model.root_element().serialize();
2125 assert_ne!(modeltxt_1_2, modeltxt_1_2_3);
2126 model.get_element_by_path("/Package2/System").unwrap();
2127 model.remove_file(&file3);
2128 let modeltxt_1_2_x = model.root_element().serialize();
2130 assert_eq!(modeltxt_1_2, modeltxt_1_2_x);
2131 model.remove_file(&file2);
2132 let modeltxt_1_x_x = model.root_element().serialize();
2134 assert_eq!(modeltxt_1, modeltxt_1_x_x);
2135 assert_eq!(model.files().count(), 1);
2136 }
2137
2138 #[test]
2139 fn remove_last_file_clears_reference_caches() {
2140 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2141<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd">
2142 <AR-PACKAGES>
2143 <AR-PACKAGE>
2144 <SHORT-NAME>BasePkg</SHORT-NAME>
2145 <ELEMENTS>
2146 <ECU-INSTANCE>
2147 <SHORT-NAME>Ecu</SHORT-NAME>
2148 </ECU-INSTANCE>
2149 </ELEMENTS>
2150 </AR-PACKAGE>
2151 <AR-PACKAGE>
2152 <SHORT-NAME>RefPkg</SHORT-NAME>
2153 <REFERENCE-BASES>
2154 <REFERENCE-BASE>
2155 <SHORT-LABEL>BaseA</SHORT-LABEL>
2156 <PACKAGE-REF DEST="AR-PACKAGE">/BasePkg</PACKAGE-REF>
2157 </REFERENCE-BASE>
2158 </REFERENCE-BASES>
2159 <ELEMENTS>
2160 <SYSTEM>
2161 <SHORT-NAME>Sys</SHORT-NAME>
2162 <FIBEX-ELEMENTS>
2163 <FIBEX-ELEMENT-REF-CONDITIONAL>
2164 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseA">Ecu</FIBEX-ELEMENT-REF>
2165 </FIBEX-ELEMENT-REF-CONDITIONAL>
2166 </FIBEX-ELEMENTS>
2167 </SYSTEM>
2168 </ELEMENTS>
2169 </AR-PACKAGE>
2170 </AR-PACKAGES>
2171</AUTOSAR>"#
2172 .as_bytes();
2173
2174 let model = AutosarModel::new();
2175 let (file, _) = model.load_buffer(FILEBUF, "test", true).unwrap();
2176
2177 assert!(!model.0.read().relative_references.is_empty());
2178 assert!(!model.0.read().reference_origins.is_empty());
2179 model.remove_file(&file);
2180
2181 assert!(model.0.read().relative_references.is_empty());
2182 assert!(model.0.read().reference_origins.is_empty());
2183 }
2184
2185 #[test]
2186 fn refcount() {
2187 let model = AutosarModel::default();
2188 let weak = model.downgrade();
2189 let project2 = weak.upgrade();
2190 assert_eq!(Arc::strong_count(&model.0), 2);
2191 assert_eq!(model, project2.unwrap());
2192 }
2193
2194 #[test]
2195 fn identifiables_iterator() {
2196 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2197 <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">
2198 <AR-PACKAGES>
2199 <AR-PACKAGE><SHORT-NAME>OuterPackage1</SHORT-NAME>
2200 <AR-PACKAGES>
2201 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
2202 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
2203 </AR-PACKAGES>
2204 </AR-PACKAGE>
2205 <AR-PACKAGE><SHORT-NAME>OuterPackage2</SHORT-NAME>
2206 <AR-PACKAGES>
2207 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
2208 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
2209 </AR-PACKAGES>
2210 </AR-PACKAGE>
2211 </AR-PACKAGES></AUTOSAR>"#;
2212 let model = AutosarModel::new();
2213 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
2214 let mut identifiable_elements = model.identifiable_elements().collect::<Vec<_>>();
2215 identifiable_elements.sort_by(|a, b| a.0.cmp(&b.0));
2216 assert_eq!(identifiable_elements[0].0, "/OuterPackage1");
2217 assert_eq!(identifiable_elements[1].0, "/OuterPackage1/InnerPackage1");
2218 assert_eq!(identifiable_elements[2].0, "/OuterPackage1/InnerPackage2");
2219 assert_eq!(identifiable_elements[3].0, "/OuterPackage2");
2220 assert_eq!(identifiable_elements[4].0, "/OuterPackage2/InnerPackage1");
2221 assert_eq!(identifiable_elements[5].0, "/OuterPackage2/InnerPackage2");
2222 }
2223
2224 #[test]
2225 fn check_references() {
2226 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2227 <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">
2228 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
2229 <ELEMENTS>
2230 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
2231 <FIBEX-ELEMENTS>
2232 <FIBEX-ELEMENT-REF-CONDITIONAL>
2233 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE">/Pkg/EcuInstance</FIBEX-ELEMENT-REF>
2234 </FIBEX-ELEMENT-REF-CONDITIONAL>
2235 <FIBEX-ELEMENT-REF-CONDITIONAL>
2236 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/Some/Invalid/Path</FIBEX-ELEMENT-REF>
2237 </FIBEX-ELEMENT-REF-CONDITIONAL>
2238 <FIBEX-ELEMENT-REF-CONDITIONAL>
2239 <FIBEX-ELEMENT-REF DEST="I-SIGNAL">/Pkg/System</FIBEX-ELEMENT-REF>
2240 </FIBEX-ELEMENT-REF-CONDITIONAL>
2241 </FIBEX-ELEMENTS>
2242 </SYSTEM>
2243 <ECU-INSTANCE><SHORT-NAME>EcuInstance</SHORT-NAME></ECU-INSTANCE>
2244 </ELEMENTS>
2245 </AR-PACKAGE>
2246 </AR-PACKAGES></AUTOSAR>"#;
2247 let model = AutosarModel::new();
2248 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
2249 let el_system = model.get_element_by_path("/Pkg/System").unwrap();
2250 let el_fibex_elements = el_system.get_sub_element(ElementName::FibexElements).unwrap();
2251 let el_fibex_element_ref = el_fibex_elements
2252 .create_sub_element(ElementName::FibexElementRefConditional)
2253 .and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
2254 .unwrap();
2255 el_fibex_element_ref.set_character_data("/Pkg/System").unwrap();
2256 assert_eq!(model.0.read().reference_origins.len(), 3);
2262
2263 let el_fbx_ref1 = el_fibex_elements
2265 .get_sub_element_at(0)
2266 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
2267 .unwrap();
2268 assert_eq!(
2269 el_fbx_ref1.get_reference_target().unwrap().element_name(),
2270 ElementName::EcuInstance
2271 );
2272
2273 let invalid_refs = model
2274 .check_references()
2275 .iter()
2276 .filter_map(WeakElement::upgrade)
2277 .collect::<Vec<_>>();
2278 assert_eq!(invalid_refs.len(), 3);
2279 let ref0 = &invalid_refs[0];
2280 assert_eq!(ref0.element_name(), ElementName::FibexElementRef);
2281 let refpath = ref0.character_data().and_then(|cdata| cdata.string_value()).unwrap();
2282 assert!(refpath == "/Pkg/System" || refpath == "/Some/Invalid/Path");
2284
2285 model.get_element_by_path("/Pkg/EcuInstance").unwrap();
2286 let refs = model.get_references_to("/Pkg/EcuInstance");
2287 assert_eq!(refs.len(), 1);
2288 let refs = model.get_references_to("nonexistent");
2289 assert!(refs.is_empty());
2290 assert_eq!(model.verify_reference_caches(), Ok(()));
2291 }
2292
2293 #[test]
2294 fn serialize_files() {
2295 let model = AutosarModel::default();
2296 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
2297 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
2298
2299 let result = model.serialize_files();
2300 assert_eq!(result.len(), 2);
2301 assert_eq!(
2302 result.get(&PathBuf::from("filename1")).unwrap(),
2303 &file1.serialize().unwrap()
2304 );
2305 assert_eq!(
2306 result.get(&PathBuf::from("filename2")).unwrap(),
2307 &file2.serialize().unwrap()
2308 );
2309 }
2310
2311 #[test]
2312 fn duplicate() {
2313 let model = AutosarModel::new();
2314 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
2315 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
2316 let el_ar_packages = model
2317 .root_element()
2318 .create_sub_element(ElementName::ArPackages)
2319 .unwrap();
2320 let el_pkg1 = el_ar_packages
2321 .create_named_sub_element(ElementName::ArPackage, "pkg1")
2322 .unwrap();
2323 let el_pkg2 = el_ar_packages
2324 .create_named_sub_element(ElementName::ArPackage, "pkg2")
2325 .unwrap();
2326
2327 assert_eq!(el_ar_packages.file_membership().unwrap().1.len(), 2);
2328 el_pkg1.remove_from_file(&file2).unwrap();
2329 assert_eq!(el_pkg1.file_membership().unwrap().1.len(), 1);
2330 el_pkg2.remove_from_file(&file1).unwrap();
2331 assert_eq!(el_pkg2.file_membership().unwrap().1.len(), 1);
2332
2333 let model2 = model.duplicate().unwrap();
2334 assert_eq!(model2.files().count(), 2);
2335 let mut files_iter = model2.files();
2336 let mut model2_file1 = files_iter.next().unwrap();
2338 let mut model2_file2 = files_iter.next().unwrap();
2339 if model2_file1.filename() != file1.filename() {
2341 std::mem::swap(&mut model2_file1, &mut model2_file2);
2342 }
2343
2344 assert_eq!(file1.filename(), model2_file1.filename());
2345 assert_eq!(file2.filename(), model2_file2.filename());
2346 assert_eq!(file1.serialize().unwrap(), model2_file1.serialize().unwrap());
2347 assert_eq!(file2.serialize().unwrap(), model2_file2.serialize().unwrap());
2348 }
2349
2350 #[test]
2357 fn duplicate_mixed_versions() {
2358 const FILEBUF_OLD: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2360 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-0-1.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2361 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>PkgOld</SHORT-NAME><ELEMENTS>
2362 <PORT-BLUEPRINT><SHORT-NAME>Blueprint</SHORT-NAME></PORT-BLUEPRINT>
2363 </ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2364 const FILEBUF_NEW: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2366 <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">
2367 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>PkgNew</SHORT-NAME><ELEMENTS>
2368 <ADAPTIVE-APPLICATION-SW-COMPONENT-TYPE><SHORT-NAME>Adaptive</SHORT-NAME></ADAPTIVE-APPLICATION-SW-COMPONENT-TYPE>
2369 </ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2370
2371 let model = AutosarModel::new();
2372 let (file_old, _) = model.load_buffer(FILEBUF_OLD, "old.arxml", true).unwrap();
2373 let (file_new, _) = model.load_buffer(FILEBUF_NEW, "new.arxml", true).unwrap();
2374
2375 let copy = model.duplicate().unwrap();
2376
2377 assert!(copy.get_element_by_path("/PkgOld/Blueprint").is_some());
2379 assert!(copy.get_element_by_path("/PkgNew/Adaptive").is_some());
2381 assert_eq!(model.elements_dfs().count(), copy.elements_dfs().count());
2382
2383 assert_eq!(copy.files().count(), 2);
2387 let copy_old = copy.files().find(|f| f.filename() == file_old.filename()).unwrap();
2388 let copy_new = copy.files().find(|f| f.filename() == file_new.filename()).unwrap();
2389 assert_eq!(copy_old.version(), AutosarVersion::Autosar_4_0_1);
2390 assert_eq!(copy_new.version(), AutosarVersion::Autosar_00050);
2391 assert_eq!(copy_old.serialize().unwrap(), file_old.serialize().unwrap());
2392 assert_eq!(copy_new.serialize().unwrap(), file_new.serialize().unwrap());
2393 }
2394
2395 #[test]
2396 fn write() {
2397 let model = AutosarModel::default();
2398 model.write().unwrap();
2400
2401 let dir = tempdir().unwrap();
2402 let filename = dir.path().with_file_name("new.arxml");
2403 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
2404 model.write().unwrap();
2405 assert!(filename.exists());
2406
2407 let filename = PathBuf::from("nonexistent/dir/some_file.arxml");
2408 let model = AutosarModel::default();
2409 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
2411 let result = model.write();
2413 assert!(result.is_err());
2414 }
2415
2416 #[test]
2417 fn traits() {
2418 let model = AutosarModel::new();
2420 let model_cloned = model.clone();
2421 assert_eq!(model, model_cloned);
2422 assert_eq!(format!("{model:#?}"), format!("{model_cloned:#?}"));
2423 #[allow(clippy::mutable_key_type)]
2424 let mut hashset = HashSet::<AutosarModel>::new();
2425 hashset.insert(model);
2426 let inserted = hashset.insert(model_cloned);
2427 assert!(!inserted);
2428
2429 let cdata = CharacterData::String("x".to_string());
2431 let cdata2 = cdata.clone();
2432 assert_eq!(cdata, cdata2);
2433 assert_eq!(format!("{cdata:#?}"), format!("{cdata2:#?}"));
2434
2435 let ct: ContentType = ContentType::Elements;
2437 let ct2 = ct;
2438 assert_eq!(ct, ct2);
2439 assert_eq!(format!("{ct:#?}"), format!("{ct2:#?}"));
2440 }
2441
2442 #[test]
2443 fn elements_dfs_with_max_depth() {
2444 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2445 <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">
2446 <AR-PACKAGES>
2447 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
2448 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
2449 <SHORT-NAME>BswModuleValues</SHORT-NAME>
2450 <PARAMETER-VALUES>
2451 <ECUC-NUMERICAL-PARAM-VALUE>
2452 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
2453 </ECUC-NUMERICAL-PARAM-VALUE>
2454 <ECUC-NUMERICAL-PARAM-VALUE>
2455 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
2456 </ECUC-NUMERICAL-PARAM-VALUE>
2457 <ECUC-NUMERICAL-PARAM-VALUE>
2458 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
2459 </ECUC-NUMERICAL-PARAM-VALUE>
2460 </PARAMETER-VALUES>
2461 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
2462 </ELEMENTS></AR-PACKAGE>
2463 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
2464 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
2465 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
2466 let model = AutosarModel::new();
2467 let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
2468 let all_count = model.elements_dfs().count();
2469 let lvl2_count = model.elements_dfs_with_max_depth(2).count();
2470 assert!(all_count > lvl2_count);
2471 for elem in model.elements_dfs_with_max_depth(2) {
2472 assert!(elem.0 <= 2);
2473 }
2474 }
2475
2476 #[test]
2477 fn model_merge() {
2478 const FILE_A: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
2480<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
2481 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2482 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
2483 <AR-PACKAGES>
2484 <AR-PACKAGE>
2485 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
2486 <ELEMENTS>
2487 <ECUC-MODULE-CONFIGURATION-VALUES>
2488 <SHORT-NAME>A</SHORT-NAME>
2489 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
2490 <CONTAINERS>
2491 <ECUC-CONTAINER-VALUE>
2492 <SHORT-NAME>AB</SHORT-NAME>
2493 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
2494 <PARAMETER-VALUES>
2495 <ECUC-NUMERICAL-PARAM-VALUE>
2496 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
2497 <VALUE>0.01</VALUE>
2498 </ECUC-NUMERICAL-PARAM-VALUE>
2499 <ECUC-TEXTUAL-PARAM-VALUE>
2500 <DEFINITION-REF DEST="ECUC-ENUMERATION-PARAM-DEF">/AUTOSAR_A/B/E</DEFINITION-REF>
2501 <VALUE>ABC42</VALUE>
2502 </ECUC-TEXTUAL-PARAM-VALUE>
2503 </PARAMETER-VALUES>
2504 </ECUC-CONTAINER-VALUE>
2505 </CONTAINERS>
2506 </ECUC-MODULE-CONFIGURATION-VALUES>
2507 </ELEMENTS>
2508 </AR-PACKAGE>
2509 </AR-PACKAGES>
2510</AUTOSAR>
2511 "#;
2512
2513 const FILE_B: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
2514<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
2515 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2516 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
2517 <AR-PACKAGES>
2518 <AR-PACKAGE>
2519 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
2520 <ELEMENTS>
2521 <ECUC-MODULE-CONFIGURATION-VALUES>
2522 <SHORT-NAME>A</SHORT-NAME>
2523 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
2524 <CONTAINERS>
2525 <ECUC-CONTAINER-VALUE>
2526 <SHORT-NAME>AB</SHORT-NAME>
2527 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
2528 <PARAMETER-VALUES>
2529 <ECUC-NUMERICAL-PARAM-VALUE>
2530 <DEFINITION-REF DEST="ECUC-INTEGER-PARAM-DEF">/AUTOSAR_A/B/C</DEFINITION-REF>
2531 <VALUE>0</VALUE>
2532 </ECUC-NUMERICAL-PARAM-VALUE>
2533 <ECUC-NUMERICAL-PARAM-VALUE>
2534 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
2535 <VALUE>0.01</VALUE>
2536 </ECUC-NUMERICAL-PARAM-VALUE>
2537 </PARAMETER-VALUES>
2538 </ECUC-CONTAINER-VALUE>
2539 </CONTAINERS>
2540 </ECUC-MODULE-CONFIGURATION-VALUES>
2541 </ELEMENTS>
2542 </AR-PACKAGE>
2543 </AR-PACKAGES>
2544</AUTOSAR>"#;
2545
2546 let model = AutosarModel::new();
2548 let (_, _) = model.load_buffer(FILE_A, "file_a", true).unwrap();
2549 let (_, _) = model.load_buffer(FILE_B, "file_b", true).unwrap();
2550 model.sort();
2552 let model_txt = model.root_element().serialize();
2553
2554 let model2 = AutosarModel::new();
2555 let (_, _) = model2.load_buffer(FILE_B, "file_b", true).unwrap();
2556 let (_, _) = model2.load_buffer(FILE_A, "file_a", true).unwrap();
2557 model2.sort();
2559 let model2_txt = model2.root_element().serialize();
2560
2561 assert_eq!(model_txt, model2_txt);
2562 }
2563
2564 #[test]
2565 fn model_merge_2() {
2566 const FILEBUF1: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
2568<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
2569 <AR-PACKAGES>
2570 <AR-PACKAGE>
2571 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
2572 <ELEMENTS>
2573 <BSW-MODULE-DESCRIPTION>
2574 <SHORT-NAME>BSWMD</SHORT-NAME>
2575 <IMPLEMENTED-ENTRYS>
2576 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2577 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_A0</BSW-MODULE-ENTRY-REF>
2578 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2579 </IMPLEMENTED-ENTRYS>
2580 </BSW-MODULE-DESCRIPTION>
2581 </ELEMENTS>
2582 </AR-PACKAGE>
2583 </AR-PACKAGES>
2584</AUTOSAR>"#;
2585 const FILEBUF2: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
2586<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
2587 <AR-PACKAGES>
2588 <AR-PACKAGE>
2589 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
2590 <ELEMENTS>
2591 <BSW-MODULE-DESCRIPTION>
2592 <SHORT-NAME>BSWMD</SHORT-NAME>
2593 <IMPLEMENTED-ENTRYS>
2594 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2595 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B0</BSW-MODULE-ENTRY-REF>
2596 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2597 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2598 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B1</BSW-MODULE-ENTRY-REF>
2599 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2600 </IMPLEMENTED-ENTRYS>
2601 </BSW-MODULE-DESCRIPTION>
2602 </ELEMENTS>
2603 </AR-PACKAGE>
2604 </AR-PACKAGES>
2605</AUTOSAR>"#;
2606
2607 let model = AutosarModel::new();
2608 let (_, _) = model.load_buffer(FILEBUF1, "file1", true).unwrap();
2609 let (_, _) = model.load_buffer(FILEBUF2, "file2", true).unwrap();
2610
2611 let a0_refs = model.get_references_to("/path/to/entry_A0");
2612 assert!(a0_refs.len() == 1);
2613 assert!(a0_refs[0].upgrade().is_some());
2614 let b0_refs = model.get_references_to("/path/to/entry_B0");
2615 assert!(b0_refs.len() == 1);
2616 assert!(b0_refs[0].upgrade().is_some());
2617 let b1_refs = model.get_references_to("/path/to/entry_B1");
2618 assert!(b1_refs.len() == 1);
2619 assert!(b1_refs[0].upgrade().is_some());
2620 assert_eq!(model.verify_reference_caches(), Ok(()));
2621 }
2622
2623 #[test]
2624 fn model_merge_3() {
2625 const FILEBUF1: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
2626<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">
2627<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
2628 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
2629 <SHORT-NAME>BswModuleValues</SHORT-NAME>
2630 <PARAMETER-VALUES>
2631 <ECUC-NUMERICAL-PARAM-VALUE>
2632 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
2633 <VALUE>1</VALUE>
2634 </ECUC-NUMERICAL-PARAM-VALUE>
2635 </PARAMETER-VALUES>
2636 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
2637</ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
2638 const FILEBUF2: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
2639<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">
2640<AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
2641 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
2642 <SHORT-NAME>BswModuleValues</SHORT-NAME>
2643 <PARAMETER-VALUES>
2644 <ECUC-NUMERICAL-PARAM-VALUE>
2645 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
2646 <ANNOTATIONS/>
2647 <VALUE>2</VALUE>
2648 </ECUC-NUMERICAL-PARAM-VALUE>
2649 </PARAMETER-VALUES>
2650 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
2651</ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
2652
2653 let model = AutosarModel::new();
2658 let (_, _) = model.load_buffer(FILEBUF1, "file1", true).unwrap();
2659 let result = model.load_buffer(FILEBUF2, "file2", true);
2660 assert!(result.is_err());
2661 }
2662
2663 #[test]
2664 fn data_merge_after_insertion_conflict() {
2665 const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2674 <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">
2675 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME><ELEMENTS>
2676 <SERVICE-SW-COMPONENT-TYPE><SHORT-NAME>Swc</SHORT-NAME>
2677 <INTERNAL-BEHAVIORS><SWC-INTERNAL-BEHAVIOR><SHORT-NAME>Behavior</SHORT-NAME>
2678 <PORT-API-OPTIONS>
2679 <PORT-API-OPTION>
2680 <PORT-REF DEST="P-PORT-PROTOTYPE">/Pkg/Swc/PortA</PORT-REF>
2681 </PORT-API-OPTION>
2682 </PORT-API-OPTIONS>
2683 </SWC-INTERNAL-BEHAVIOR></INTERNAL-BEHAVIORS>
2684 </SERVICE-SW-COMPONENT-TYPE>
2685 </ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2686 const FILEBUF2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2687 <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">
2688 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME><ELEMENTS>
2689 <SERVICE-SW-COMPONENT-TYPE><SHORT-NAME>Swc</SHORT-NAME>
2690 <INTERNAL-BEHAVIORS><SWC-INTERNAL-BEHAVIOR><SHORT-NAME>Behavior</SHORT-NAME>
2691 <PORT-API-OPTIONS>
2692 <PORT-API-OPTION>
2693 <ENABLE-TAKE-ADDRESS>true</ENABLE-TAKE-ADDRESS>
2694 <PORT-ARG-VALUES>
2695 <PORT-DEFINED-ARGUMENT-VALUE>
2696 <VALUE-TYPE-TREF DEST="IMPLEMENTATION-DATA-TYPE">/Pkg/SomeType</VALUE-TYPE-TREF>
2697 </PORT-DEFINED-ARGUMENT-VALUE>
2698 </PORT-ARG-VALUES>
2699 <PORT-REF DEST="P-PORT-PROTOTYPE">/Pkg/Swc/PortB</PORT-REF>
2700 </PORT-API-OPTION>
2701 </PORT-API-OPTIONS>
2702 </SWC-INTERNAL-BEHAVIOR></INTERNAL-BEHAVIORS>
2703 </SERVICE-SW-COMPONENT-TYPE>
2704 </ELEMENTS></AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
2705
2706 let single_model = AutosarModel::new();
2708 let (single_file1, _) = single_model.load_buffer(FILEBUF1, "file1.arxml", true).unwrap();
2709 let file1_txt = single_file1.serialize().unwrap();
2710 let single_model = AutosarModel::new();
2711 let (single_file2, _) = single_model.load_buffer(FILEBUF2, "file2.arxml", true).unwrap();
2712 let file2_txt = single_file2.serialize().unwrap();
2713
2714 let model = AutosarModel::new();
2715 let (file1, _) = model.load_buffer(FILEBUF1, "file1.arxml", true).unwrap();
2716 let (file2, _) = model.load_buffer(FILEBUF2, "file2.arxml", true).unwrap();
2717
2718 let el_port_api_options = model
2719 .get_element_by_path("/Pkg/Swc/Behavior")
2720 .and_then(|behavior| behavior.get_sub_element(ElementName::PortApiOptions))
2721 .unwrap();
2722 let options: Vec<Element> = el_port_api_options.sub_elements().collect();
2723 assert_eq!(options.len(), 2);
2725 assert_eq!(options[0].file_membership().unwrap().1.len(), 1);
2726 assert_eq!(options[1].file_membership().unwrap().1.len(), 1);
2727
2728 for element_name in [ElementName::EnableTakeAddress, ElementName::PortArgValues] {
2731 let count = model
2732 .elements_dfs()
2733 .filter(|(_, elem)| elem.element_name() == element_name)
2734 .count();
2735 assert_eq!(count, 1, "{element_name} exists {count} times, expected 1");
2736 }
2737 let sub_elements_0: Vec<ElementName> = options[0].sub_elements().map(|e| e.element_name()).collect();
2739 assert_eq!(sub_elements_0, vec![ElementName::PortRef]);
2740 let sub_elements_1: Vec<ElementName> = options[1].sub_elements().map(|e| e.element_name()).collect();
2741 assert_eq!(
2742 sub_elements_1,
2743 vec![
2744 ElementName::EnableTakeAddress,
2745 ElementName::PortArgValues,
2746 ElementName::PortRef
2747 ]
2748 );
2749
2750 assert_eq!(file1.serialize().unwrap(), file1_txt);
2752 assert_eq!(file2.serialize().unwrap(), file2_txt);
2753
2754 let model = AutosarModel::new();
2756 let (file2, _) = model.load_buffer(FILEBUF2, "file2.arxml", true).unwrap();
2757 let (file1, _) = model.load_buffer(FILEBUF1, "file1.arxml", true).unwrap();
2758 assert_eq!(file1.serialize().unwrap(), file1_txt);
2759 assert_eq!(file2.serialize().unwrap(), file2_txt);
2760 }
2761
2762 const FILEBUF1_COMPLEX_BASES: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2765<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">
2766 <AR-PACKAGES>
2767 <AR-PACKAGE><SHORT-NAME>BasesPkg</SHORT-NAME>
2768 <REFERENCE-BASES>
2769 <REFERENCE-BASE>
2770 <SHORT-LABEL>BaseA</SHORT-LABEL>
2771 <PACKAGE-REF DEST="AR-PACKAGE">/ContentPkg</PACKAGE-REF>
2772 </REFERENCE-BASE>
2773 <REFERENCE-BASE>
2774 <SHORT-LABEL>BaseC</SHORT-LABEL>
2775 <PACKAGE-REF DEST="AR-PACKAGE">/ContentPkg2</PACKAGE-REF>
2776 </REFERENCE-BASE>
2777 </REFERENCE-BASES>
2778 <AR-PACKAGES>
2779 <AR-PACKAGE><SHORT-NAME>SubPackage</SHORT-NAME>
2780 <REFERENCE-BASES>
2781 <REFERENCE-BASE>
2782 <SHORT-LABEL>BaseB</SHORT-LABEL>
2783 <PACKAGE-REF DEST="AR-PACKAGE" BASE="BaseA">SubPackage</PACKAGE-REF>
2784 </REFERENCE-BASE>
2785 </REFERENCE-BASES>
2786 <ELEMENTS>
2787 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
2788 <FIBEX-ELEMENTS>
2789 <FIBEX-ELEMENT-REF-CONDITIONAL>
2790 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseB">Ecu</FIBEX-ELEMENT-REF>
2791 </FIBEX-ELEMENT-REF-CONDITIONAL>
2792 <FIBEX-ELEMENT-REF-CONDITIONAL>
2793 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseC">Ecu</FIBEX-ELEMENT-REF>
2794 </FIBEX-ELEMENT-REF-CONDITIONAL>
2795 </FIBEX-ELEMENTS>
2796 </SYSTEM>
2797 </ELEMENTS>
2798 </AR-PACKAGE>
2799 </AR-PACKAGES>
2800 </AR-PACKAGE>
2801 <AR-PACKAGE><SHORT-NAME>ContentPkg</SHORT-NAME>
2802 <AR-PACKAGES>
2803 <AR-PACKAGE><SHORT-NAME>SubPackage</SHORT-NAME>
2804 <ELEMENTS>
2805 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
2806 </ELEMENTS>
2807 </AR-PACKAGE>
2808 </AR-PACKAGES>
2809 </AR-PACKAGE>
2810 <AR-PACKAGE><SHORT-NAME>ContentPkg2</SHORT-NAME>
2811 <ELEMENTS>
2812 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
2813 </ELEMENTS>
2814 </AR-PACKAGE>
2815 </AR-PACKAGES>
2816</AUTOSAR>"#.as_bytes();
2817
2818 type CacheCorruption = (&'static str, fn(&AutosarModel) -> Vec<Element>);
2822
2823 fn get_absolute_package_ref(model: &AutosarModel) -> Element {
2825 model
2826 .get_element_by_path("/BasesPkg")
2827 .and_then(|el_package| el_package.get_sub_element(ElementName::ReferenceBases))
2828 .and_then(|el_bases| el_bases.get_sub_element_at(0))
2829 .and_then(|el_base| el_base.get_sub_element(ElementName::PackageRef))
2830 .unwrap()
2831 }
2832
2833 #[test]
2834 fn verify_reference_caches_detects_inconsistency() {
2835 let corruptions: [CacheCorruption; 8] = [
2839 ("missing entry", |model| {
2841 model.0.write().relative_references.clear();
2842 vec![]
2843 }),
2844 ("missing absolute entry", |model| {
2846 let el_package_ref = get_absolute_package_ref(model);
2847 let target = el_package_ref.character_data().unwrap().string_value().unwrap();
2848 model.0.write().reference_origins.remove(&target);
2849 vec![el_package_ref]
2850 }),
2851 ("absolute reference in the reverse index", |model| {
2853 let el_package_ref = get_absolute_package_ref(model);
2854 model
2855 .0
2856 .write()
2857 .relative_references
2858 .insert(el_package_ref.downgrade(), None);
2859 vec![el_package_ref]
2860 }),
2861 ("wrong key", |model| {
2863 let el_package_ref = get_absolute_package_ref(model);
2864 model
2865 .0
2866 .write()
2867 .reference_origins
2868 .entry("/Wrong".to_string())
2869 .or_default()
2870 .push(el_package_ref.downgrade());
2871 vec![el_package_ref]
2872 }),
2873 ("reverse index entry which is not a reference", |model| {
2875 let el_system = model.get_element_by_path("/BasesPkg/SubPackage/System").unwrap();
2876 model.0.write().relative_references.insert(el_system.downgrade(), None);
2877 vec![el_system]
2878 }),
2879 ("stale target path", |model| {
2881 let el_ref = get_complex_bases_refs(model).0;
2882 model
2883 .0
2884 .write()
2885 .relative_references
2886 .insert(el_ref.downgrade(), Some("/Stale".to_string()));
2887 vec![el_ref]
2888 }),
2889 ("missing bucket entry", |model| {
2891 let el_ref = get_complex_bases_refs(model).0;
2892 let mut model_locked = model.0.write();
2893 let target = model_locked
2894 .relative_references
2895 .get(&el_ref.downgrade())
2896 .cloned()
2897 .flatten()
2898 .unwrap();
2899 model_locked.reference_origins.remove(&target);
2900 vec![el_ref]
2901 }),
2902 ("removed element", |model| {
2904 let el_ref = get_complex_bases_refs(model).0;
2905 let el_parent = el_ref.parent().unwrap().unwrap();
2906 model
2907 .0
2908 .write()
2909 .reference_origins
2910 .insert("/detached".to_string(), vec![el_ref.downgrade()]);
2911 el_parent.remove_sub_element(el_ref.clone()).unwrap();
2912 vec![el_ref]
2913 }),
2914 ];
2915
2916 for (description, corrupt) in corruptions {
2917 let model = AutosarModel::new();
2918 model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true).unwrap();
2919 assert_eq!(model.verify_reference_caches(), Ok(()));
2920 let _keep_alive = corrupt(&model);
2921 assert!(
2922 model.verify_reference_caches().is_err(),
2923 "verify_reference_caches() did not detect: {description}"
2924 );
2925 }
2926 }
2927
2928 #[test]
2929 fn complex_reference_bases() {
2930 let model = AutosarModel::new();
2931 let result = model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true);
2932 assert!(result.is_ok());
2933
2934 let el_system = model.get_element_by_path("/BasesPkg/SubPackage/System").unwrap();
2935 let el_fibex_elements = el_system.get_sub_element(ElementName::FibexElements).unwrap();
2936 let el_fibex_element_ref = el_fibex_elements
2937 .get_sub_element_at(0)
2938 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
2939 .unwrap();
2940
2941 let el_ecu = el_fibex_element_ref.get_reference_target().unwrap();
2943 assert_eq!(el_ecu.element_name(), ElementName::EcuInstance);
2944
2945 let origins = model.get_references_to(&el_ecu.path().unwrap());
2947 assert_eq!(origins.len(), 1);
2948 let origin = origins[0].upgrade().unwrap();
2949 assert_eq!(origin.element_name(), ElementName::FibexElementRef);
2950
2951 let origins2 = model.get_references_to("/ContentPkg2/Ecu");
2952 assert_eq!(origins2.len(), 1);
2953 assert_eq!(model.verify_reference_caches(), Ok(()));
2954 }
2955
2956 fn get_complex_bases_refs(model: &AutosarModel) -> (Element, Element) {
2959 let el_fibex_elements = model
2960 .get_element_by_path("/BasesPkg/SubPackage/System")
2961 .and_then(|el_system| el_system.get_sub_element(ElementName::FibexElements))
2962 .unwrap();
2963 let mut refs = el_fibex_elements
2964 .sub_elements()
2965 .filter_map(|ferc| ferc.get_sub_element(ElementName::FibexElementRef));
2966 (refs.next().unwrap(), refs.next().unwrap())
2967 }
2968
2969 #[test]
2970 fn rename_relative_reference_target() {
2971 let model = AutosarModel::new();
2972 let result = model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true);
2973 assert!(result.is_ok());
2974 let (el_ref_base_b, el_ref_base_c) = get_complex_bases_refs(&model);
2975
2976 let el_ecu = model.get_element_by_path("/ContentPkg/SubPackage/Ecu").unwrap();
2978 el_ecu.set_item_name("RenamedEcu").unwrap();
2979 assert_eq!(
2980 el_ref_base_b.character_data().unwrap().string_value().unwrap(),
2981 "RenamedEcu"
2982 );
2983 assert_eq!(el_ref_base_b.get_reference_target().unwrap(), el_ecu);
2984 assert_eq!(el_ref_base_c.character_data().unwrap().string_value().unwrap(), "Ecu");
2986 assert_eq!(
2987 el_ref_base_c.get_reference_target().unwrap(),
2988 model.get_element_by_path("/ContentPkg2/Ecu").unwrap()
2989 );
2990
2991 assert_eq!(model.get_references_to("/ContentPkg/SubPackage/RenamedEcu").len(), 1);
2994 assert_eq!(model.get_references_to("/ContentPkg/SubPackage/Ecu").len(), 0);
2995 assert_eq!(model.get_references_to("/ContentPkg2/Ecu").len(), 1);
2996 assert!(model.check_references().is_empty());
2997 assert_eq!(model.verify_reference_caches(), Ok(()));
2998 }
2999
3000 #[test]
3001 fn rename_relative_reference_target_repeatedly() {
3002 let model = AutosarModel::new();
3005 let result = model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true);
3006 assert!(result.is_ok());
3007 let (el_ref_base_b, _) = get_complex_bases_refs(&model);
3008
3009 let el_ecu = model.get_element_by_path("/ContentPkg/SubPackage/Ecu").unwrap();
3010 for new_name in ["Ecu2", "Ecu3", "Ecu4"] {
3011 el_ecu.set_item_name(new_name).unwrap();
3012 assert_eq!(
3013 el_ref_base_b.character_data().unwrap().string_value().unwrap(),
3014 new_name
3015 );
3016 assert_eq!(el_ref_base_b.get_reference_target().unwrap(), el_ecu);
3017 assert_eq!(model.get_references_to(&el_ecu.path().unwrap()).len(), 1);
3018 assert!(model.check_references().is_empty());
3019 }
3020 assert_eq!(model.verify_reference_caches(), Ok(()));
3021 }
3022
3023 const FILEBUF_MULTI_COMPONENT_RELATIVE_REF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
3027<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">
3028 <AR-PACKAGES>
3029 <AR-PACKAGE><SHORT-NAME>BasesPkg</SHORT-NAME>
3030 <REFERENCE-BASES>
3031 <REFERENCE-BASE>
3032 <SHORT-LABEL>BaseA</SHORT-LABEL>
3033 <PACKAGE-REF DEST="AR-PACKAGE">/ContentPkg</PACKAGE-REF>
3034 </REFERENCE-BASE>
3035 </REFERENCE-BASES>
3036 <ELEMENTS>
3037 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
3038 <FIBEX-ELEMENTS>
3039 <FIBEX-ELEMENT-REF-CONDITIONAL>
3040 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseA">SubPackage/Ecu</FIBEX-ELEMENT-REF>
3041 </FIBEX-ELEMENT-REF-CONDITIONAL>
3042 </FIBEX-ELEMENTS>
3043 </SYSTEM>
3044 </ELEMENTS>
3045 </AR-PACKAGE>
3046 <AR-PACKAGE><SHORT-NAME>ContentPkg</SHORT-NAME>
3047 <AR-PACKAGES>
3048 <AR-PACKAGE><SHORT-NAME>SubPackage</SHORT-NAME>
3049 <ELEMENTS>
3050 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
3051 </ELEMENTS>
3052 </AR-PACKAGE>
3053 </AR-PACKAGES>
3054 </AR-PACKAGE>
3055 </AR-PACKAGES>
3056</AUTOSAR>"#.as_bytes();
3057
3058 fn get_multi_component_relative_ref(model: &AutosarModel) -> Element {
3060 model
3061 .get_element_by_path("/BasesPkg/System")
3062 .and_then(|el_system| el_system.get_sub_element(ElementName::FibexElements))
3063 .and_then(|el_fibex_elements| el_fibex_elements.get_sub_element_at(0))
3064 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
3065 .unwrap()
3066 }
3067
3068 #[test]
3069 fn rename_package_inside_relative_reference_path() {
3070 let model = AutosarModel::new();
3073 let result = model.load_buffer(FILEBUF_MULTI_COMPONENT_RELATIVE_REF, "test", true);
3074 assert!(result.is_ok());
3075 let el_ref = get_multi_component_relative_ref(&model);
3076 let el_ecu = el_ref.get_reference_target().unwrap();
3077
3078 let el_subpackage = model.get_element_by_path("/ContentPkg/SubPackage").unwrap();
3080 el_subpackage.set_item_name("SubPkgX").unwrap();
3081 assert_eq!(el_ref.character_data().unwrap().string_value().unwrap(), "SubPkgX/Ecu");
3082 assert_eq!(el_ref.get_reference_target().unwrap(), el_ecu);
3083 assert_eq!(model.get_references_to("/ContentPkg/SubPkgX/Ecu").len(), 1);
3084 assert!(model.check_references().is_empty());
3085
3086 let el_content_package = model.get_element_by_path("/ContentPkg").unwrap();
3088 el_content_package.set_item_name("ContentPkgX").unwrap();
3089 assert_eq!(el_ref.character_data().unwrap().string_value().unwrap(), "SubPkgX/Ecu");
3090 assert_eq!(el_ref.get_reference_target().unwrap(), el_ecu);
3091 assert_eq!(model.get_references_to("/ContentPkgX/SubPkgX/Ecu").len(), 1);
3092 assert!(model.check_references().is_empty());
3093 assert_eq!(model.verify_reference_caches(), Ok(()));
3094 }
3095
3096 #[test]
3097 fn move_relative_reference_target() {
3098 let model = AutosarModel::new();
3101 let result = model.load_buffer(FILEBUF_MULTI_COMPONENT_RELATIVE_REF, "test", true);
3102 assert!(result.is_ok());
3103 let el_ref = get_multi_component_relative_ref(&model);
3104 let el_ecu = el_ref.get_reference_target().unwrap();
3105
3106 let el_elements2 = model
3108 .get_element_by_path("/ContentPkg")
3109 .and_then(|el_package| el_package.get_sub_element(ElementName::ArPackages))
3110 .and_then(|el_packages| {
3111 el_packages
3112 .create_named_sub_element(ElementName::ArPackage, "SubPackage2")
3113 .ok()
3114 })
3115 .and_then(|el_package2| el_package2.create_sub_element(ElementName::Elements).ok())
3116 .unwrap();
3117 el_elements2.move_element_here(&el_ecu).unwrap();
3118
3119 assert_eq!(el_ecu.path().unwrap(), "/ContentPkg/SubPackage2/Ecu");
3120 assert_eq!(
3121 el_ref.character_data().unwrap().string_value().unwrap(),
3122 "SubPackage2/Ecu"
3123 );
3124 assert_eq!(el_ref.get_reference_target().unwrap(), el_ecu);
3125 assert_eq!(model.get_references_to("/ContentPkg/SubPackage2/Ecu").len(), 1);
3126 assert!(model.check_references().is_empty());
3127 assert_eq!(model.verify_reference_caches(), Ok(()));
3128 }
3129
3130 #[test]
3131 fn move_relative_reference_target_out_of_base() {
3132 let model = AutosarModel::new();
3136 let result = model.load_buffer(FILEBUF_MULTI_COMPONENT_RELATIVE_REF, "test", true);
3137 assert!(result.is_ok());
3138 let el_ref = get_multi_component_relative_ref(&model);
3139 let el_ecu = el_ref.get_reference_target().unwrap();
3140
3141 let el_elements = model
3143 .get_element_by_path("/BasesPkg")
3144 .and_then(|el_package| el_package.get_sub_element(ElementName::Elements))
3145 .unwrap();
3146 el_elements.move_element_here(&el_ecu).unwrap();
3147
3148 assert_eq!(el_ecu.path().unwrap(), "/BasesPkg/Ecu");
3149 assert_eq!(
3151 el_ref.character_data().unwrap().string_value().unwrap(),
3152 "SubPackage/Ecu"
3153 );
3154 assert!(el_ref.get_reference_target().is_err());
3155 assert!(model.get_references_to("/BasesPkg/Ecu").is_empty());
3156 let broken = model.check_references();
3158 assert_eq!(broken.len(), 1);
3159 assert_eq!(broken[0].upgrade().unwrap(), el_ref);
3160 assert_eq!(model.verify_reference_caches(), Ok(()));
3161 }
3162
3163 #[test]
3164 fn rename_package_referenced_by_relative_reference_base() {
3165 let model = AutosarModel::new();
3169 let result = model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true);
3170 assert!(result.is_ok());
3171 let (el_ref_base_b, el_ref_base_c) = get_complex_bases_refs(&model);
3172 let el_ecu = el_ref_base_b.get_reference_target().unwrap();
3173
3174 let el_subpackage = model.get_element_by_path("/ContentPkg/SubPackage").unwrap();
3175 el_subpackage.set_item_name("SubPkgX").unwrap();
3176
3177 let el_package_ref = model
3178 .get_element_by_path("/BasesPkg/SubPackage")
3179 .and_then(|el_package| el_package.get_sub_element(ElementName::ReferenceBases))
3180 .and_then(|el_bases| el_bases.get_sub_element_at(0))
3181 .and_then(|el_base| el_base.get_sub_element(ElementName::PackageRef))
3182 .unwrap();
3183 assert_eq!(
3184 el_package_ref.character_data().unwrap().string_value().unwrap(),
3185 "SubPkgX"
3186 );
3187 assert_eq!(
3189 el_package_ref.resolve_reference_base("BaseB").as_deref(),
3190 Some("/ContentPkg/SubPkgX")
3191 );
3192
3193 assert_eq!(el_ref_base_b.character_data().unwrap().string_value().unwrap(), "Ecu");
3195 assert_eq!(el_ref_base_b.get_reference_target().unwrap(), el_ecu);
3196 assert_eq!(el_ecu.path().unwrap(), "/ContentPkg/SubPkgX/Ecu");
3197 assert_eq!(
3198 el_ref_base_c.get_reference_target().unwrap().path().unwrap(),
3199 "/ContentPkg2/Ecu"
3200 );
3201 assert_eq!(model.get_references_to("/ContentPkg/SubPkgX/Ecu").len(), 1);
3202 assert_eq!(model.get_references_to("/ContentPkg/SubPkgX").len(), 1);
3203 assert!(model.check_references().is_empty());
3204 assert_eq!(model.verify_reference_caches(), Ok(()));
3205 }
3206
3207 #[test]
3208 fn reference_base_scope_respects_path_boundaries() {
3209 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
3213<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">
3214 <AR-PACKAGES>
3215 <AR-PACKAGE><SHORT-NAME>Pkg1</SHORT-NAME>
3216 <REFERENCE-BASES>
3217 <REFERENCE-BASE>
3218 <SHORT-LABEL>Base</SHORT-LABEL>
3219 <PACKAGE-REF DEST="AR-PACKAGE">/Target</PACKAGE-REF>
3220 </REFERENCE-BASE>
3221 </REFERENCE-BASES>
3222 <ELEMENTS>
3223 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
3224 <FIBEX-ELEMENTS>
3225 <FIBEX-ELEMENT-REF-CONDITIONAL>
3226 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="Base">Ecu</FIBEX-ELEMENT-REF>
3227 </FIBEX-ELEMENT-REF-CONDITIONAL>
3228 </FIBEX-ELEMENTS>
3229 </SYSTEM>
3230 </ELEMENTS>
3231 <AR-PACKAGES>
3232 <AR-PACKAGE><SHORT-NAME>Sub</SHORT-NAME>
3233 <ELEMENTS>
3234 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
3235 <FIBEX-ELEMENTS>
3236 <FIBEX-ELEMENT-REF-CONDITIONAL>
3237 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="Base">Ecu</FIBEX-ELEMENT-REF>
3238 </FIBEX-ELEMENT-REF-CONDITIONAL>
3239 </FIBEX-ELEMENTS>
3240 </SYSTEM>
3241 </ELEMENTS>
3242 </AR-PACKAGE>
3243 </AR-PACKAGES>
3244 </AR-PACKAGE>
3245 <AR-PACKAGE><SHORT-NAME>Pkg10</SHORT-NAME>
3246 <ELEMENTS>
3247 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
3248 <FIBEX-ELEMENTS>
3249 <FIBEX-ELEMENT-REF-CONDITIONAL>
3250 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="Base">Ecu</FIBEX-ELEMENT-REF>
3251 </FIBEX-ELEMENT-REF-CONDITIONAL>
3252 </FIBEX-ELEMENTS>
3253 </SYSTEM>
3254 </ELEMENTS>
3255 </AR-PACKAGE>
3256 <AR-PACKAGE><SHORT-NAME>Target</SHORT-NAME>
3257 <ELEMENTS>
3258 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
3259 </ELEMENTS>
3260 </AR-PACKAGE>
3261 </AR-PACKAGES>
3262</AUTOSAR>"#.as_bytes();
3263 let model = AutosarModel::new();
3264 model.load_buffer(FILEBUF, "test", true).unwrap();
3265
3266 let get_reference = |system_path: &str| {
3267 model
3268 .get_element_by_path(system_path)
3269 .and_then(|e| e.get_sub_element(ElementName::FibexElements))
3270 .and_then(|e| e.get_sub_element_at(0))
3271 .and_then(|e| e.get_sub_element(ElementName::FibexElementRef))
3272 .unwrap()
3273 };
3274
3275 let el_declaring = get_reference("/Pkg1/System");
3277 assert_eq!(el_declaring.resolve_reference_base("Base").as_deref(), Some("/Target"));
3278 assert_eq!(
3279 el_declaring.get_reference_target().unwrap().path().unwrap(),
3280 "/Target/Ecu"
3281 );
3282 let el_nested = get_reference("/Pkg1/Sub/System");
3284 assert_eq!(el_nested.resolve_reference_base("Base").as_deref(), Some("/Target"));
3285 assert_eq!(el_nested.get_reference_target().unwrap().path().unwrap(), "/Target/Ecu");
3286 let el_sibling = get_reference("/Pkg10/System");
3289 assert_eq!(el_sibling.resolve_reference_base("Base"), None);
3290 assert!(el_sibling.get_reference_target().is_err());
3291
3292 assert_eq!(model.check_references().len(), 1);
3293 assert_eq!(model.get_references_to("/Target/Ecu").len(), 2);
3294 assert_eq!(model.verify_reference_caches(), Ok(()));
3295 }
3296
3297 #[test]
3298 fn cyclic_reference_base_terminates() {
3299 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
3302<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">
3303 <AR-PACKAGES>
3304 <AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
3305 <REFERENCE-BASES>
3306 <REFERENCE-BASE>
3307 <SHORT-LABEL>SelfRef</SHORT-LABEL>
3308 <PACKAGE-REF DEST="AR-PACKAGE" BASE="SelfRef">Sub</PACKAGE-REF>
3309 </REFERENCE-BASE>
3310 <REFERENCE-BASE>
3311 <SHORT-LABEL>MutualA</SHORT-LABEL>
3312 <PACKAGE-REF DEST="AR-PACKAGE" BASE="MutualB">SubA</PACKAGE-REF>
3313 </REFERENCE-BASE>
3314 <REFERENCE-BASE>
3315 <SHORT-LABEL>MutualB</SHORT-LABEL>
3316 <PACKAGE-REF DEST="AR-PACKAGE" BASE="MutualA">SubB</PACKAGE-REF>
3317 </REFERENCE-BASE>
3318 </REFERENCE-BASES>
3319 <ELEMENTS>
3320 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
3321 <FIBEX-ELEMENTS>
3322 <FIBEX-ELEMENT-REF-CONDITIONAL>
3323 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="SelfRef">Ecu</FIBEX-ELEMENT-REF>
3324 </FIBEX-ELEMENT-REF-CONDITIONAL>
3325 <FIBEX-ELEMENT-REF-CONDITIONAL>
3326 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="MutualA">Ecu</FIBEX-ELEMENT-REF>
3327 </FIBEX-ELEMENT-REF-CONDITIONAL>
3328 </FIBEX-ELEMENTS>
3329 </SYSTEM>
3330 </ELEMENTS>
3331 </AR-PACKAGE>
3332 </AR-PACKAGES>
3333</AUTOSAR>"#.as_bytes();
3334 let model = AutosarModel::new();
3335 model.load_buffer(FILEBUF, "test", true).unwrap();
3336
3337 let el_fibex_elements = model
3338 .get_element_by_path("/Pkg/System")
3339 .and_then(|e| e.get_sub_element(ElementName::FibexElements))
3340 .unwrap();
3341 let mut references = el_fibex_elements
3342 .sub_elements()
3343 .filter_map(|ferc| ferc.get_sub_element(ElementName::FibexElementRef));
3344 let el_self_cycle = references.next().unwrap();
3346 assert_eq!(el_self_cycle.resolve_reference_base("SelfRef"), None);
3347 assert!(el_self_cycle.get_reference_target().is_err());
3348 let el_mutual_cycle = references.next().unwrap();
3350 assert_eq!(el_mutual_cycle.resolve_reference_base("MutualA"), None);
3351 assert!(el_mutual_cycle.get_reference_target().is_err());
3352
3353 assert_eq!(model.check_references().len(), 5);
3356 assert_eq!(model.verify_reference_caches(), Ok(()));
3357 }
3358
3359 #[test]
3360 fn load_duplicate_reference_bases() {
3361 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
3366<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">
3367 <AR-PACKAGES>
3368 <AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
3369 <REFERENCE-BASES>
3370 <REFERENCE-BASE>
3371 <SHORT-LABEL>Base</SHORT-LABEL>
3372 <PACKAGE-REF DEST="AR-PACKAGE">/Target</PACKAGE-REF>
3373 </REFERENCE-BASE>
3374 </REFERENCE-BASES>
3375 </AR-PACKAGE>
3376 </AR-PACKAGES>
3377</AUTOSAR>"#.as_bytes();
3378 let model = AutosarModel::new();
3379 model.load_buffer(FILEBUF, "file1", true).unwrap();
3380 model.load_buffer(FILEBUF, "file2", true).unwrap();
3381
3382 let el_reference_bases = model
3384 .get_element_by_path("/Pkg")
3385 .and_then(|e| e.get_sub_element(ElementName::ReferenceBases))
3386 .unwrap();
3387 assert_eq!(el_reference_bases.sub_elements().count(), 1);
3388 let el_package_ref = el_reference_bases
3389 .get_sub_element_at(0)
3390 .and_then(|e| e.get_sub_element(ElementName::PackageRef))
3391 .unwrap();
3392 assert_eq!(
3393 el_package_ref.resolve_reference_base("Base").as_deref(),
3394 Some("/Target")
3395 );
3396
3397 let el_package = model.get_element_by_path("/Pkg").unwrap();
3399 el_package.remove_sub_element(el_reference_bases).unwrap();
3400 assert_eq!(el_package_ref.resolve_reference_base("Base"), None);
3401 assert_eq!(model.verify_reference_caches(), Ok(()));
3402 }
3403
3404 #[test]
3405 fn duplicate_model_resolves_relative_references() {
3406 let model = AutosarModel::new();
3409 model.load_buffer(FILEBUF1_COMPLEX_BASES, "test", true).unwrap();
3410 let copy = model.duplicate().unwrap();
3411
3412 let el_fibex_element_ref = copy
3413 .get_element_by_path("/BasesPkg/SubPackage/System")
3414 .and_then(|e| e.get_sub_element(ElementName::FibexElements))
3415 .and_then(|e| e.get_sub_element_at(0))
3416 .and_then(|e| e.get_sub_element(ElementName::FibexElementRef))
3417 .unwrap();
3418 assert_eq!(
3419 el_fibex_element_ref.get_reference_target().unwrap().path().unwrap(),
3420 "/ContentPkg/SubPackage/Ecu"
3421 );
3422 assert!(copy.check_references().is_empty());
3423 assert_eq!(model.verify_reference_caches(), Ok(()));
3424 }
3425}