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");
     }
 }