From 1601a799ba9d6c8c1d3bbfbe8d381ba104eae8be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20M=C3=B6slinger?= <dominik.moeslinger@univie.ac.at>
Date: Thu, 22 Jul 2021 15:51:24 +0200
Subject: [PATCH] TST: SQL Schema change !!! Run
 CCS/Tst/codblocksreusefeature/db_schema.py after merge! (Optional: run
 db_drop_schema.py before merge, run: add_dummy_data.py after merge) Pre/Post
 Conditions/Descriptions stored in new SQL Database Addition of Pre/Post
 Description, General Comment, Step Comment, VRC Step Description Version name
 added to Json File Name (Only to File name) Progress View: Make .txt Output
 File

---
 Tst/progress_view/progress_view.py            | 157 +++++++++++++++---
 .../testlib/analyse_command_log.py            |  18 +-
 .../testlib/analyse_test_run.py               | 130 +++++++--------
 Tst/testing_library/testlib/report.py         |  12 +-
 Tst/tst/generator.py                          |  60 +++++--
 Tst/tst/generator_templates/co_footer.py      |  11 +-
 .../generator_templates/co_post_condition.py  |   4 +-
 .../generator_templates/co_pre_condition.py   |   4 +-
 Tst/tst/generator_templates/run_footer.py     |   4 +-
 Tst/tst/generator_templates/run_header.py     |   2 +-
 Tst/tst/tst.py                                |  13 +-
 Tst/tst/view.py                               |   9 +-
 12 files changed, 295 insertions(+), 129 deletions(-)

diff --git a/Tst/progress_view/progress_view.py b/Tst/progress_view/progress_view.py
index dde08e1..2067eaa 100644
--- a/Tst/progress_view/progress_view.py
+++ b/Tst/progress_view/progress_view.py
@@ -187,6 +187,17 @@ class TestProgressView(Gtk.ApplicationWindow):
         self.title_box.pack_start(self.test_title, False, True, 0)
         self.box.pack_start(self.title_box, False, True, 20)
 
+        # --------------- tree view ---------------
+        self.sorted_model = Gtk.TreeModelSort(model=self.progress_tree_store)
+        self.sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
+        self.view = Gtk.TreeView(model=self.sorted_model)
+        self.view.set_grid_lines(True)
+        self.view.set_has_tooltip(True)
+        self.view.set_tooltip_column(10)
+
+        self.scroll_win = Gtk.ScrolledWindow()
+        self.scroll_win.add(self.view)
+
         # buttons for the tree view (expand all, collapse all)
         self.make_button_box()
         self.box.pack_start(self.box_buttons, False, True, 0)
@@ -196,21 +207,10 @@ class TestProgressView(Gtk.ApplicationWindow):
         self.btn_apply_css.connect('clicked', self.on_apply_css)
         # self.box_buttons.pack_start(self.btn_apply_css, False, False, 0)
 
-        # --------------- tree view ---------------
-        self.sorted_model = Gtk.TreeModelSort(model=self.progress_tree_store)
-        self.sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
-        self.view = Gtk.TreeView(model=self.sorted_model)
-        self.view.set_grid_lines(True)
-        self.view.set_has_tooltip(True)
-        self.view.set_tooltip_column(10)
         self.make_treeview()
 
         self.view.expand_all()
 
-        self.scroll_win = Gtk.ScrolledWindow()
-        #self.scroll_win.set_min_content_height(int(confignator.get_option(section='progress-viewer-window-size', option='minimum-height')))
-        #self.scroll_win.set_min_content_width(int(confignator.get_option(section='progress-viewer-window-size', option='minimum-width-step-mode')))
-        self.scroll_win.add(self.view)
         self.box.pack_start(self.scroll_win, True, True, 0)
 
         self.connect('destroy', self.on_destroy)
@@ -251,15 +251,15 @@ class TestProgressView(Gtk.ApplicationWindow):
         :param Gio.SimpleAction simple_action: The object which received the signal
         :param parameter: the parameter to the activation, or None if it has no parameter
         """
-        dialog = Gtk.FileChooserDialog('Please choose a Test Specification',
-                                       self,
-                                       Gtk.FileChooserAction.OPEN,
-                                       (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+        dialog = Gtk.FileChooserDialog(title='Please choose a Test Specification',
+                                       parent=self,
+                                       action=Gtk.FileChooserAction.OPEN)
+        dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
+
         self.add_filters(dialog)
         response = dialog.run()
         if response == Gtk.ResponseType.OK:
             file_selected = dialog.get_filename()
-            # ToDo: get the path for all 3 (json, cmd, vrc) and load them
             self.open_test_files(None, self.get_log_file_paths_from_json_file_name(file_selected))
         elif response == Gtk.ResponseType.CANCEL:
             pass
@@ -291,7 +291,7 @@ class TestProgressView(Gtk.ApplicationWindow):
 
         self.sort_button = Gtk.Switch()
         self.sort_button.connect("notify::active", self.on_remake_treeview)
-        self.sort_button.set_active(False)
+        self.sort_button.set_active(True)
         self.box_buttons.pack_end(self.sort_button, False, True, 0)
 
         self.sort_label2 = Gtk.Label()
@@ -590,21 +590,81 @@ class TestProgressView(Gtk.ApplicationWindow):
         return
 
     def save_as_file_dialog(self):
+        # If one log file is loaded use it as Log file path, otherwise ask for it in separate dialog
+        test_name = None
+        if self.path_cmd:
+            file_name = self.path_cmd.split('/')[-1]
+            log_file_path = self.path_cmd[:-len(file_name)]
+            test_name = file_name.split('_')[0]
+        elif self.path_vrc:
+            file_name = self.path_vrc.split('/')[-1]
+            log_file_path = self.path_vrc[:-len(file_name)]
+            test_name = file_name.split('_')[0]
+        else:
+            log_file_path=None
+
+        if not test_name and self.path_json:
+            test_name = self.path_json.split('/')[-1].split('.')[0]
+
         dialog = Save_to_File_Dialog(parent=self)
         response = dialog.run()
         if response == Gtk.ResponseType.OK:
+            if not test_name:
+                test_name = dialog.test_name.get_text()
+                if not test_name:
+                    dialog.destroy()
+                    self.logger.info('Can not create Output file without test name')
+                    return
+
             folder = dialog.get_current_folder()
             run_count = dialog.run_id_selection.get_active()
-            run = None
+            test_report = dialog.test_report_int.get_text()
+            sent_run_id = None
             if run_count:
                 for run_id in self.run_count.keys():
                     if self.run_count[run_id] == run_count:
-                        run = run_id
-            analyse_test_run.save_result_to_file('test', log_file_path=folder, run_id=run)
-            #confignator.save_option('tst-logging', 'output-file-path', folder)
+                        sent_run_id = run_id
+            if not log_file_path:
+                if not dialog.log_file_path_check.get_active():
+                    log_file_path = 'FIND'
+            if not self.path_json:
+                if dialog.log_file_path_check.get_active():
+                    json_file_path = None
+                else:
+                    json_file_path = 'FIND'
+            else:
+                json_file_path = self.path_json
+
         elif response == Gtk.ResponseType.CANCEL:
-            pass
+            dialog.destroy()
+            return
+
+        dialog.destroy()
+        if log_file_path == 'FIND':
+            dialog = File_Path_Dialog(parent=self, file='Command or Verification Log File', is_json=False)
+            response = dialog.run()
+            if response == Gtk.ResponseType.OK:
+                log_file_path = dialog.get_current_folder()
+            elif response == Gtk.ResponseType.CANCEL:
+                dialog.destroy()
+                return
+        dialog.destroy()
+
+        if json_file_path == 'FIND':
+            dialog = File_Path_Dialog(parent=self, file='the Json File', is_json=True)
+            response = dialog.run()
+            if response == Gtk.ResponseType.OK:
+                json_file_path = dialog.get_filename()
+            elif response == Gtk.ResponseType.CANCEL:
+                dialog.destroy()
+                return
+
         dialog.destroy()
+
+        analyse_test_run.save_result_to_file(test_name=test_name, log_file_path=log_file_path, output_file_path=folder,
+                                             json_file_path=json_file_path, run_id=sent_run_id, test_report=test_report,
+                                             logger=self.logger)
+
         return
 
     def on_destroy(self, *args):
@@ -1245,6 +1305,25 @@ class Save_to_File_Dialog(Gtk.FileChooserDialog):
         self.savedetailes = Gtk.HBox()  # Store everything in this box
         self.savedetailes.set_border_width(15)
 
+        # Only shown if a name was not given (Found from Log Files or Json File in main window
+        name_label = Gtk.Label(label='Test Name: ')
+        self.test_name = Gtk.Entry()
+        self.test_name.set_tooltip_text('The name of the Test')
+
+        # Only shown if Log File path was not given (Found from loaded log File in main window)
+        log_file_path_label = Gtk.Label(label='Use Basic Log File Path: ')
+        log_file_path_label.set_tooltip_text('Basic File Path: {}'.format(confignator.get_option('tst-logging', 'test_run')))
+        self.log_file_path_check = Gtk.CheckButton()
+        self.log_file_path_check.set_active(True)
+
+        # Only shown if Json File path was not given (Found from loaded json file in main window)
+        json_file_path_label = Gtk.Label(label='Use Basic Json File Path: ')
+        json_file_path_label.set_tooltip_text(
+            'Basic File Path: {}, Also True if No Json File should be used (Specification Date in Output File will be empty)'.format(
+                confignator.get_option('tst-paths', 'tst_products')))
+        self.json_file_path_check = Gtk.CheckButton()
+        self.json_file_path_check.set_active(True)
+
         run_id_label = Gtk.Label(label='Select Run ID: ')
 
         # Select the Run ID which should be printed to the File
@@ -1280,11 +1359,45 @@ class Save_to_File_Dialog(Gtk.FileChooserDialog):
         self.savedetailes.pack_end(self.test_report_int, False, True, 10)
         self.savedetailes.pack_end(test_report_label, False, True, 10)
 
+        if not self.win.path_vrc and not self.win.path_cmd:
+            self.savedetailes.pack_end(self.log_file_path_check, False, True, 10)
+            self.savedetailes.pack_end(log_file_path_label, False, True, 10)
+
+        if not self.win.path_json:
+            self.savedetailes.pack_end(self.json_file_path_check, False, True, 10)
+            self.savedetailes.pack_end(json_file_path_label, False, True, 10)
+
+        if not self.win.path_json and not self.win.path_vrc and not self.win.path_cmd:
+            self.savedetailes.pack_end(self.test_name, False, True, 10)
+            self.savedetailes.pack_end(name_label, False, True, 10)
+
+
         area.pack_start(self.savedetailes, False, True, 0)
 
         self.show_all()
         return
 
+class File_Path_Dialog(Gtk.FileChooserDialog):
+    def __init__(self, parent=None, file=None, is_json=None):
+        super(File_Path_Dialog, self).__init__(title='Please choose {}'.format(file),
+                                                  parent=parent,
+                                                  action=Gtk.FileChooserAction.OPEN)
+
+        self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
+
+        self.win = parent
+        if not is_json:
+            self.set_current_folder(confignator.get_option(section='tst-logging', option='test_run'))
+        else:
+            self.set_current_folder(confignator.get_option(section='tst-paths', option='tst_products'))
+
+        if not is_json:
+            area = self.get_content_area()
+            #main_box = Gtk.HBox()
+            label = Gtk.Label(label='It does not matter if Command or Verification Log File is choosen, both are in the same Folder')
+            #main_box.pack_end(label, False, True, 10)
+            area.pack_start(label, False, True, 0)
+        self.show_all()
 def run():
     bus_name = confignator.get_option('dbus_names', 'progress-view')
     dbus.validate_bus_name(bus_name)
diff --git a/Tst/testing_library/testlib/analyse_command_log.py b/Tst/testing_library/testlib/analyse_command_log.py
index 521d9e5..d61d4ed 100644
--- a/Tst/testing_library/testlib/analyse_command_log.py
+++ b/Tst/testing_library/testlib/analyse_command_log.py
@@ -191,6 +191,8 @@ def get_general_run_info(filename, run_id=None):
     :rtype: list of dict
     """
     general_infos = []
+    precon_infos = []
+    postcon_infos = []
     with open(filename, 'r') as fileobject:
         for line in fileobject:
             if report.key_word_found(line, report.cmd_test_start_keyword):
@@ -200,7 +202,21 @@ def get_general_run_info(filename, run_id=None):
                 elif general_run_info['run_id'] == run_id:
                     general_infos.append(general_run_info)
 
-    return general_infos
+            elif report.key_word_found(line, report.cmd_precon_keyword):
+                precon = report.parse_step_from_json_string(line, report.cmd_precon_keyword)
+                if not run_id:
+                    precon_infos.append(precon)
+                elif precon['run_id'] == run_id:
+                    precon_infos.append(precon)
+
+            elif report.key_word_found(line, report.cmd_postcon_keyword):
+                postcon = report.parse_step_from_json_string(line, report.cmd_postcon_keyword)
+                if not run_id:
+                    postcon_infos.append(postcon)
+                elif postcon['run_id'] == run_id:
+                    postcon_infos.append(postcon)
+
+    return general_infos, precon_infos, postcon_infos
 
 if __name__ == '__main__':
     example_log_file = '../logs_test_runs/simple_example_command.log'
diff --git a/Tst/testing_library/testlib/analyse_test_run.py b/Tst/testing_library/testlib/analyse_test_run.py
index 282eee7..a64a7f8 100644
--- a/Tst/testing_library/testlib/analyse_test_run.py
+++ b/Tst/testing_library/testlib/analyse_test_run.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 """
-Gives a pretty print overview of the outcome of a test run. For this the log files of the executed Command and Verification scripts are used.
+Gives a pretty print overview or an output file of the outcome of a test run.
+For this the log files of the executed Command and Verification scripts are used.
 """
 import confignator
 import csv
@@ -12,7 +13,6 @@ import os, os.path
 import errno
 import logging
 
-
 import sys
 sys.path.append(confignator.get_option('paths', 'ccs'))
 import ccs_function_lib as cfl
@@ -22,7 +22,8 @@ import toolbox
 from testlib import analyse_verification_log
 from testlib import analyse_command_log
 
-test_name = 'test'
+test_name = 'test'  # Name of the test that should be analysed
+run_id = '20210722123605'  # Run ID that should be analysed or NONE for whole file
 
 cmd_log_file_end = '_command.log'
 vrc_log_file_end = '_verification.log'
@@ -31,11 +32,16 @@ basic_output_file_path = confignator.get_option('tst-logging', 'output-file-path
 basic_json_file_path = confignator.get_option('tst-paths', 'tst_products')
 output_file_header = ['Item', 'Description', 'Verification', 'TestResult']
 
-def save_result_to_file(test_name, log_file_path=None, output_file_path=None, json_file_path=None, run_id=None, logger=False):
+def save_result_to_file(test_name, log_file_path=None, output_file_path=None, json_file_path=None, run_id=None, test_report=None, logger=False):
     """
     Analyses the command and verification log file and creates a txt output table
     :param str test_name: the name of the test and of its log files
     :param str log_file_path: Path to the log files, None if basic one should be used
+    :param str output_file_path: Path were the output file should be saved
+    :param str json_file_path: Path to the json file, None if basic one should be used
+    :param str run_id: Output only for specific Run defined by Run ID, None use whole file
+    :param str test_report: The Test Report number as a string, end of the output file name
+    :param str logger: A logger
     """
     # Logger is only used, if function is called from a different file (Progres Viewer), therefore a logger and a file
     # are already set up, if file is called standalone, messages are printed
@@ -45,6 +51,7 @@ def save_result_to_file(test_name, log_file_path=None, output_file_path=None, js
         console_hdlr = toolbox.create_console_handler(hdlr_lvl=logging.DEBUG)
         logger.addHandler(hdlr=console_hdlr)
 
+    # Get the given file paths or use the basic ones specified in the tst.cfg file
     if not log_file_path:
         log_file_path = basic_log_file_path
     if not output_file_path:
@@ -52,14 +59,16 @@ def save_result_to_file(test_name, log_file_path=None, output_file_path=None, js
     if not json_file_path:
         json_file_path = basic_json_file_path + '/' + test_name + '.json'
 
-    cmd_log_file = log_file_path + test_name + cmd_log_file_end
-    vrc_log_file = log_file_path + test_name + vrc_log_file_end
+    cmd_log_file = log_file_path + '/' + test_name + cmd_log_file_end
+    vrc_log_file = log_file_path + '/' + test_name + vrc_log_file_end
 
+    # Get all the steps and verification steps from the respective log files
     cmd_steps = analyse_command_log.get_steps_and_commands(cmd_log_file)
     vrc_steps = analyse_verification_log.get_verification_steps(vrc_log_file)
 
     cmd_steps_filtered = []
     vrc_steps_filtered = []
+
     # Filter for a specific run
     if run_id:
         for step in cmd_steps:
@@ -73,34 +82,31 @@ def save_result_to_file(test_name, log_file_path=None, output_file_path=None, js
         cmd_steps = cmd_steps_filtered
         vrc_steps = vrc_steps_filtered
 
-    # Get the general run information, and the description
-    general_run_infos = analyse_command_log.get_general_run_info(cmd_log_file, run_id=run_id)
-
-    if run_id and len(general_run_infos) != 1:
-        if logger:
-            logger.error('Multiple Runs found with the same given Run ID')
-        else:
-            print('Multiple Runs found with the same given Run ID')
-        return
+    # Get the general run information/description/precon/postcon and the pre/post that do not belong to a step
+    # Is already filtered for a specific run if one was given
+    general_run_infos, precon_info, postcon_info = analyse_command_log.get_general_run_info(cmd_log_file, run_id=run_id)
 
+    # Give the output file its name, consits of test name, the specification nbr (version)
     name_start = '{}-TS-{}-TR-'.format(test_name, cmd_steps[0]['version'])
-    file_versions = []
-    try:
+
+    # Check if output folder exists otherwise make it
+    if not os.path.isdir(output_file_path):
+        os.makedirs(output_file_path)
+
+    # If Test Report Integer is not given, check all existing files and use the next higher one
+    if not test_report:
+        prev_test_reports = []
         for file_name in listdir(output_file_path):
             if file_name.startswith(name_start):
-                file_versions.append(int(file_name.split('-')[-1].split('.')[0]))
+                prev_test_reports.append(int(file_name.split('-')[-1].split('.')[0]))
 
-        file_versions.sort()
-        file_count = file_versions[-1] + 1 if file_versions else 1
-    except FileNotFoundError:
-        if logger:
-            logger.info('Used Folder: "{}" does not yet exist, is now created'.format(output_file_path))
-        else:
-            print('Used Folder: "{}" does not yet exist, is now created'.format(output_file_path))
-        file_count = 1
-        os.makedirs(output_file_path)
+        prev_test_reports.sort()
+        test_report = prev_test_reports[-1] + 1 if prev_test_reports else 1
+    else:
+        test_report = int(test_report)
 
-    output_file_name_path = output_file_path + name_start + f'{file_count:03d}' + '.txt'
+    # Output file name, with the path
+    output_file_name_path = output_file_path + '/' + name_start + f'{test_report:03d}' + '.txt'
 
     with open(output_file_name_path, 'w', encoding='UTF8', newline='') as file:
         writer = csv.writer(file, delimiter='|')
@@ -110,24 +116,26 @@ def save_result_to_file(test_name, log_file_path=None, output_file_path=None, js
 
         # write the general info line, if multiple descriptions/versions are found all are written to the output file
         version = get_version(cmd_steps, logger)
-        description = get_description(general_run_infos, logger)
-        writer.writerow([test_name, description, 'Test Spec. Version: ' + version, 'Test Rep. Version: ' + f'{file_count:03d}'])
+        description = get_general_run_info_info(general_run_infos, 'descr', logger)
+        writer.writerow([test_name, description, 'Test Spec. Version: ' + version, 'Test Rep. Version: ' + f'{test_report:03d}'])
 
-        # write date line
+        # write date line, first Date (Specification Date) is the last time the json file was changed or None if no json file was given
+        # second Date (Execution Date), Is the execution Date of the first step
         date_format = '%Y-%m-%d'
         specification_date = datetime.datetime.strftime(datetime.datetime.fromtimestamp(os.stat(json_file_path).st_mtime), date_format) if os.path.isfile(json_file_path) else ''  # When was the last time the json file was changed?
         time_execution = datetime.datetime.strftime(cmd_steps[0]['exec_date'], date_format)
         writer.writerow(['Date', '', specification_date, time_execution])
 
-        # write Precon line
-        writer.writerow(['Precon.', 'This has still to be solved', '', ''])  # TODO: What should be shown of the Precon
+        # write Precon line (Precon Descritpion)
+        precon_descr = get_general_run_info_info(precon_info, 'precon_descr', logger)
+        writer.writerow(['Precon.', precon_descr, '', ''])
 
-        # write the general comment line
-        general_comment = get_general_comment(general_run_infos, logger)
+        # write the general comment line, the Test Comment is only shown if it exists
+        general_comment = get_general_run_info_info(general_run_infos, 'comment', logger)
         if general_comment:
             writer.writerow(['Comment', general_comment, '', ''])
 
-        # write step line
+        # write step line with Step Description, VRC Step Description and Result
         for step in cmd_steps:
             for vrc_step in vrc_steps:
                 if step['step_id'] == vrc_step['step_id']:
@@ -138,30 +146,35 @@ def save_result_to_file(test_name, log_file_path=None, output_file_path=None, js
                         step_number_secondary) == 0 else '{}.{}'.format(step_number_primary,
                                                                         step_number_secondary)
                     step_desc = 'Step ' + str(step_number_shown)
-                    writer.writerow([step_desc, step['descr'], vrc_step['vrc_descr'], test_result])  # TODO: Third coloumn is what?
+                    writer.writerow([step_desc, step['descr'], vrc_step['vrc_descr'], test_result])
                     if step['comment']:
                         writer.writerow(['Comment', step['comment'], '', ''])
 
-        # write Postcon line
-        writer.writerow(['Postcon.', 'This has still to be solved', '', ''])  # TODO: What should be shown of the Postcon
+        # write Postcon line (Post Con Description)
+        postcon_descr = get_general_run_info_info(postcon_info, 'postcon_descr', logger)
+        writer.writerow(['Postcon.', postcon_descr, '', ''])
+
 
-def get_description(general_run_infos, logger):
+def get_general_run_info_info(general_run_infos, info_keyword, logger):
     """
-    Get a string of the different description that could be found
+    Get one information from the given general_run_infos (Typically: Description, General Commen, Pre/Post Conditions)
     :param list of dicts general_run_infos: all the general run information that were found
-    :return str description: A string that contains all found versions
+    :param str info_keyword: Which information should be extracted from the general run infos
+    :return str description: A string that contains all found informations
     """
+    info = ''
     for count, run_info in enumerate(general_run_infos):
         if count == 0:
-            description = run_info['descr']
+            info = run_info[info_keyword]
         else:
-            description += ' / ' + run_info['descr']
+            info += ' / ' + run_info[info_keyword]
             if logger:
-                logger.warning('More than one Description was found in the command log File')
+                logger.warning('More than one {} was found in the command log File'.format(info_keyword))
             else:
-                print('More than one Description was found in the command log File')
+                print('More than one {} was found in the command log File'.format(info_keyword))
+
+    return info
 
-    return description
 
 def get_version(steps, logger):
     """
@@ -170,6 +183,7 @@ def get_version(steps, logger):
     :return str version_final: A string that contains all found versions
     """
     version_list = []
+    version_final = ''
     for step in steps:
         if step['version'] not in version_list:
             version_list.append(step['version'])
@@ -184,24 +198,6 @@ def get_version(steps, logger):
                 print('More than one Version was found in the command log File')
     return version_final
 
-def get_general_comment(general_run_infos, logger):
-    """
-    Get a string of the different general comment that could be found
-    :param list of dicts general_run_infos: all the general run information that were found
-    :return str general_comment: A string that contains all found gerneral comments
-    """
-    for count, run_info in enumerate(general_run_infos):
-        if count == 0:
-            general_comment = run_info['comment']
-        else:
-            general_comment += ' / ' + run_info['comment']
-            if logger:
-                logger.warning('More than one General Comment was found in the command log File')
-            else:
-                print('More than one General Comment was found in the command log File')
-
-    return general_comment
-
 def show_basic_results(test_name, log_file_path=None):
     """
     Analyses the command and verification log file and prints a basic overview of the results
@@ -238,5 +234,5 @@ def show_basic_results(test_name, log_file_path=None):
     return
 
 if __name__ == '__main__':
-    save_result_to_file(test_name, run_id='20210721092439')
-    #show_basic_results(test_name)
+    #save_result_to_file(test_name, run_id=run_id)
+    show_basic_results(test_name)
diff --git a/Tst/testing_library/testlib/report.py b/Tst/testing_library/testlib/report.py
index aff1119..ac6920e 100644
--- a/Tst/testing_library/testlib/report.py
+++ b/Tst/testing_library/testlib/report.py
@@ -292,7 +292,7 @@ def print_event_data_tuple(tm_packets):
             logger.debug('Event {}: {} -> {}'.format(event_id, src, dest))
 
 
-def write_precondition_outcome(result, step_id, precon_descr):
+def write_precondition_outcome(result, run_id, precon_descr):
     """
     Logs the outcome of the establish_preconditions function in a test script.
     :param result: bool
@@ -300,15 +300,15 @@ def write_precondition_outcome(result, step_id, precon_descr):
     """
 
     logger.info('{} {}'.format(cmd_precon_keyword, make_json_string(result=result,
-                                                                    precon_desc=precon_descr,
-                                                                    step_id=step_id)))
+                                                                    precon_descr=precon_descr,
+                                                                    run_id=run_id)))
     if result is True:
         logger.info('Preconditions are fulfilled.\n')
     else:
         logger.warning('Preconditions are NOT fulfilled.\n')
 
 
-def write_postcondition_outcome(result, step_id, postcon_descr):
+def write_postcondition_outcome(result, run_id, postcon_descr):
     """
     Logs the outcome of the establish_postconditions function in a test script.
     :param result: bool
@@ -316,8 +316,8 @@ def write_postcondition_outcome(result, step_id, postcon_descr):
     """
 
     logger.info('{} {}'.format(cmd_postcon_keyword, make_json_string(result=result,
-                                                                     postcon_desc=postcon_descr,
-                                                                     step_id=step_id)))
+                                                                     postcon_descr=postcon_descr,
+                                                                     run_id=run_id)))
     if result is True:
         logger.info('Postconditions are fulfilled.\n')
     else:
diff --git a/Tst/tst/generator.py b/Tst/tst/generator.py
index 94f78c6..f68a538 100644
--- a/Tst/tst/generator.py
+++ b/Tst/tst/generator.py
@@ -101,6 +101,9 @@ def make_command_script(model, model_spec):
     with open(co_class_path, 'r') as class_file_obj:
         class_template_str = class_file_obj.read()
         class_file_obj.close()
+
+        test_comment_w_indent = model_spec.comment.replace('\n', " ' \\\n".format('\'') + 6 * indent + "'")
+
         class_str = string.Template(class_template_str)
         cls = class_str.substitute(testSpecClassName=create_class_name(model_spec.name),
                                    testSpecFileName=create_file_name(model_spec.name),
@@ -109,7 +112,7 @@ def make_command_script(model, model_spec):
                                    testSpecVersion=model_spec.version,
                                    testPreCondition=model_spec.precon_name,
                                    testPostCondition=model_spec.postcon_name,
-                                   testComment=model_spec.comment)
+                                   testComment=test_comment_w_indent)
         # add the header string
         content += '\n\n' + cls
 
@@ -118,9 +121,13 @@ def make_command_script(model, model_spec):
         pre_cond_template_str = pre_cond_file_obj.read()
         pre_cond_file_obj.close()
 
+        # Be sure that if it is multiline, that all have the right indent
+        pre_con_code = model_spec.precon_code.replace('\n', '\n' + 2 * indent)
+        pre_con_descr = model_spec.precon_descr.replace('\n', " ' \\\n" + 6 * indent + "'")
+
         pre_cond_template = string.Template(pre_cond_template_str)
-        pre_cond_combined = pre_cond_template.substitute(TestPreconEntry=model_spec.precon_code,
-                                                         TestPreconDescription=model_spec.precon_descr)
+        pre_cond_combined = pre_cond_template.substitute(TestPreconEntry=pre_con_code,
+                                                         TestPreconDescription=pre_con_descr)
         # add the header string
         content += '\n' + pre_cond_combined
 
@@ -132,11 +139,13 @@ def make_command_script(model, model_spec):
             step_str = string.Template(step_template_str)
             command_code = step.command_code
             command_code_w_indents = command_code.replace('\n', '\n' + 3 * indent)
+            step_comment_w_indents = step.step_comment.replace('\n', " ' \\\n" + 5 * indent + "'")
+            step_descr_w_indents = step.description.replace('\n', " ' \\\n" + 5 * indent + "'")
             if len(command_code_w_indents) == 0:
                 command_code_w_indents = 'pass'
             step = step_str.substitute(testStepNumber=step.step_number_test_format,
-                                       testStepDescription=step.description,
-                                       testStepComment=step.step_comment,
+                                       testStepDescription=step_descr_w_indents,
+                                       testStepComment=step_comment_w_indents,
                                        testStepCommandCode=command_code_w_indents,
                                        testSpecFileName=create_file_name(model_spec.name),
                                        testSpecClassName=create_class_name(model_spec.name))
@@ -148,14 +157,20 @@ def make_command_script(model, model_spec):
         post_cond_template_str = post_cond_file_obj.read()
         post_cond_file_obj.close()
 
+        # Be sure that if it is multiline, that all have the right indent
+        post_con_code = model_spec.postcon_code.replace('\n', '\n' + 2 * indent)
+        post_con_descr = model_spec.postcon_descr.replace('\n', " ' \\\n" + 6 * indent + "'")
+
         post_cond_template = string.Template(post_cond_template_str)
-        post_cond_combined = post_cond_template.substitute(TestPostconEntry=model_spec.postcon_code,
-                                                           TestPostconDescr=model_spec.description)
+        post_cond_combined = post_cond_template.substitute(TestPostconEntry=post_con_code,
+                                                           TestPostconDescr=post_con_descr)
+
+        #post_cond_combined.replace('\n', '\n' + 3 * indent)
 
         # add the header string
         content += '\n' + post_cond_combined
 
-    # add the footer (post condition and other functions)
+    # add the footer run and other functions)
     with open(co_footer_path, 'r') as footer_file_obj:
         footer_template_str = footer_file_obj.read()
         footer_file_obj.close()
@@ -188,13 +203,17 @@ def make_command_run_script(model, model_spec):
     with open(run_header_path, 'r') as header_file_obj:
         header_template = header_file_obj.read()
         header_file_obj.close()
+
+        precon_descr_w_indent = model_spec.precon_descr.replace('\n', '\n{}#{}'.format(indent, 4*indent))
+
         # add the header string
         header_template_str = string.Template(header_template)
         #header_str = header_template_str.substitute(testSpecClassName=create_class_name(model.name),
         #                                            testSpecFileName=create_file_name(model.name),
         #                                            testPrecondDesc=model.pre_condition.description)
         header_str = header_template_str.substitute(testSpecClassName=create_class_name(model_spec.name),
-                                                    testSpecFileName=create_file_name(model_spec.name))
+                                                    testSpecFileName=create_file_name(model_spec.name),
+                                                    TestPreconDescription=precon_descr_w_indent)
 
         content += header_str
 
@@ -203,22 +222,27 @@ def make_command_run_script(model, model_spec):
         step_template_str = step_file_obj.read()
         step_file_obj.close()
         for step in model.steps:
+            step_descr_w_indent = step.description.replace('\n', '\n{}#{}'.format(indent, 3*indent))
             step_str = string.Template(step_template_str)
             #step = step_str.substitute(testStepNumber=model.steps[step].step_number,
             #                           testStepDescription=model.steps[step].description,
             #                           testStepComment=model.steps[step].comment)
             step = step_str.substitute(testStepNumber=step.step_number_test_format,
-                                       testStepDescription=step.description)
+                                       testStepDescription=step_descr_w_indent)
             # add the string for a steps
             content += '\n' + step
 
     # add the step definitions
-    with open(run_footer_path, 'r') as step_file_obj:
-        step_template_str = step_file_obj.read()
-        step_file_obj.close()
-        header_template_str = string.Template(header_template)
+    with open(run_footer_path, 'r') as footer_file_obj:
+        footer_template_str = footer_file_obj.read()
+        footer_file_obj.close()
+
+        postcon_descr_w_indent = model_spec.postcon_descr.replace('\n', '\n{}#{}'.format(indent, 4*indent))
+
+        footer_template_str = string.Template(footer_template_str)
+        footer = footer_template_str.substitute(TestPostconDescription=postcon_descr_w_indent)
 
-        content += '\n' + header_template_str
+        content += '\n' + footer
 
     # create the new file
     file_path = create_script_path(name=model_spec.name, auxiliary=run_scrpt_auxiliary)
@@ -272,6 +296,8 @@ def make_verification_script(model, model_spec):
             step_str = string.Template(step_template_str)
             verification_code = step.verification_code
             verification_code_w_indents = verification_code.replace('\n', '\n' + 3 * indent)
+            verification_descr_w_indents = step.step_comment.replace('\n', " ' \\\n" + 6 * indent + "'")
+            step_descr_w_indents = step.description.replace('\n', " ' \\\n" + 5 * indent + "'")
             if len(verification_code_w_indents) == 0:
                 verification_code_w_indents = 'pass'
             #step = step_str.substitute(testStepNumber=model.steps[step].step_number,
@@ -279,8 +305,8 @@ def make_verification_script(model, model_spec):
             #                           testStepComment=model.steps[step].comment,
             #                           testStepVerificationCode=verification_code_w_indents)
             step = step_str.substitute(testStepNumber=step.step_number_test_format,
-                                       testStepDescription=step.description,
-                                       testStepVerificationDescription=step.verification_description,
+                                       testStepDescription=step_descr_w_indents,
+                                       testStepVerificationDescription=verification_descr_w_indents,
                                        testStepVerificationCode=verification_code_w_indents)
             # add the string for a steps
             content += '\n' + step
diff --git a/Tst/tst/generator_templates/co_footer.py b/Tst/tst/generator_templates/co_footer.py
index 9bee9f5..32f5cfb 100644
--- a/Tst/tst/generator_templates/co_footer.py
+++ b/Tst/tst/generator_templates/co_footer.py
@@ -47,13 +47,14 @@
                 finally:
                     # add the summary of the step to the result array
                     self.step_results.append(res)
+
+            # postcondition of the test
+            logger.info('Postconditions: {}'.format(self.postcondition))
+            self.postcond_ok = self.post_condition(pool_name=pool_name)
+
         else:
             logger.error('Preconditions could not be established. Test steps were not executed!\n')
 
-        # postcondition of the test
-        logger.info('Postconditions: {}'.format(self.postcondition))
-        self.postcond_ok = self.post_condition(pool_name=pool_name)
-
         # save the packet pool
         self.save_pool_in_file(pool_name=pool_name, save_pool=save_pool)
 
@@ -78,4 +79,4 @@
         now = datetime.now()  # current date and time
         if not self.run_id and pool_name:
             self.run_id = now.strftime("%Y%m%d%H%M%S")
-        return now.strftime("%Y%m%d%H%M%S%f")
\ No newline at end of file
+        return now.strftime("%Y%m%d%H%M%S%f")
diff --git a/Tst/tst/generator_templates/co_post_condition.py b/Tst/tst/generator_templates/co_post_condition.py
index 18b0c4d..f2a7037 100644
--- a/Tst/tst/generator_templates/co_post_condition.py
+++ b/Tst/tst/generator_templates/co_post_condition.py
@@ -44,7 +44,7 @@
         :rtype: bool
         """
         testing_logger.cmd_log_handler(__name__)
-        step_id = self.check_run_and_step_id(pool_name=pool_name)
+        self.check_run_and_step_id(pool_name=pool_name)
         postcon_descr = '$TestPostconDescr'
         success = False
         logger.info('establishing postconditions started')
@@ -52,6 +52,6 @@
         $TestPostconEntry
 
         logger.info('establishing postconditions finished')
-        report.write_postcondition_outcome(success, step_id, postcon_descr)
+        report.write_postcondition_outcome(success, self.run_id, postcon_descr)
         return success
 
diff --git a/Tst/tst/generator_templates/co_pre_condition.py b/Tst/tst/generator_templates/co_pre_condition.py
index 7dbfdd2..c4ca548 100644
--- a/Tst/tst/generator_templates/co_pre_condition.py
+++ b/Tst/tst/generator_templates/co_pre_condition.py
@@ -8,7 +8,7 @@
             True if the preconditions are fulfilled
         """
         testing_logger.cmd_log_handler(__name__)
-        step_id = self.check_run_and_step_id(pool_name=pool_name)
+        self.check_run_and_step_id(pool_name=pool_name)
         precon_descr = '$TestPreconDescription'
         success = False
         logger.info('establishing preconditions started')
@@ -16,7 +16,7 @@
         $TestPreconEntry
 
         logger.info('establishing preconditions finished')
-        report.write_precondition_outcome(success, step_id, precon_descr)
+        report.write_precondition_outcome(success, self.run_id, precon_descr)
         return success
 
     # INITIALIZE every step --------------------------------------------------------------------------------------------
diff --git a/Tst/tst/generator_templates/run_footer.py b/Tst/tst/generator_templates/run_footer.py
index 18e4341..6d48cc0 100644
--- a/Tst/tst/generator_templates/run_footer.py
+++ b/Tst/tst/generator_templates/run_footer.py
@@ -1,3 +1,3 @@
     #! CCS.BREAKPOINT
-    # Execute the Post Conditions
-    threading.Thread(target=testinstance.post_condition, kwargs = {'pool_name': pool_name}, daemon = True).start()
\ No newline at end of file
+    # Post-Conditions: $TestPostconDescription
+    threading.Thread(target=testinstance.post_condition, kwargs = {'pool_name': pool_name}, daemon = True).start()
diff --git a/Tst/tst/generator_templates/run_header.py b/Tst/tst/generator_templates/run_header.py
index 6f5726b..e10e981 100644
--- a/Tst/tst/generator_templates/run_header.py
+++ b/Tst/tst/generator_templates/run_header.py
@@ -59,5 +59,5 @@ if False:
 
 #! CCS.BREAKPOINT
 if False:
-    # Exectute the preconditions
+    # Pre-Conditions: $TestPreconDescription
     threading.Thread(target=testinstance.establish_preconditions, kwargs={'pool_name': pool_name}, daemon=True).start()
diff --git a/Tst/tst/tst.py b/Tst/tst/tst.py
index 4bb532d..1176f93 100755
--- a/Tst/tst/tst.py
+++ b/Tst/tst/tst.py
@@ -537,6 +537,7 @@ class TstAppWindow(Gtk.ApplicationWindow):
         self.save_as_file_dialog()
 
     def save_as_file_dialog(self):
+        current_test = self.current_test_instance()
         current_name = self.current_model().name
         current_model = self.current_model()
         dialog = Gtk.FileChooserDialog('Please choose a file',
@@ -548,8 +549,16 @@ class TstAppWindow(Gtk.ApplicationWindow):
         response = dialog.run()
         if response == Gtk.ResponseType.OK:
             file_selected = dialog.get_filename()
-            file_management.save_file(file_path=file_selected, test_spec=current_model, file_extension='json', logger=self.logger)
-            current_model.filename = file_selected
+            if '-v_' in file_selected:
+                test_name = file_selected.split('-v_')[0]
+                filename = file_selected
+            else:
+                test_name = file_selected.split('.json')[0]
+                filename = '{}-v_{}.json'.format(test_name, current_model.version)
+
+            file_management.save_file(file_path=filename, test_spec=current_model, file_extension='json', logger=self.logger)
+            current_test.filename = filename.split('/')[-1]
+            current_model.name = test_name.split('/')[-1]
         elif response == Gtk.ResponseType.CANCEL:
             pass
         dialog.destroy()
diff --git a/Tst/tst/view.py b/Tst/tst/view.py
index b657b34..1406044 100644
--- a/Tst/tst/view.py
+++ b/Tst/tst/view.py
@@ -287,6 +287,10 @@ class Board(Gtk.Box):
         # Set the test comment
         self.test_meta_data_comment.get_buffer().set_text(self.model.comment)
 
+        # Set the Locked STep numeration
+        self.text_meta_data_test_is_locked.set_active(self.model.primary_counter_locked)
+
+
 
     def collapse_all_steps(self, button):
         """ Close all expander of the steps """
@@ -1153,7 +1157,7 @@ class StepWidget(Gtk.EventBox):
         step_in_data_model = self.model.get_sequence(self.sequence).steps[stp_ndx]
         # use the setter of the data model
         if isinstance(step_in_data_model, data_model.Step):
-            step_in_data_model.command_code = self.step_description
+            step_in_data_model.description = self.step_description
         else:
             self.logger('step with the step number {} could not be found'.format(self.step_number))
         # update the model
@@ -1581,7 +1585,7 @@ class Edit_Pre_Post_Con_Dialog(Gtk.Dialog):
         self.add_buttons(
             Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK
         )
-        self.set_default_size(200, 200)
+        self.set_default_size(400, 400)
         self.first_entry = selection
         self.win = parent
         self.file_path = os.path.join(confignator.get_option('paths', 'tst'),
@@ -1718,6 +1722,7 @@ class Edit_Pre_Post_Con_Dialog(Gtk.Dialog):
         self.con_lbl_box.pack_start(con_lbl, False, False, 0)
 
         self.con_scrolled_window = Gtk.ScrolledWindow()
+        self.con_scrolled_window.set_tooltip_text('Set variable "success" to True/False, to check if {}-Conditon is fulfilled'.format(self.pre_post.upper()))
         #self.commands_scrolled_window.set_size_request(50, 100)
         self.con_view = GtkSource.View()
         self.con_view.set_auto_indent(True)
-- 
GitLab