From 06180b31a1a3b6ae3ea2174f639a206aba7841a5 Mon Sep 17 00:00:00 2001 From: Daniel Thaler <daniel@dthaler.de> Date: Sun, 29 Dec 2024 14:08:50 +0100 Subject: [PATCH] improvements for the merge function - TYPEDEF_CHARACTERISTIC may reference a MEASUREMENT, but this reference was not updated during merges - USER_RIGHTS can no longer get duplicate user entries as a result of the merge - all items are merged in the order in which they appear in the merge file. Previously they were merged in reverse order - full unit tests --- a2lfile/src/merge.rs | 1752 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 1432 insertions(+), 320 deletions(-) diff --git a/a2lfile/src/merge.rs b/a2lfile/src/merge.rs index c8b2ad2..cf8051e 100644 --- a/a2lfile/src/merge.rs +++ b/a2lfile/src/merge.rs @@ -28,10 +28,7 @@ pub(crate) fn merge_modules(orig_module: &mut Module, merge_module: &mut Module) // merge MOD_COMMON - depends on RECORD_LAYOUT merge_mod_common(orig_module, merge_module); - // merge TYPEDEF_* - no dependencies - merge_typedef(orig_module, merge_module); - - // merge AXIS_PTS, CHARACTERISTIC, MEASUREMENT, INSTANCE, BLOB + // merge AXIS_PTS, CHARACTERISTIC, MEASUREMENT, INSTANCE, BLOB, TYPEDEF_* // depends on each other, as well as COMPU_METHOD, TYPEDEF_* and MOD_COMMON.MEMORY_SEGMENT merge_objects(orig_module, merge_module); @@ -86,9 +83,10 @@ fn merge_mod_par(orig_module: &mut Module, merge_module: &mut Module) { fn merge_memory_layout(orig_module: &mut Module, merge_module: &mut Module) { let orig_memory_layout = &mut orig_module.mod_par.as_mut().unwrap().memory_layout; - let merge_memory_layout = &mut merge_module.mod_par.as_mut().unwrap().memory_layout; + let merge_memory_layout = + std::mem::take(&mut merge_module.mod_par.as_mut().unwrap().memory_layout); - while let Some(mut merge_ml) = merge_memory_layout.pop() { + for mut merge_ml in merge_memory_layout { let mut is_equal = false; // compare with every existing MEMORY_LAYOUT, and only take new ones for orig_ml in orig_memory_layout.iter() { @@ -116,7 +114,8 @@ fn merge_memory_segment(orig_module: &mut Module, merge_module: &mut Module) { let orig_mod_par = orig_module.mod_par.as_mut().unwrap(); let merge_mod_par = merge_module.mod_par.as_mut().unwrap(); - while let Some(mut memory_segment) = merge_mod_par.memory_segment.pop() { + let memory_segment_list = std::mem::take(&mut merge_mod_par.memory_segment); + for mut memory_segment in memory_segment_list { if let Some(true) = merge_action.get(&memory_segment.name) { memory_segment.reset_location(); orig_mod_par.memory_segment.push(memory_segment); @@ -166,14 +165,15 @@ fn rename_memory_segments(merge_module: &mut Module, rename_table: &HashMap<Stri fn merge_system_constant(orig_module: &mut Module, merge_module: &mut Module) { let orig_system_constant = &mut orig_module.mod_par.as_mut().unwrap().system_constant; - let merge_system_constant = &mut merge_module.mod_par.as_mut().unwrap().system_constant; + let merge_system_constant = + std::mem::take(&mut merge_module.mod_par.as_mut().unwrap().system_constant); let orig_sc_names: HashSet<String> = orig_system_constant .iter() .map(|sc| sc.name.clone()) .collect(); - while let Some(mut merge_sysconst) = merge_system_constant.pop() { + for mut merge_sysconst in merge_system_constant { if !orig_sc_names.contains(&merge_sysconst.name) { merge_sysconst.reset_location(); orig_system_constant.push(merge_sysconst); @@ -207,7 +207,8 @@ fn merge_unit(orig_module: &mut Module, merge_module: &mut Module) { rename_units(merge_module, &rename_table); - while let Some(mut unit) = merge_module.unit.pop() { + let merge_unit_list = std::mem::take(&mut merge_module.unit); + for mut unit in merge_unit_list { if let Some(true) = merge_action.get(&unit.name) { unit.reset_location(); orig_module.unit.push(unit); @@ -245,19 +246,22 @@ fn merge_compu_tab(orig_module: &mut Module, merge_module: &mut Module) { rename_compu_tabs(merge_module, &rename_table); - while let Some(mut compu_tab) = merge_module.compu_tab.pop() { + let merge_compu_tab_list = std::mem::take(&mut merge_module.compu_tab); + for mut compu_tab in merge_compu_tab_list { if let Some(true) = merge_action.get(&compu_tab.name) { compu_tab.reset_location(); orig_module.compu_tab.push(compu_tab); } } - while let Some(mut compu_vtab) = merge_module.compu_vtab.pop() { + let merge_compu_vtab_list = std::mem::take(&mut merge_module.compu_vtab); + for mut compu_vtab in merge_compu_vtab_list { if let Some(true) = merge_action.get(&compu_vtab.name) { compu_vtab.reset_location(); orig_module.compu_vtab.push(compu_vtab); } } - while let Some(mut compu_vtab_range) = merge_module.compu_vtab_range.pop() { + let merge_compu_vtab_range_list = std::mem::take(&mut merge_module.compu_vtab_range); + for mut compu_vtab_range in merge_compu_vtab_range_list { if let Some(true) = merge_action.get(&compu_vtab_range.name) { compu_vtab_range.reset_location(); orig_module.compu_vtab_range.push(compu_vtab_range); @@ -311,7 +315,8 @@ fn merge_compu_method(orig_module: &mut Module, merge_module: &mut Module) { rename_compu_methods(merge_module, &rename_table); - while let Some(mut compu_method) = merge_module.compu_method.pop() { + let merge_compu_method_list = std::mem::take(&mut merge_module.compu_method); + for mut compu_method in merge_compu_method_list { if let Some(true) = merge_action.get(&compu_method.name) { compu_method.reset_location(); orig_module.compu_method.push(compu_method); @@ -387,7 +392,8 @@ fn merge_record_layout(orig_module: &mut Module, merge_module: &mut Module) { rename_record_layouts(merge_module, &rename_table); - while let Some(mut record_layout) = merge_module.record_layout.pop() { + let merge_record_layout_list = std::mem::take(&mut merge_module.record_layout); + for mut record_layout in merge_record_layout_list { if let Some(true) = merge_action.get(&record_layout.name) { record_layout.reset_location(); orig_module.record_layout.push(record_layout); @@ -450,9 +456,12 @@ fn merge_mod_common(orig_module: &mut Module, merge_module: &mut Module) { } } -// ------------------------ AXIS_PTS, CHARACTERISTIC, MEASUREMENT, INSTANCE, BLOB ------------------------ +// ----------- AXIS_PTS, CHARACTERISTIC, MEASUREMENT, INSTANCE, BLOB, TYPEDEF_* ----------- fn merge_objects(orig_module: &mut Module, merge_module: &mut Module) { + // objects and typedefs depend on each other. + // Specifically, the INSTANCE object may reference any TYPEDEF_*, while TYPDEFE_CHARACTERISTIC may reference any MEASUREMENT + // As a result, all renaming needs to be done first, and then the items can be merged. let mut log_msgs = Vec::<String>::new(); let orig_map = build_namemap_object(orig_module, &mut log_msgs); let merge_map = build_namemap_object(merge_module, &mut log_msgs); @@ -460,36 +469,88 @@ fn merge_objects(orig_module: &mut Module, merge_module: &mut Module) { rename_objects(merge_module, &object_rename_table); - while let Some(mut axis_pts) = merge_module.axis_pts.pop() { + let orig_map = build_namemap_typedef(orig_module, &mut log_msgs); + let merge_map = build_namemap_typedef(merge_module, &mut log_msgs); + let (merge_action, rename_table) = calculate_item_actions(&orig_map, &merge_map); + + rename_typedefs(merge_module, &rename_table); + + // merge all objects + let merge_axis_pts_list = std::mem::take(&mut merge_module.axis_pts); + for mut axis_pts in merge_axis_pts_list { if let Some(true) = object_merge_action.get(&axis_pts.name) { axis_pts.reset_location(); orig_module.axis_pts.push(axis_pts); } } - while let Some(mut blob) = merge_module.blob.pop() { + let merge_blob_list = std::mem::take(&mut merge_module.blob); + for mut blob in merge_blob_list { if let Some(true) = object_merge_action.get(&blob.name) { blob.reset_location(); orig_module.blob.push(blob); } } - while let Some(mut characteristic) = merge_module.characteristic.pop() { + let merge_characteristic_list = std::mem::take(&mut merge_module.characteristic); + for mut characteristic in merge_characteristic_list { if let Some(true) = object_merge_action.get(&characteristic.name) { characteristic.reset_location(); orig_module.characteristic.push(characteristic); } } - while let Some(mut instance) = merge_module.instance.pop() { + let merge_instance_list = std::mem::take(&mut merge_module.instance); + for mut instance in merge_instance_list { if let Some(true) = object_merge_action.get(&instance.name) { instance.reset_location(); orig_module.instance.push(instance); } } - while let Some(mut measurement) = merge_module.measurement.pop() { + let merge_measurement_list = std::mem::take(&mut merge_module.measurement); + for mut measurement in merge_measurement_list { if let Some(true) = object_merge_action.get(&measurement.name) { measurement.reset_location(); orig_module.measurement.push(measurement); } } + + // merge all TYPEDEF_* + let merge_typedef_axis_list = std::mem::take(&mut merge_module.typedef_axis); + for mut typedef_axis in merge_typedef_axis_list { + if let Some(true) = merge_action.get(&typedef_axis.name) { + typedef_axis.reset_location(); + orig_module.typedef_axis.push(typedef_axis); + } + } + let merge_typedef_blob_list = std::mem::take(&mut merge_module.typedef_blob); + for mut typedef_blob in merge_typedef_blob_list { + if let Some(true) = merge_action.get(&typedef_blob.name) { + typedef_blob.reset_location(); + orig_module.typedef_blob.push(typedef_blob); + } + } + let merge_typedef_characteristic_list = + std::mem::take(&mut merge_module.typedef_characteristic); + for mut typedef_characteristic in merge_typedef_characteristic_list { + if let Some(true) = merge_action.get(&typedef_characteristic.name) { + typedef_characteristic.reset_location(); + orig_module + .typedef_characteristic + .push(typedef_characteristic); + } + } + let merge_typedef_measurement_list = std::mem::take(&mut merge_module.typedef_measurement); + for mut typedef_measurement in merge_typedef_measurement_list { + if let Some(true) = merge_action.get(&typedef_measurement.name) { + typedef_measurement.reset_location(); + orig_module.typedef_measurement.push(typedef_measurement); + } + } + let merge_typedef_struct_list = std::mem::take(&mut merge_module.typedef_structure); + for mut typedef_structure in merge_typedef_struct_list { + if let Some(true) = merge_action.get(&typedef_structure.name) { + typedef_structure.reset_location(); + orig_module.typedef_structure.push(typedef_structure); + } + } } fn rename_objects(merge_module: &mut Module, rename_table: &HashMap<String, String>) { @@ -658,6 +719,10 @@ fn rename_objects(merge_module: &mut Module, rename_table: &HashMap<String, Stri } } } + // MODULE.VARIANT_CODING.VAR_CHARACTERISTIC + for var_characteristic in &mut variant_coding.var_characteristic { + rename_item_list(&mut var_characteristic.criterion_name_list, rename_table); + } } } @@ -671,7 +736,8 @@ fn merge_function(orig_module: &mut Module, merge_module: &mut Module) { .map(|(i, f)| (f.name.clone(), i)) .collect(); - while let Some(mut function) = merge_module.function.pop() { + let merge_function_list = std::mem::take(&mut merge_module.function); + for mut function in merge_function_list { if let Some(idx) = orig_map.get(&function.name) { // a function with this name already exists in the original module let orig_function = &mut orig_module.function[*idx]; @@ -769,7 +835,8 @@ fn merge_group(orig_module: &mut Module, merge_module: &mut Module) { .map(|(i, g)| (g.name.clone(), i)) .collect(); - while let Some(mut group) = merge_module.group.pop() { + let merge_group_list = std::mem::take(&mut merge_module.group); + for mut group in merge_group_list { if let Some(idx) = orig_map.get(&group.name) { // a group with this name already exists in the original module let orig_group = &mut orig_module.group[*idx]; @@ -868,7 +935,8 @@ fn merge_frame(orig_module: &mut Module, merge_module: &mut Module) { rename_frames(merge_module, &rename_table); - while let Some(mut frame) = merge_module.frame.pop() { + let merge_frame_list = std::mem::take(&mut merge_module.frame); + for mut frame in merge_frame_list { if let Some(true) = merge_action.get(&frame.name) { frame.reset_location(); orig_module.frame.push(frame); @@ -898,7 +966,8 @@ fn merge_transformer(orig_module: &mut Module, merge_module: &mut Module) { rename_transformers(merge_module, &rename_table); - while let Some(mut transformer) = merge_module.transformer.pop() { + let merge_transformer_list = std::mem::take(&mut merge_module.transformer); + for mut transformer in merge_transformer_list { if let Some(true) = merge_action.get(&transformer.name) { transformer.reset_location(); orig_module.transformer.push(transformer); @@ -923,48 +992,6 @@ fn rename_transformers(merge_module: &mut Module, rename_table: &HashMap<String, // ------------------------ TYPEDEF_* ------------------------ -fn merge_typedef(orig_module: &mut Module, merge_module: &mut Module) { - let mut log_msgs = Vec::<String>::new(); - let orig_map = build_namemap_typedef(orig_module, &mut log_msgs); - let merge_map = build_namemap_typedef(merge_module, &mut log_msgs); - let (merge_action, rename_table) = calculate_item_actions(&orig_map, &merge_map); - - rename_typedefs(merge_module, &rename_table); - - while let Some(mut typedef_axis) = merge_module.typedef_axis.pop() { - if let Some(true) = merge_action.get(&typedef_axis.name) { - typedef_axis.reset_location(); - orig_module.typedef_axis.push(typedef_axis); - } - } - while let Some(mut typedef_blob) = merge_module.typedef_blob.pop() { - if let Some(true) = merge_action.get(&typedef_blob.name) { - typedef_blob.reset_location(); - orig_module.typedef_blob.push(typedef_blob); - } - } - while let Some(mut typedef_characteristic) = merge_module.typedef_characteristic.pop() { - if let Some(true) = merge_action.get(&typedef_characteristic.name) { - typedef_characteristic.reset_location(); - orig_module - .typedef_characteristic - .push(typedef_characteristic); - } - } - while let Some(mut typedef_measurement) = merge_module.typedef_measurement.pop() { - if let Some(true) = merge_action.get(&typedef_measurement.name) { - typedef_measurement.reset_location(); - orig_module.typedef_measurement.push(typedef_measurement); - } - } - while let Some(mut typedef_structure) = merge_module.typedef_structure.pop() { - if let Some(true) = merge_action.get(&typedef_structure.name) { - typedef_structure.reset_location(); - orig_module.typedef_structure.push(typedef_structure); - } - } -} - fn rename_typedefs(merge_module: &mut Module, rename_table: &HashMap<String, String>) { if rename_table.is_empty() { return; @@ -1018,14 +1045,16 @@ fn rename_typedefs(merge_module: &mut Module, rename_table: &HashMap<String, Str // ------------------------ USER_RIGHTS ------------------------ fn merge_user_rights(orig_module: &mut Module, merge_module: &mut Module) { - // there is no renaming here; as far as I can tell there is no requirement that there should only be one entry per user id - while let Some(mut merge_user_rights) = merge_module.user_rights.pop() { + // the specification only allows one user rights block per user id + // If a merged user rights block already exists in the original module, it will be ignored + let merge_user_rights_list = std::mem::take(&mut merge_module.user_rights); + for mut merge_user_rights in merge_user_rights_list { merge_user_rights.reset_location(); - // don't create any exact duplicates, but copy everything else + // make sure not to create any duplicates if !orig_module .user_rights .iter() - .any(|user_rights| user_rights == &merge_user_rights) + .any(|user_rights| user_rights.user_level_id == merge_user_rights.user_level_id) { orig_module.user_rights.push(merge_user_rights); } @@ -1147,224 +1176,1128 @@ mod test { } #[test] - fn test_merge_mod_par() { - static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; - static FILE_B: &str = r#"/begin PROJECT p "" + fn test_merge_measurement() { + static FILE_A: &str = + r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" - /begin MOD_PAR "" - /begin MEMORY_LAYOUT PRG_DATA 0x1000 1 0 0 0 0 0 /end MEMORY_LAYOUT - /begin MEMORY_LAYOUT PRG_DATA 0x1234 1 0 0 0 0 0 /end MEMORY_LAYOUT - /begin MEMORY_SEGMENT equal_seg "" DATA FLASH INTERN 0 0x1000 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT - /begin MEMORY_SEGMENT seg_b "" DATA FLASH INTERN 0 0x1234 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT - SYSTEM_CONSTANT "System_Constant_1" "1" - SYSTEM_CONSTANT "System_Constant_2" "2" - /end MOD_PAR + /begin MEASUREMENT measurement1 "" FLOAT32_IEEE conversion 1 1.0 0 100 + /end MEASUREMENT /end MODULE /end PROJECT"#; - static FILE_C: &str = r#"/begin PROJECT p "" + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" - /begin MOD_PAR "" - /begin MEMORY_LAYOUT PRG_DATA 0x1000 1 0 0 0 0 0 /end MEMORY_LAYOUT - /begin MEMORY_LAYOUT PRG_DATA 0xFFFFFF 1 0 0 0 0 0 /end MEMORY_LAYOUT - /begin MEMORY_SEGMENT equal_seg "" DATA FLASH INTERN 0 0x1000 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT - /begin MEMORY_SEGMENT seg_b "" DATA FLASH INTERN 0 0x1235 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT - /begin MEMORY_SEGMENT seg_c "" DATA FLASH INTERN 0 0x1234 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT - SYSTEM_CONSTANT "System_Constant_1" "1" - SYSTEM_CONSTANT "System_Constant_3" "333" - /end MOD_PAR - /begin AXIS_PTS axispts_name "long_identifier" 0x1234 input_qty deposit_record 0 conversion 3 0.0 10.0 - REF_MEMORY_SEGMENT seg_b + /begin MEASUREMENT measurement1 "" SWORD conversion 1 1.0 0 100 + /end MEASUREMENT + /begin MEASUREMENT measurement2 "" FLOAT32_IEEE conversion 1 1.0 0 100 + /end MEASUREMENT + + /begin AXIS_PTS axispts1 "" 0x1234 measurement1 deposit_record 0 conversion 3 0.0 10.0 /end AXIS_PTS - /begin CHARACTERISTIC characteristic_name "long_identifier" VALUE 0x1234 deposit_ident 0 conversion 0.0 10.0 - REF_MEMORY_SEGMENT seg_b + /begin AXIS_PTS axispts2 "" 0x1234 measurement2 deposit_record 0 conversion 3 0.0 10.0 + /end AXIS_PTS + /begin CHARACTERISTIC characteristic1 "" CURVE 0x1234 record_layout_name 0 compu_method 0.0 1.0 + /begin AXIS_DESCR COM_AXIS measurement1 compu_method 1 0 100 + /end AXIS_DESCR /end CHARACTERISTIC - /begin MEASUREMENT measurement_name "long_identifier" FLOAT32_IEEE conversion 1 1.0 0 100 - REF_MEMORY_SEGMENT seg_b - /end MEASUREMENT + /begin CHARACTERISTIC characteristic2 "" CURVE 0x1234 record_layout_name 0 compu_method 0.0 1.0 + /begin AXIS_DESCR COM_AXIS measurement2 compu_method 1 0 100 + /end AXIS_DESCR + /end CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VALUE record_layout_name 0 compu_method 0 100 + /begin AXIS_DESCR COM_AXIS measurement1 compu_method 1 0 100 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic2 "" VALUE record_layout_name 0 compu_method 0 100 + /begin AXIS_DESCR COM_AXIS measurement2 compu_method 1 0 100 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /begin FRAME frame1 "" 1 2 + FRAME_MEASUREMENT measurement1 measurement2 + /end FRAME + /begin VARIANT_CODING + /begin VAR_CRITERION criterion_name1 "" + VAR_MEASUREMENT measurement1 + /end VAR_CRITERION + /begin VAR_CRITERION criterion_name2 "" + VAR_MEASUREMENT measurement2 + /end VAR_CRITERION + /end VARIANT_CODING /end MODULE /end PROJECT"#; let mut log_msgs = vec![]; - // merging B into A -> A has no MOD_PAR, so it is taken from B - let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); - let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); - assert!(a2l_file_a.project.module[0].mod_par.is_none()); + // merging B into A -> A has no MEASUREMENT, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + assert!(a2l_file_a.project.module[0].measurement.is_empty()); a2l_file_a.merge_modules(&mut a2l_file_b); - assert!(a2l_file_a.project.module[0].mod_par.is_some()); + assert_eq!(a2l_file_a.project.module[0].measurement.len(), 1); // merging C into B - let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); - let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); - assert!(a2l_file_b.project.module[0].mod_par.is_some()); - assert!(a2l_file_c.project.module[0].mod_par.is_some()); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + assert_eq!(a2l_file_b.project.module[0].measurement.len(), 1); + assert_eq!(a2l_file_c.project.module[0].measurement.len(), 2); a2l_file_b.merge_modules(&mut a2l_file_c); - let mod_par_b = a2l_file_b.project.module[0].mod_par.as_ref().unwrap(); - assert_eq!(mod_par_b.memory_layout.len(), 3); - assert_eq!(mod_par_b.memory_segment.len(), 4); - assert_eq!(mod_par_b.system_constant.len(), 3); - // the references to MEMORY_SGMENT seg_b should be renamed - assert_ne!( - a2l_file_b.project.module[0].axis_pts[0] - .ref_memory_segment - .as_ref() - .unwrap() - .name, - "seg_b" + assert_eq!(a2l_file_b.project.module[0].measurement.len(), 3); + + let module = &a2l_file_b.project.module[0]; + + // check that references to measurement1 in file C have been renamed to measurement1.MERGE + let axispts1 = &module.axis_pts[0]; + assert_eq!(axispts1.input_quantity, "measurement1.MERGE"); + let axispts2 = &module.axis_pts[1]; + assert_eq!(axispts2.input_quantity, "measurement2"); + + let characteristic1 = &module.characteristic[0]; + assert_eq!( + characteristic1.axis_descr[0].input_quantity, + "measurement1.MERGE" ); - assert_ne!( - a2l_file_b.project.module[0].characteristic[0] - .ref_memory_segment - .as_ref() - .unwrap() - .name, - "seg_b" + let characteristic2 = &module.characteristic[1]; + assert_eq!(characteristic2.axis_descr[0].input_quantity, "measurement2"); + + let typedef_characteristic1 = &module.typedef_characteristic[0]; + assert_eq!( + typedef_characteristic1.axis_descr[0].input_quantity, + "measurement1.MERGE" ); - assert_ne!( - a2l_file_b.project.module[0].measurement[0] - .ref_memory_segment - .as_ref() - .unwrap() - .name, - "seg_b" + let typedef_characteristic2 = &module.typedef_characteristic[1]; + assert_eq!( + typedef_characteristic2.axis_descr[0].input_quantity, + "measurement2" + ); + + let frame = &module.frame[0]; + let frame_measurement = frame.frame_measurement.as_ref().unwrap(); + assert_eq!(frame_measurement.identifier_list.len(), 2); + assert_eq!(frame_measurement.identifier_list[0], "measurement1.MERGE"); + assert_eq!(frame_measurement.identifier_list[1], "measurement2"); + + let variant_coding = &module.variant_coding.as_ref().unwrap(); + let var_criterion1 = &variant_coding.var_criterion[0]; + assert_eq!( + var_criterion1.var_measurement.as_ref().unwrap().name, + "measurement1.MERGE" + ); + let var_criterion2 = &variant_coding.var_criterion[1]; + assert_eq!( + var_criterion2.var_measurement.as_ref().unwrap().name, + "measurement2" ); } #[test] - fn test_merge_compu_tab() { - let mut log_msgs = vec![]; + fn test_typedef_measurement() { static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; static FILE_B: &str = r#"/begin PROJECT p "" /begin MODULE m "" - /begin COMPU_TAB ct1 "" TAB_NOINTP 1 - 1 1 - /end COMPU_TAB - /begin COMPU_VTAB cvt1 "" TAB_VERB 1 - 1 "v" - /end COMPU_VTAB - /begin COMPU_VTAB_RANGE cvtr1 TAB_VERB 1 - 1 2 "v" - /end COMPU_VTAB_RANGE - /begin COMPU_TAB ct2 "" TAB_NOINTP 1 - 1 1 - /end COMPU_TAB - /begin COMPU_VTAB cvt2 "" TAB_VERB 1 - 1 "v" - /end COMPU_VTAB - /begin COMPU_VTAB_RANGE cvtr2 TAB_VERB 1 - 1 2 "v" - /end COMPU_VTAB_RANGE + /begin TYPEDEF_MEASUREMENT td_measurement1 "" FLOAT32_IEEE conversion 1 1.0 0 100 + /end TYPEDEF_MEASUREMENT /end MODULE /end PROJECT"#; static FILE_C: &str = r#"/begin PROJECT p "" /begin MODULE m "" - /begin COMPU_TAB ct1 "" TAB_NOINTP 1 - 1 1 - /end COMPU_TAB - /begin COMPU_VTAB cvt1 "" TAB_VERB 1 - 1 "v" - /end COMPU_VTAB - /begin COMPU_VTAB_RANGE cvtr1 TAB_VERB 1 - 1 2 "v" - /end COMPU_VTAB_RANGE - /begin COMPU_TAB ct2 "" TAB_NOINTP 1 - 2 2 - /end COMPU_TAB - /begin COMPU_VTAB cvt2 "" TAB_VERB 1 - 2 "v" - /end COMPU_VTAB - /begin COMPU_VTAB_RANGE cvtr2 TAB_VERB 1 - 2 3 "v" - /end COMPU_VTAB_RANGE - /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" - COMPU_TAB_REF ct2 - /end COMPU_METHOD - /begin COMPU_METHOD m2 "" TAB_VERB "%6.3" "" - COMPU_TAB_REF cvt2 - /end COMPU_METHOD - /begin COMPU_METHOD m3 "" TAB_VERB "%6.3" "" - COMPU_TAB_REF cvtr2 - /end COMPU_METHOD + /begin TYPEDEF_MEASUREMENT td_measurement1 "" SWORD conversion 1 1.0 0 100 + /end TYPEDEF_MEASUREMENT + /begin TYPEDEF_MEASUREMENT td_measurement2 "" FLOAT32_IEEE conversion 1 1.0 0 100 + /end TYPEDEF_MEASUREMENT + + /begin TYPEDEF_STRUCTURE typedef_structure1 "" 0 + /begin STRUCTURE_COMPONENT component1 td_measurement1 0 + /end STRUCTURE_COMPONENT + /begin STRUCTURE_COMPONENT component2 td_measurement2 1 + /end STRUCTURE_COMPONENT + /end TYPEDEF_STRUCTURE + /begin INSTANCE instance1 "" td_measurement1 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" td_measurement2 0x0 + /end INSTANCE /end MODULE /end PROJECT"#; - // merging B into A -> A has no COMPU_TABs, so they are all taken from B + let mut log_msgs = vec![]; + + // merging B into A -> A has no MEASUREMENT, so it is taken from B let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); - assert!(a2l_file_a.project.module[0].compu_tab.is_empty()); - assert!(a2l_file_a.project.module[0].compu_vtab.is_empty()); - assert!(a2l_file_a.project.module[0].compu_vtab_range.is_empty()); + assert!(a2l_file_a.project.module[0].measurement.is_empty()); a2l_file_a.merge_modules(&mut a2l_file_b); - assert_eq!(a2l_file_a.project.module[0].compu_tab.len(), 2); - assert_eq!(a2l_file_a.project.module[0].compu_vtab.len(), 2); - assert_eq!(a2l_file_a.project.module[0].compu_vtab_range.len(), 2); + assert_eq!(a2l_file_a.project.module[0].typedef_measurement.len(), 1); // merging C into B let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); - + assert_eq!(a2l_file_b.project.module[0].typedef_measurement.len(), 1); + assert_eq!(a2l_file_c.project.module[0].typedef_measurement.len(), 2); a2l_file_b.merge_modules(&mut a2l_file_c); - assert_eq!(a2l_file_b.project.module[0].compu_tab.len(), 3); - assert_eq!(a2l_file_b.project.module[0].compu_vtab.len(), 3); - assert_eq!(a2l_file_b.project.module[0].compu_vtab_range.len(), 3); + assert_eq!(a2l_file_b.project.module[0].typedef_measurement.len(), 3); + + let typedef_structure = &a2l_file_b.project.module[0].typedef_structure[0]; + assert_eq!(typedef_structure.structure_component.len(), 2); + assert_eq!( + typedef_structure.structure_component[0].component_type, + "td_measurement1.MERGE" + ); + assert_eq!( + typedef_structure.structure_component[1].component_type, + "td_measurement2" + ); } #[test] - fn test_merge_unit() { - let mut log_msgs = vec![]; + fn test_merge_characteristic() { static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; - static FILE_B: &str = r#"/begin PROJECT p "" + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" - /begin UNIT unit_name "" "x" DERIVED - /end UNIT - /begin UNIT unit_name2 "" "x" DERIVED - /end UNIT + /begin CHARACTERISTIC characteristic1 "" VALUE 0x1234 deposit_ident 0 conversion 0.0 10.0 + /end CHARACTERISTIC /end MODULE /end PROJECT"#; - static FILE_C: &str = r#"/begin PROJECT p "" + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" - /begin UNIT unit_name "" "x" DERIVED - /end UNIT - /begin UNIT unit_name2 "" "xyz" DERIVED - /end UNIT - /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" - REF_UNIT unit_name2 - /end COMPU_METHOD + /begin CHARACTERISTIC characteristic1 "" VALUE 0x1234 deposit_ident 0 foo 0.0 10.0 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + CURVE_AXIS_REF characteristic1 + /end AXIS_DESCR + /begin DEPENDENT_CHARACTERISTIC "x" characteristic1 /end DEPENDENT_CHARACTERISTIC + /end CHARACTERISTIC + /begin CHARACTERISTIC characteristic2 "" VALUE 0x1235 deposit_ident 0 conversion 0.0 10.0 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + CURVE_AXIS_REF characteristic2 + /end AXIS_DESCR + /begin DEPENDENT_CHARACTERISTIC "x" characteristic2 /end DEPENDENT_CHARACTERISTIC + /end CHARACTERISTIC + + /begin FUNCTION function_name "" + /begin DEF_CHARACTERISTIC characteristic1 characteristic2 + /end DEF_CHARACTERISTIC + /begin REF_CHARACTERISTIC characteristic1 characteristic2 + /end REF_CHARACTERISTIC + /end FUNCTION + /begin GROUP group_name "" ROOT + /begin REF_CHARACTERISTIC characteristic1 characteristic2 + /end REF_CHARACTERISTIC + /end GROUP + /begin VARIANT_CODING + /begin VAR_CRITERION criterion_name1 "" + VAR_SELECTION_CHARACTERISTIC characteristic1 + /end VAR_CRITERION + /begin VAR_CRITERION criterion_name2 "" + VAR_SELECTION_CHARACTERISTIC characteristic2 + /end VAR_CRITERION + /begin VAR_CHARACTERISTIC name + characteristic1 characteristic2 + /end VAR_CHARACTERISTIC + /end VARIANT_CODING /end MODULE /end PROJECT"#; - // merging B into A -> A has no UNIT, so it is taken from B + let mut log_msgs = vec![]; + + // merging B into A -> A has no CHARACTERISTIC, so it is taken from B let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); - assert!(a2l_file_a.project.module[0].unit.is_empty()); + assert!(a2l_file_a.project.module[0].characteristic.is_empty()); a2l_file_a.merge_modules(&mut a2l_file_b); - assert_eq!(a2l_file_a.project.module[0].unit.len(), 2); + assert_eq!(a2l_file_a.project.module[0].characteristic.len(), 1); // merging C into B - let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); - let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); - assert_eq!(a2l_file_b.project.module[0].unit.len(), 2); - println!("units: {:?}", a2l_file_b.project.module[0].unit); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + assert_eq!(a2l_file_b.project.module[0].characteristic.len(), 1); + assert_eq!(a2l_file_c.project.module[0].characteristic.len(), 2); a2l_file_b.merge_modules(&mut a2l_file_c); - assert_eq!(a2l_file_b.project.module[0].unit.len(), 3); - println!("units: {:?}", a2l_file_b.project.module[0].unit); - println!("cm: {:?}", a2l_file_b.project.module[0].compu_method); + assert_eq!(a2l_file_b.project.module[0].characteristic.len(), 3); + + let module = &a2l_file_b.project.module[0]; + let characteristic1 = &module.characteristic[0]; + assert_eq!(characteristic1.name, "characteristic1"); + + let characteristic1_merge = &module.characteristic[1]; + let curve_axis_ref1 = &characteristic1_merge.axis_descr[0].curve_axis_ref; + assert_eq!( + curve_axis_ref1.as_ref().unwrap().curve_axis, + "characteristic1.MERGE" + ); + let dep_characteristic1 = &characteristic1_merge + .dependent_characteristic + .as_ref() + .unwrap(); + assert_eq!( + dep_characteristic1.characteristic_list[0], + "characteristic1.MERGE" + ); + let characteristic2 = &module.characteristic[2]; + let curve_axis_ref2 = &characteristic2.axis_descr[0].curve_axis_ref; + assert_eq!( + curve_axis_ref2.as_ref().unwrap().curve_axis, + "characteristic2" + ); + let dep_characteristic2 = &characteristic2.dependent_characteristic.as_ref().unwrap(); + assert_eq!( + dep_characteristic2.characteristic_list[0], + "characteristic2" + ); + + let function = &a2l_file_b.project.module[0].function[0]; + let def_characteristic = &function.def_characteristic.as_ref().unwrap(); + assert_eq!( + def_characteristic.identifier_list[0], + "characteristic1.MERGE" + ); + assert_eq!(def_characteristic.identifier_list[1], "characteristic2"); + + let ref_characteristic = &function.ref_characteristic.as_ref().unwrap(); + assert_eq!( + ref_characteristic.identifier_list[0], + "characteristic1.MERGE" + ); + assert_eq!(ref_characteristic.identifier_list[1], "characteristic2"); + + let group = &a2l_file_b.project.module[0].group[0]; + let ref_characteristic = &group.ref_characteristic.as_ref().unwrap(); + assert_eq!( + ref_characteristic.identifier_list[0], + "characteristic1.MERGE" + ); + assert_eq!(ref_characteristic.identifier_list[1], "characteristic2"); } #[test] - fn merge_group() { - let mut log_msgs = vec![]; - static FILE_A: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" - /begin MEASUREMENT m1 "" FLOAT32_IEEE NO_COMPU_METHOD 1 1.0 0 100 - /end MEASUREMENT - /begin GROUP g1 "" - /begin REF_MEASUREMENT m1 /end REF_MEASUREMENT - /end GROUP - /begin FUNCTION f "" - /begin IN_MEASUREMENT m1 /end IN_MEASUREMENT - /begin LOC_MEASUREMENT m1 /end LOC_MEASUREMENT - /begin OUT_MEASUREMENT m1 /end OUT_MEASUREMENT - /end FUNCTION + fn test_merge_typedef_characteristic() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VALUE record_layout_name 0 compu_method_name 0 100 + /end TYPEDEF_CHARACTERISTIC + /begin INSTANCE instance1 "" typedef_characteristic1 0x100 + /end INSTANCE + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VAL_BLK record_layout_name 0 compu_method_name 0 100 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + CURVE_AXIS_REF instance1 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic2 "" VALUE record_layout_name 0 compu_method_name 0 100 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + CURVE_AXIS_REF instance2 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + + /begin INSTANCE instance1 "" typedef_characteristic1 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" typedef_characteristic2 0x0 + /end INSTANCE + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no CHARACTERISTIC, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0] + .typedef_characteristic + .is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].typedef_characteristic.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].typedef_characteristic.len(), 1); + assert_eq!(a2l_file_c.project.module[0].typedef_characteristic.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].typedef_characteristic.len(), 3); + + let module = &a2l_file_b.project.module[0]; + let td_chara1 = &module.typedef_characteristic[0]; + assert_eq!(td_chara1.name, "typedef_characteristic1"); + let td_chara1_merge = &module.typedef_characteristic[1]; + let curve_axis_ref1 = &td_chara1_merge.axis_descr[0].curve_axis_ref; + assert_eq!( + curve_axis_ref1.as_ref().unwrap().curve_axis, + "instance1.MERGE" + ); + let td_chara2 = &module.typedef_characteristic[2]; + let curve_axis_ref2 = &td_chara2.axis_descr[0].curve_axis_ref; + assert_eq!(curve_axis_ref2.as_ref().unwrap().curve_axis, "instance2"); + } + + #[test] + fn test_merge_axis_pts() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin AXIS_PTS axispts1 "" 0x1234 input_qty deposit_record 0 conversion 3 0.0 10.0 + /end AXIS_PTS + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin AXIS_PTS axispts1 "" 0x1234 input_qty deposit_record 0 foo 3 0.0 10.0 + /end AXIS_PTS + /begin AXIS_PTS axispts2 "" 0x1235 input_qty deposit_record 0 conversion 3 0.0 10.0 + /end AXIS_PTS + + /begin CHARACTERISTIC characteristic1 "" CURVE 0x1234 record_layout_name 0 compu_method 0.0 1.0 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + AXIS_PTS_REF axispts1 + /end AXIS_DESCR + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + AXIS_PTS_REF axispts2 + /end AXIS_DESCR + /end CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VAL_BLK record_layout_name 0 compu_method_name 0 100 + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + AXIS_PTS_REF axispts1 + /end AXIS_DESCR + /begin AXIS_DESCR COM_AXIS measurement_name compu_method 1 0 100 + AXIS_PTS_REF axispts2 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no AXIS_PTS, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].axis_pts.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].axis_pts.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].axis_pts.len(), 1); + assert_eq!(a2l_file_c.project.module[0].axis_pts.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].axis_pts.len(), 3); + + let characteristic = &a2l_file_b.project.module[0].characteristic[0]; + assert_eq!(characteristic.axis_descr.len(), 2); + let axis_pts_ref1 = &characteristic.axis_descr[0].axis_pts_ref.as_ref().unwrap(); + assert_eq!(axis_pts_ref1.axis_points, "axispts1.MERGE"); + let axis_pts_ref2 = &characteristic.axis_descr[1].axis_pts_ref.as_ref().unwrap(); + assert_eq!(axis_pts_ref2.axis_points, "axispts2"); + + let td_characteristic = &a2l_file_b.project.module[0].typedef_characteristic[0]; + assert_eq!(td_characteristic.axis_descr.len(), 2); + let axis_pts_ref1 = &td_characteristic.axis_descr[0] + .axis_pts_ref + .as_ref() + .unwrap(); + assert_eq!(axis_pts_ref1.axis_points, "axispts1.MERGE"); + let axis_pts_ref2 = &td_characteristic.axis_descr[1] + .axis_pts_ref + .as_ref() + .unwrap(); + assert_eq!(axis_pts_ref2.axis_points, "axispts2"); + } + + #[test] + fn test_merge_typedef_axis() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_AXIS typedef_axis1 "" measurement_name record_layout_name 0 compu_method_name 1 0 100 + /end TYPEDEF_AXIS + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_AXIS typedef_axis1 "" xxxxx record_layout_name 0 compu_method_name 1 0 100 + /end TYPEDEF_AXIS + /begin TYPEDEF_AXIS typedef_axis2 "" measurement_name record_layout_name 0 compu_method_name 1 0 100 + /end TYPEDEF_AXIS + + /begin INSTANCE instance1 "" typedef_axis1 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" typedef_axis2 0x0 + /end INSTANCE + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no AXIS_PTS, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].typedef_axis.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].typedef_axis.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].typedef_axis.len(), 1); + assert_eq!(a2l_file_c.project.module[0].typedef_axis.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].typedef_axis.len(), 3); + } + + #[test] + fn test_merge_blob() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin BLOB blob1 "" 0x1234 0x5678 + /end BLOB + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin BLOB blob1 "" 0x567 0x5678 + /end BLOB + /begin BLOB blob2 "" 0x1235 0x5679 + /end BLOB + + /begin TRANSFORMER transformer_name "version string" "dll32" "dll64" 1 ON_CHANGE NO_INVERSE_TRANSFORMER + /begin TRANSFORMER_IN_OBJECTS blob1 blob2 + /end TRANSFORMER_IN_OBJECTS + /begin TRANSFORMER_OUT_OBJECTS blob1 blob2 + /end TRANSFORMER_OUT_OBJECTS + /end TRANSFORMER + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no BLOB, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].blob.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].blob.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].blob.len(), 1); + assert_eq!(a2l_file_c.project.module[0].blob.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].blob.len(), 3); + + let transformer = &a2l_file_b.project.module[0].transformer[0]; + let in_objects = transformer.transformer_in_objects.as_ref().unwrap(); + assert_eq!(in_objects.identifier_list.len(), 2); + assert_eq!(in_objects.identifier_list[0], "blob1.MERGE"); + assert_eq!(in_objects.identifier_list[1], "blob2"); + let out_objects = transformer.transformer_out_objects.as_ref().unwrap(); + assert_eq!(out_objects.identifier_list.len(), 2); + assert_eq!(out_objects.identifier_list[0], "blob1.MERGE"); + assert_eq!(out_objects.identifier_list[1], "blob2"); + } + + #[test] + fn test_merge_typedef_blob() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_BLOB typedef_blob1 "" 1 + /end TYPEDEF_BLOB + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_BLOB typedef_blob1 "" 100 + /end BLOB + /begin TYPEDEF_BLOB typedef_blob2 "" 1 + /end BLOB + + /begin INSTANCE instance1 "" typedef_blob1 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" typedef_blob2 0x0 + /end INSTANCE + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no BLOB, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].typedef_blob.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].typedef_blob.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].typedef_blob.len(), 1); + assert_eq!(a2l_file_c.project.module[0].typedef_blob.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].typedef_blob.len(), 3); + } + + #[test] + fn test_merge_typedef_structure() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_STRUCTURE typedef_structure1 "" 0 + /end TYPEDEF_STRUCTURE + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin TYPEDEF_STRUCTURE typedef_structure1 "foo" 100 + /end TYPEDEF_STRUCTURE + /begin TYPEDEF_STRUCTURE typedef_structure2 "" 0 + /end TYPEDEF_STRUCTURE + + /begin INSTANCE instance1 "" typedef_structure1 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" typedef_structure2 0x0 + /end INSTANCE + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no BLOB, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].typedef_structure.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].typedef_structure.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].typedef_structure.len(), 1); + assert_eq!(a2l_file_c.project.module[0].typedef_structure.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + + let module = &a2l_file_b.project.module[0]; + let td_structure1 = &module.typedef_structure[0]; + assert_eq!(td_structure1.name, "typedef_structure1"); + + let td_structure1_merge = &module.typedef_structure[1]; + assert_eq!(td_structure1_merge.name, "typedef_structure1.MERGE"); + + let td_structure2 = &module.typedef_structure[2]; + assert_eq!(td_structure2.name, "typedef_structure2"); + } + + #[test] + fn test_merge_instance() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin INSTANCE instance1 "" type_ref 0x1234 + /end INSTANCE + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin INSTANCE instance1 "" other_type_ref 0x0 + /end INSTANCE + /begin INSTANCE instance2 "" type_ref 0x1111 + /end INSTANCE + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no INSTANCE, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].instance.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].instance.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].instance.len(), 1); + assert_eq!(a2l_file_c.project.module[0].instance.len(), 2); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].instance.len(), 3); + } + + #[test] + fn test_merge_mod_par() { + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin MOD_PAR "" + /begin MEMORY_LAYOUT PRG_DATA 0x1000 1 0 0 0 0 0 /end MEMORY_LAYOUT + /begin MEMORY_LAYOUT PRG_DATA 0x1234 1 0 0 0 0 0 /end MEMORY_LAYOUT + /begin MEMORY_SEGMENT equal_seg "" DATA FLASH INTERN 0 0x1000 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT + /begin MEMORY_SEGMENT seg_b "" DATA FLASH INTERN 0 0x1234 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT + SYSTEM_CONSTANT "System_Constant_1" "1" + SYSTEM_CONSTANT "System_Constant_2" "2" + /end MOD_PAR + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin MOD_PAR "" + /begin MEMORY_LAYOUT PRG_DATA 0x1000 1 0 0 0 0 0 /end MEMORY_LAYOUT + /begin MEMORY_LAYOUT PRG_DATA 0xFFFFFF 1 0 0 0 0 0 /end MEMORY_LAYOUT + /begin MEMORY_SEGMENT equal_seg "" DATA FLASH INTERN 0 0x1000 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT + /begin MEMORY_SEGMENT seg_b "" DATA FLASH INTERN 0 0x1235 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT + /begin MEMORY_SEGMENT seg_c "" DATA FLASH INTERN 0 0x1234 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT + SYSTEM_CONSTANT "System_Constant_1" "1" + SYSTEM_CONSTANT "System_Constant_3" "333" + /end MOD_PAR + /begin AXIS_PTS axispts1 "long_identifier" 0x1234 input_qty deposit_record 0 conversion 3 0.0 10.0 + REF_MEMORY_SEGMENT seg_b + /end AXIS_PTS + /begin AXIS_PTS axispts2 "long_identifier" 0x1234 input_qty deposit_record 0 conversion 3 0.0 10.0 + /end AXIS_PTS + /begin CHARACTERISTIC characteristic1 "long_identifier" VALUE 0x1234 deposit_ident 0 conversion 0.0 10.0 + REF_MEMORY_SEGMENT seg_b + /end CHARACTERISTIC + /begin CHARACTERISTIC characteristic2 "long_identifier" VALUE 0x1234 deposit_ident 0 conversion 0.0 10.0 + /end CHARACTERISTIC + /begin MEASUREMENT measurement1 "long_identifier" FLOAT32_IEEE conversion 1 1.0 0 100 + REF_MEMORY_SEGMENT seg_b + /end MEASUREMENT + /begin MEASUREMENT measurement2 "long_identifier" FLOAT32_IEEE conversion 1 1.0 0 100 + /end MEASUREMENT + /end MODULE + /end PROJECT"#; + + let mut log_msgs = vec![]; + + // merging B into A -> A has no MOD_PAR, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].mod_par.is_none()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert!(a2l_file_a.project.module[0].mod_par.is_some()); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_b.project.module[0].mod_par.is_some()); + assert!(a2l_file_c.project.module[0].mod_par.is_some()); + a2l_file_b.merge_modules(&mut a2l_file_c); + let mod_par_b = a2l_file_b.project.module[0].mod_par.as_ref().unwrap(); + assert_eq!(mod_par_b.memory_layout.len(), 3); + assert_eq!(mod_par_b.memory_segment.len(), 4); + assert_eq!(mod_par_b.system_constant.len(), 3); + + let module = &a2l_file_b.project.module[0]; + // the references to MEMORY_SGMENT seg_b should be renamed + let ref_memory_segment = &module.axis_pts[0].ref_memory_segment.as_ref(); + assert_ne!(ref_memory_segment.unwrap().name, "seg_b"); + let ref_memory_segment = &module.characteristic[0].ref_memory_segment.as_ref(); + assert_ne!(ref_memory_segment.unwrap().name, "seg_b"); + let ref_memory_segment = &module.measurement[0].ref_memory_segment.as_ref(); + assert_ne!(ref_memory_segment.unwrap().name, "seg_b"); + } + + #[test] + fn test_merge_compu_method() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" + /end COMPU_METHOD + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" + COEFFS 1 2 3 4 5 6 + /end COMPU_METHOD + /begin COMPU_METHOD m2 "" TAB_NOINTP "%6.3" "" + /end COMPU_METHOD + + // various items with references to m1 and m2 + + /begin AXIS_PTS axispts1 "" 0x1234 NO_INPUT_QUANTITY record_layout_name 0 m1 3 0.0 10.0 + /end AXIS_PTS + /begin AXIS_PTS axispts2 "" 0x1234 NO_INPUT_QUANTITY record_layout_name 0 m2 3 0.0 10.0 + /end AXIS_PTS + /begin CHARACTERISTIC characteristic1 "" CURVE 0x1234 record_layout_name 0 m1 0.0 1.0 + /begin AXIS_DESCR COM_AXIS measurement_name m1 1 0 100 + /end AXIS_DESCR + /end CHARACTERISTIC + /begin CHARACTERISTIC characteristic2 "" CURVE 0x1234 record_layout_name 0 m2 0.0 1.0 + /begin AXIS_DESCR COM_AXIS measurement_name m2 1 0 100 + /end AXIS_DESCR + /end CHARACTERISTIC + /begin MEASUREMENT measurement1 "" FLOAT32_IEEE m1 1 1.0 0 100 + /end MEASUREMENT + /begin MEASUREMENT measurement2 "" FLOAT32_IEEE m2 1 1.0 0 100 + /end MEASUREMENT + /begin TYPEDEF_AXIS typedef_axis1 "" measurement_name record_layout_name 0 m1 1 0 100 + /end TYPEDEF_AXIS + /begin TYPEDEF_AXIS typedef_axis2 "" measurement_name record_layout_name 0 m2 1 0 100 + /end TYPEDEF_AXIS + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VALUE record_layout_name 0 m1 0 100 + /begin AXIS_DESCR COM_AXIS measurement_name m1 1 0 100 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic2 "" VALUE record_layout_name 0 m2 0 100 + /begin AXIS_DESCR COM_AXIS measurement_name m2 1 0 100 + /end AXIS_DESCR + /end TYPEDEF_CHARACTERISTIC + /begin TYPEDEF_MEASUREMENT typedef_measurement1 "" UBYTE m1 1 1 0 100 + /end TYPEDEF_MEASUREMENT + /begin TYPEDEF_MEASUREMENT typedef_measurement2 "" UBYTE m2 1 1 0 100 + /end TYPEDEF_MEASUREMENT + /end MODULE + /end PROJECT"#; + + // merging B into A -> A has no COMPU_METHODs, so they are all taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].compu_method.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].compu_method.len(), 1); + + // merging C into B: m1 is present in both, but with different content -> m1 is renamed + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].compu_method.len(), 1); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].compu_method.len(), 3); + + let module = &a2l_file_b.project.module[0]; + + // check that the references in file C to m1 have been renamed + let axispts1 = &module.axis_pts[0]; + assert_eq!(axispts1.conversion, "m1.MERGE"); + + let characteristic1 = &module.characteristic[0]; + assert_eq!(characteristic1.conversion, "m1.MERGE"); + assert_eq!(characteristic1.axis_descr[0].conversion, "m1.MERGE"); + + let measurement1 = &module.measurement[0]; + assert_eq!(measurement1.conversion, "m1.MERGE"); + + let typedef_axis1 = &module.typedef_axis[0]; + assert_eq!(typedef_axis1.conversion, "m1.MERGE"); + + let typedef_characteristic1 = &module.typedef_characteristic[0]; + assert_eq!(typedef_characteristic1.conversion, "m1.MERGE"); + assert_eq!(typedef_characteristic1.axis_descr[0].conversion, "m1.MERGE"); + + let typedef_measurement1 = &module.typedef_measurement[0]; + assert_eq!(typedef_measurement1.conversion, "m1.MERGE"); + + // check that the references in file C to m2 have not been renamed + let axispts2 = &module.axis_pts[1]; + assert_eq!(axispts2.conversion, "m2"); + + let characteristic2 = &module.characteristic[1]; + assert_eq!(characteristic2.conversion, "m2"); + assert_eq!(characteristic2.axis_descr[0].conversion, "m2"); + + let measurement2 = &module.measurement[1]; + assert_eq!(measurement2.conversion, "m2"); + + let typedef_axis2 = &module.typedef_axis[1]; + assert_eq!(typedef_axis2.conversion, "m2"); + + let typedef_characteristic2 = &module.typedef_characteristic[1]; + assert_eq!(typedef_characteristic2.conversion, "m2"); + assert_eq!(typedef_characteristic2.axis_descr[0].conversion, "m2"); + + let typedef_measurement2 = &module.typedef_measurement[1]; + assert_eq!(typedef_measurement2.conversion, "m2"); + } + + #[test] + fn test_merge_compu_tab() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 + /begin PROJECT p "" + /begin MODULE m "" + /begin COMPU_TAB ct1 "" TAB_NOINTP 1 + 1 1 + /end COMPU_TAB + /begin COMPU_VTAB cvt1 "" TAB_VERB 1 + 1 "v" + /end COMPU_VTAB + /begin COMPU_VTAB_RANGE cvtr1 "" 1 + 1 2 "v" + /end COMPU_VTAB_RANGE + /begin COMPU_TAB ct2 "" TAB_NOINTP 1 + 1 1 + /end COMPU_TAB + /begin COMPU_VTAB cvt2 "" TAB_VERB 1 + 1 "v" + /end COMPU_VTAB + /begin COMPU_VTAB_RANGE cvtr2 "" 1 + 1 2 "v" + /end COMPU_VTAB_RANGE + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"ASAP2_VERSION 1 71 + /begin PROJECT p "" + /begin MODULE m "" + /begin COMPU_TAB ct1 "" TAB_NOINTP 1 + 1 1 + /end COMPU_TAB + /begin COMPU_VTAB cvt1 "" TAB_VERB 1 + 1 "v" + /end COMPU_VTAB + /begin COMPU_VTAB_RANGE cvtr1 "" 1 + 1 2 "v" + /end COMPU_VTAB_RANGE + /begin COMPU_TAB ct2 "" TAB_NOINTP 1 + 2 2 + /end COMPU_TAB + /begin COMPU_VTAB cvt2 "" TAB_VERB 1 + 2 "v" + /end COMPU_VTAB + /begin COMPU_VTAB_RANGE cvtr2 "" 1 + 2 3 "v" + /end COMPU_VTAB_RANGE + /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" + COMPU_TAB_REF ct2 + /end COMPU_METHOD + /begin COMPU_METHOD m2 "" TAB_VERB "%6.3" "" + COMPU_TAB_REF cvt2 + /end COMPU_METHOD + /begin COMPU_METHOD m3 "" TAB_VERB "%6.3" "" + COMPU_TAB_REF cvtr2 + /end COMPU_METHOD + /begin COMPU_METHOD m4 "" TAB_NOINTP "%6.3" "" + STATUS_STRING_REF ct1 + /end COMPU_METHOD + /begin COMPU_METHOD m4 "" TAB_NOINTP "%6.3" "" + STATUS_STRING_REF cvtr2 + /end COMPU_METHOD + /end MODULE + /end PROJECT"#; + + // merging B into A -> A has no COMPU_TABs, so they are all taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + assert!(a2l_file_a.project.module[0].compu_tab.is_empty()); + assert!(a2l_file_a.project.module[0].compu_vtab.is_empty()); + assert!(a2l_file_a.project.module[0].compu_vtab_range.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].compu_tab.len(), 2); + assert_eq!(a2l_file_a.project.module[0].compu_vtab.len(), 2); + assert_eq!(a2l_file_a.project.module[0].compu_vtab_range.len(), 2); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].compu_tab.len(), 3); + assert_eq!(a2l_file_b.project.module[0].compu_vtab.len(), 3); + assert_eq!(a2l_file_b.project.module[0].compu_vtab_range.len(), 3); + } + + #[test] + fn test_merge_unit() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin UNIT unit1 "" "x" DERIVED + /end UNIT + /begin UNIT unit2 "" "x" DERIVED + /end UNIT + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin UNIT unit1 "" "x" DERIVED + /end UNIT + /begin UNIT unit2 "" "xyz" DERIVED + /end UNIT + /begin UNIT unit3 "" "xyz" DERIVED + /end UNIT + /begin COMPU_METHOD m1 "" TAB_NOINTP "%6.3" "" + REF_UNIT unit2 + /end COMPU_METHOD + /begin COMPU_METHOD m2 "" TAB_NOINTP "%6.3" "" + /end COMPU_METHOD + /end MODULE + /end PROJECT"#; + + // merging B into A -> A has no UNIT, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].unit.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].unit.len(), 2); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].unit.len(), 2); + assert_eq!(a2l_file_c.project.module[0].unit.len(), 3); + a2l_file_b.merge_modules(&mut a2l_file_c); + // uni1 is identical in both files, so it is not duplicated + // unit2 is present in both, but with different content -> unit2 is renamed + // unit3 is only present in C + assert_eq!(a2l_file_b.project.module[0].unit.len(), 4); + + let module = &a2l_file_b.project.module[0]; + let unit1 = &module.unit[0]; + assert_eq!(unit1.name, "unit1"); + let unit2 = &module.unit[1]; + assert_eq!(unit2.name, "unit2"); + let unit2_merge = &module.unit[2]; + assert_eq!(unit2_merge.name, "unit2.MERGE"); + let unit3 = &module.unit[3]; + assert_eq!(unit3.name, "unit3"); + + // check that the references in file C to unit2 have been renamed + let compu_method1 = &module.compu_method[0]; + let ref_unit = compu_method1.ref_unit.as_ref().unwrap(); + assert_eq!(ref_unit.unit, "unit2.MERGE"); + } + + #[test] + fn test_merge_record_layout() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"/begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin RECORD_LAYOUT record_layout1 + /end RECORD_LAYOUT + /end MODULE + /end PROJECT"#; + static FILE_C: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin RECORD_LAYOUT record_layout1 + AXIS_PTS_X 1 SWORD INDEX_INCR DIRECT + /end RECORD_LAYOUT + /begin RECORD_LAYOUT record_layout2 + /end RECORD_LAYOUT + /begin AXIS_PTS axispts1 "" 0x1234 NO_INPUT_QUANTITY record_layout1 0 compu_method 3 0.0 10.0 + /end AXIS_PTS + /begin AXIS_PTS axispts2 "" 0x1234 NO_INPUT_QUANTITY record_layout2 0 compu_method 3 0.0 10.0 + /end AXIS_PTS + /begin CHARACTERISTIC characteristic1 "" CURVE 0x1234 record_layout1 0 compu_method 0.0 1.0 + /end CHARACTERISTIC + /begin CHARACTERISTIC characteristic2 "" CURVE 0x1234 record_layout2 0 compu_method 0.0 1.0 + /end CHARACTERISTIC + /begin TYPEDEF_AXIS typedef_axis1 "" measurement_name record_layout1 0 compu_method 1 0 100 + /end TYPEDEF_AXIS + /begin TYPEDEF_AXIS typedef_axis2 "" measurement_name record_layout2 0 compu_method 1 0 100 + /end TYPEDEF_AXIS + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic1 "" VALUE record_layout1 0 compu_method 0 100 + /end TYPEDEF_CHARACTERISTIC + /begin TYPEDEF_CHARACTERISTIC typedef_characteristic2 "" VALUE record_layout2 0 compu_method 0 100 + /end TYPEDEF_CHARACTERISTIC + /begin MOD_COMMON "" + S_REC_LAYOUT record_layout1 + /end MOD_COMMON + /end MODULE + /end PROJECT"#; + + // merging B into A -> A has no RECORD_LAYOUT, so it is taken from B + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + assert!(a2l_file_a.project.module[0].record_layout.is_empty()); + a2l_file_a.merge_modules(&mut a2l_file_b); + assert_eq!(a2l_file_a.project.module[0].record_layout.len(), 1); + + // merging C into B + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, false).unwrap(); + assert_eq!(a2l_file_b.project.module[0].record_layout.len(), 1); + a2l_file_b.merge_modules(&mut a2l_file_c); + assert_eq!(a2l_file_b.project.module[0].record_layout.len(), 3); + + let module = &a2l_file_b.project.module[0]; + + // check that the references in file C to record_layout1 have been renamed + let axispts1 = &module.axis_pts[0]; + assert_eq!(axispts1.deposit_record, "record_layout1.MERGE"); + + let characteristic1 = &module.characteristic[0]; + assert_eq!(characteristic1.deposit, "record_layout1.MERGE"); + + let typedef_axis1 = &module.typedef_axis[0]; + assert_eq!(typedef_axis1.record_layout, "record_layout1.MERGE"); + + let typedef_characteristic1 = &module.typedef_characteristic[0]; + assert_eq!( + typedef_characteristic1.record_layout, + "record_layout1.MERGE" + ); + + // check that the references in file C to record_layout2 have not been renamed + let axispts2 = &module.axis_pts[1]; + assert_eq!(axispts2.deposit_record, "record_layout2"); + + let characteristic2 = &module.characteristic[1]; + assert_eq!(characteristic2.deposit, "record_layout2"); + + let typedef_axis2 = &module.typedef_axis[1]; + assert_eq!(typedef_axis2.record_layout, "record_layout2"); + + let typedef_characteristic2 = &module.typedef_characteristic[1]; + assert_eq!(typedef_characteristic2.record_layout, "record_layout2"); + + // for coverage - merge with rename, where MOD_COMMON does not exist, or does not have S_REC_LAYOUT + static FILE_D: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin RECORD_LAYOUT record_layout1 + AXIS_PTS_X 1 SWORD INDEX_INCR DIRECT + /end RECORD_LAYOUT + /end MODULE + /end PROJECT"#; + static FILE_E: &str = r#"/begin PROJECT p "" + /begin MODULE m "" + /begin RECORD_LAYOUT record_layout1 + AXIS_PTS_X 1 SWORD INDEX_INCR DIRECT + /end RECORD_LAYOUT + /begin MOD_COMMON "" + /end MOD_COMMON + /end MODULE + /end PROJECT"#; + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_d = load_from_string(FILE_D, None, &mut log_msgs, false).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_d); + assert_eq!(a2l_file_b.project.module[0].record_layout.len(), 2); + + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, false).unwrap(); + let mut a2l_file_e = load_from_string(FILE_E, None, &mut log_msgs, false).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_e); + assert_eq!(a2l_file_b.project.module[0].record_layout.len(), 2); + } + + #[test] + fn merge_group() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin MEASUREMENT m1 "" FLOAT32_IEEE NO_COMPU_METHOD 1 1.0 0 100 + /end MEASUREMENT + /begin GROUP g1 "" + /begin REF_MEASUREMENT m1 /end REF_MEASUREMENT + /end GROUP + /begin GROUP g2 "" + /end GROUP + /begin GROUP g3 "" + /end GROUP + /begin GROUP g4 "" + /end GROUP + /begin GROUP g5 "" + /begin FUNCTION_LIST func1 + /end FUNCTION_LIST + /begin REF_CHARACTERISTIC ch1 + /end REF_CHARACTERISTIC + /begin REF_MEASUREMENT meas1 + /end REF_MEASUREMENT + /begin SUB_GROUP group1 + /end SUB_GROUP + /end GROUP /end MODULE /end PROJECT"#; static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /begin MEASUREMENT m1 "different content" FLOAT32_IEEE NO_COMPU_METHOD 1 2.0 0 200 @@ -1372,11 +2305,30 @@ mod test { /begin GROUP g1 "" /begin REF_MEASUREMENT m1 /end REF_MEASUREMENT /end GROUP - /begin FUNCTION f "" - /begin IN_MEASUREMENT m1 /end IN_MEASUREMENT - /begin LOC_MEASUREMENT m1 /end LOC_MEASUREMENT - /begin OUT_MEASUREMENT m1 /end OUT_MEASUREMENT - /end FUNCTION + /begin GROUP g2 "" + /end GROUP + /begin GROUP g3 "foo" + /end GROUP + /begin GROUP g4 "" + /begin FUNCTION_LIST + /end FUNCTION_LIST + /begin REF_CHARACTERISTIC + /end REF_CHARACTERISTIC + /begin REF_MEASUREMENT + /end REF_MEASUREMENT + /begin SUB_GROUP + /end SUB_GROUP + /end GROUP + /begin GROUP g5 "" + /begin FUNCTION_LIST func1 func2 + /end FUNCTION_LIST + /begin REF_CHARACTERISTIC ch1 ch2 + /end REF_CHARACTERISTIC + /begin REF_MEASUREMENT meas1 meas2 + /end REF_MEASUREMENT + /begin SUB_GROUP group1 group2 + /end SUB_GROUP + /end GROUP /end MODULE /end PROJECT"#; let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); @@ -1391,25 +2343,37 @@ mod test { // to m1.MERGE. The group is merged, because GROUPS are merged based on the name only let group = &a2l_file_a.project.module[0].group[0]; assert_eq!(group.name, "g1"); - assert_eq!( - group.ref_measurement.as_ref().unwrap().identifier_list[0], - "m1" - ); - assert_eq!( - group.ref_measurement.as_ref().unwrap().identifier_list[1], - "m1.MERGE" - ); - assert_eq!( - group - .ref_measurement - .as_ref() - .unwrap() - .identifier_list - .len(), - 2 - ); - - println!("{}", a2l_file_a.write_to_string()); + let meas = group.ref_measurement.as_ref().unwrap(); + assert_eq!(meas.identifier_list[0], "m1"); + assert_eq!(meas.identifier_list[1], "m1.MERGE"); + assert_eq!(meas.identifier_list.len(), 2); + + // group g2 is identical in both files. There is nothing to merge or rename + let group = &a2l_file_a.project.module[0].group[1]; + assert_eq!(group.name, "g2"); + + // group g3 is not identical, due to the differnent long identifier, but it is not renamed + let group = &a2l_file_a.project.module[0].group[2]; + assert_eq!(group.name, "g3"); + + // group g4 is not identical, due to the differnent long identifier; the content is merged + let group = &a2l_file_a.project.module[0].group[3]; + assert_eq!(group.name, "g4"); + assert!(group.function_list.is_some()); + assert!(group.ref_characteristic.is_some()); + assert!(group.ref_measurement.is_some()); + assert!(group.sub_group.is_some()); + + // group g5 is not identical, due to the differnent long identifier; the content is merged + let group = &a2l_file_a.project.module[0].group[4]; + assert_eq!(group.name, "g5"); + let func_list = group.function_list.as_ref().unwrap(); + assert_eq!(func_list.name_list.len(), 2); + let ref_chr_list = group.ref_characteristic.as_ref().unwrap(); + assert_eq!(ref_chr_list.identifier_list.len(), 2); + let ref_meas_list = group.ref_measurement.as_ref().unwrap(); + assert_eq!(ref_meas_list.identifier_list.len(), 2); + assert_eq!(group.sub_group.as_ref().unwrap().identifier_list.len(), 2); } #[test] @@ -1425,6 +2389,12 @@ mod test { /begin LOC_MEASUREMENT m1 /end LOC_MEASUREMENT /begin OUT_MEASUREMENT m1 /end OUT_MEASUREMENT /begin REF_CHARACTERISTIC c /end REF_CHARACTERISTIC + /begin SUB_FUNCTION f2 + /end SUB_FUNCTION + /end FUNCTION + /begin FUNCTION f2 "" + /end FUNCTION + /begin FUNCTION f3 "" /end FUNCTION /end MODULE /end PROJECT"#; static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" @@ -1437,6 +2407,18 @@ mod test { /begin LOC_MEASUREMENT m1 /end LOC_MEASUREMENT /begin OUT_MEASUREMENT m1 /end OUT_MEASUREMENT /begin REF_CHARACTERISTIC c /end REF_CHARACTERISTIC + /begin SUB_FUNCTION f2 other + /end SUB_FUNCTION + /end FUNCTION + /begin FUNCTION f2 "" + /begin IN_MEASUREMENT m1 /end IN_MEASUREMENT + /begin LOC_MEASUREMENT m1 /end LOC_MEASUREMENT + /begin OUT_MEASUREMENT m1 /end OUT_MEASUREMENT + /begin REF_CHARACTERISTIC c /end REF_CHARACTERISTIC + /begin SUB_FUNCTION sub_func2 + /end SUB_FUNCTION + /end FUNCTION + /begin FUNCTION f3 "foo" /end FUNCTION /end MODULE /end PROJECT"#; @@ -1451,77 +2433,207 @@ mod test { // the CHARACTERISTIC c is identical in both files, so it is not renamed assert_eq!(a2l_file_a.project.module[0].characteristic.len(), 1); - // the function f is initially identical in both files, but the MEASUREMENT references are renamed - // to m1.MERGE. The function is merged, because FUNCTIONS are merged based on the name only + assert_eq!(a2l_file_a.project.module[0].function.len(), 3); let function = &a2l_file_a.project.module[0].function[0]; assert_eq!(function.name, "f"); - assert_eq!( - function.in_measurement.as_ref().unwrap().identifier_list[0], - "m1" - ); - assert_eq!( - function.in_measurement.as_ref().unwrap().identifier_list[1], - "m1.MERGE" - ); - assert_eq!( - function - .in_measurement - .as_ref() - .unwrap() - .identifier_list - .len(), - 2 - ); - assert_eq!( - function.loc_measurement.as_ref().unwrap().identifier_list[0], - "m1" - ); - assert_eq!( - function.loc_measurement.as_ref().unwrap().identifier_list[1], - "m1.MERGE" - ); - assert_eq!( - function - .loc_measurement - .as_ref() - .unwrap() - .identifier_list - .len(), - 2 - ); + + // the function f is initially identical in both files, but the MEASUREMENT references are renamed + // to m1.MERGE. The function is merged, because FUNCTIONS are merged based on the name only + let in_meas = function.in_measurement.as_ref().unwrap(); + assert_eq!(in_meas.identifier_list[0], "m1"); + assert_eq!(in_meas.identifier_list[1], "m1.MERGE"); + assert_eq!(in_meas.identifier_list.len(), 2); + let loc_meas = function.loc_measurement.as_ref().unwrap(); + assert_eq!(loc_meas.identifier_list[0], "m1"); + assert_eq!(loc_meas.identifier_list[1], "m1.MERGE"); + assert_eq!(loc_meas.identifier_list.len(), 2); assert_eq!( function.out_measurement.as_ref().unwrap().identifier_list[0], "m1" ); - assert_eq!( - function.out_measurement.as_ref().unwrap().identifier_list[1], - "m1.MERGE" - ); - assert_eq!( - function - .out_measurement - .as_ref() - .unwrap() - .identifier_list - .len(), - 2 - ); - assert_eq!( - function - .ref_characteristic - .as_ref() - .unwrap() - .identifier_list[0], - "c" - ); - assert_eq!( - function - .ref_characteristic - .as_ref() - .unwrap() - .identifier_list - .len(), - 1 - ); + let out_meas = function.out_measurement.as_ref().unwrap(); + assert_eq!(out_meas.identifier_list[1], "m1.MERGE"); + assert_eq!(out_meas.identifier_list.len(), 2); + let ref_meas = function.ref_characteristic.as_ref().unwrap(); + assert_eq!(ref_meas.identifier_list[0], "c"); + assert_eq!(ref_meas.identifier_list.len(), 1); + + let sub_func = &function.sub_function.as_ref().unwrap(); + assert_eq!(sub_func.identifier_list.len(), 2); + assert_eq!(sub_func.identifier_list[0], "f2"); + assert_eq!(sub_func.identifier_list[1], "other"); + + // function f2 was not renamed + let function = &a2l_file_a.project.module[0].function[1]; + assert_eq!(function.name, "f2"); + assert!(function.sub_function.is_some()); + + // function f3 was not renamed + let function = &a2l_file_a.project.module[0].function[2]; + assert_eq!(function.name, "f3"); + assert!(function.sub_function.is_none()); + } + + #[test] + fn test_merge_module_ifdata() { + let mut log_msgs = vec![]; + static FILE_A: &str = + r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin IF_DATA foo + /end IF_DATA + /end MODULE /end PROJECT"#; + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin IF_DATA bar + /end IF_DATA + /begin IF_DATA baz + /end IF_DATA + /end MODULE /end PROJECT"#; + + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + a2l_file_a.merge_modules(&mut a2l_file_b); + // since file A has no IF_DATA, it is taken from B + assert_eq!(a2l_file_a.project.module[0].if_data.len(), 1); + + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_c); + // file B already has IF_DATA, so the ones from C are ignored + assert_eq!(a2l_file_b.project.module[0].if_data.len(), 1); + } + + #[test] + fn test_merge_frame() { + let mut log_msgs = vec![]; + static FILE_A: &str = + r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin FRAME frame1 "" 1 2 + /end FRAME + /end MODULE /end PROJECT"#; + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin FRAME frame1 "" 2 2 + /end FRAME + /begin FRAME frame2 "" 2 3 + /end FRAME + /end MODULE /end PROJECT"#; + + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + a2l_file_a.merge_modules(&mut a2l_file_b); + // since file A has no FRAME, it is taken from B + assert_eq!(a2l_file_a.project.module[0].frame.len(), 1); + + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_c); + // frames from B and C are merged. + // frame1 is present in both, but with different content -> frame1 is renamed + // frame2 is only present in C, and is added to B + assert_eq!(a2l_file_b.project.module[0].frame.len(), 3); + } + + #[test] + fn test_merge_transformer() { + let mut log_msgs = vec![]; + static FILE_A: &str = + r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin TRANSFORMER transformer1 "version string" "dll32" "dll64" 1 ON_CHANGE NO_INVERSE_TRANSFORMER + /end TRANSFORMER + /end MODULE /end PROJECT"#; + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin TRANSFORMER transformer1 "other version" "dll32" "dll64" 1 ON_CHANGE transformer2 + /end TRANSFORMER + /begin TRANSFORMER transformer2 "version string" "dll32" "dll64" 1 ON_CHANGE transformer1 + /end TRANSFORMER + /end MODULE /end PROJECT"#; + + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + a2l_file_a.merge_modules(&mut a2l_file_b); + // since file A has no TRANSFORMER, it is taken from B + assert_eq!(a2l_file_a.project.module[0].transformer.len(), 1); + + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_c); + // transformers from B and C are merged. + // transformer1 is present in both, but with different content -> transformer1 is renamed + // transformer2 is only present in C, and is added to B + assert_eq!(a2l_file_b.project.module[0].transformer.len(), 3); + + let transformer1 = &a2l_file_b.project.module[0].transformer[0]; + assert_eq!(transformer1.name, "transformer1"); + + let transformer1_merged = &a2l_file_b.project.module[0].transformer[1]; + assert_eq!(transformer1_merged.name, "transformer1.MERGE"); + + let transformer2 = &a2l_file_b.project.module[0].transformer[2]; + assert_eq!(transformer2.name, "transformer2"); + } + + #[test] + fn test_merge_user_rights() { + let mut log_msgs = vec![]; + static FILE_A: &str = + r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin USER_RIGHTS user1 + /end USER_RIGHTS + /end MODULE /end PROJECT"#; + static FILE_C: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin USER_RIGHTS user1 + READ_ONLY + /end USER_RIGHTS + /begin USER_RIGHTS user2 + /end USER_RIGHTS + /end MODULE /end PROJECT"#; + + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + a2l_file_a.merge_modules(&mut a2l_file_b); + // since file A has no USER_RIGHTS, it is taken from B + assert_eq!(a2l_file_a.project.module[0].user_rights.len(), 1); + + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_c = load_from_string(FILE_C, None, &mut log_msgs, true).unwrap(); + a2l_file_b.merge_modules(&mut a2l_file_c); + // user_rights from B and C are merged. + // user1 is already present in B, so the new copy from C is ignored + // user2 is only present in C, and is added to B + assert_eq!(a2l_file_b.project.module[0].user_rights.len(), 2); + let user_rights1 = &a2l_file_b.project.module[0].user_rights[0]; + assert_eq!(user_rights1.user_level_id, "user1"); + assert!(user_rights1.read_only.is_none()); + + let user_rights2 = &a2l_file_b.project.module[0].user_rights[1]; + assert_eq!(user_rights2.user_level_id, "user2"); + } + + #[test] + fn test_make_unique_name() { + let mut log_msgs = vec![]; + static FILE_A: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin MEASUREMENT m1 "" FLOAT32_IEEE NO_COMPU_METHOD 1 1.0 0 100 + /end MEASUREMENT + /begin MEASUREMENT m1.MERGE "" FLOAT32_IEEE NO_COMPU_METHOD 1 1.0 0 100 + /end MEASUREMENT + /end MODULE /end PROJECT"#; + static FILE_B: &str = r#"ASAP2_VERSION 1 71 /begin PROJECT p "" /begin MODULE m "" + /begin MEASUREMENT m1 "" FLOAT32_IEEE NO_COMPU_METHOD 1 2.0 0 200 + /end MEASUREMENT + /end MODULE /end PROJECT"#; + + let mut a2l_file_a = load_from_string(FILE_A, None, &mut log_msgs, true).unwrap(); + let mut a2l_file_b = load_from_string(FILE_B, None, &mut log_msgs, true).unwrap(); + a2l_file_a.merge_modules(&mut a2l_file_b); + + // the MEASUREMENT m1 in B is renamed, because it is not identical to the one in A + // the name m1.MERGE is already taken, so the name is changed to m1.MERGE2 + assert_eq!(a2l_file_a.project.module[0].measurement.len(), 3); + let measurement = &a2l_file_a.project.module[0].measurement[2]; + assert_eq!(measurement.name, "m1.MERGE2"); } }