from os import getcwd from robot.api import TestSuiteBuilder from parserobotfile import ParseRobotFile from parseapiutilsfile import ParseApiUtilsFile import re class GenerateRobotData: def __init__(self, robot_file: str, execdir: str): self.robot = ParseRobotFile(filename=robot_file, execdir=execdir) self.apiutil = ParseApiUtilsFile(filename=self.robot.resource_file) self.suite = TestSuiteBuilder().build(robot_file) self.test_cases = list() self.test_suite = dict() self.tags_template = list() self.documentation_template = str() self.arguments = list() self.args = list() self.identifier = { 'ContextInformation': 'CI', 'CommonBehaviours': 'CB', 'Consumption': 'Cons', 'Entity/RetrieveEntity': 'E', 'Entities/CreateEntity': 'E', 'Provision': 'Prov' } self.references = { 'v1.3.1': 'ETSI GS CIM 009 V1.3.1 [], clause ' } self.initial_conditions = { 'Setup Initial Entity': 'with {\n the SUT containing an initial Entity ${entity} with an id set to ${entityId}\n}', 'Initial State': 'with {\n the SUT in the "initial state"\n}' } self.headers = { '${CONTENT_TYPE_LD_JSON}': 'Content-Type' } self.ids = { '${ENTITIES_ENDPOINT_PATH}': '${entityId}' } self.base_TP_id = str() def get_info(self): return self.test_suite def parse_robot(self): self.start_suite() _ = [self.visit_test(test=x) for x in self.suite.tests] _ = [self.get_step_data(test=x.name) for x in self.suite.tests] self.test_suite['test_cases'] = self.test_cases print() def get_params(self, string: str): params = list() request = str() index_start = string.find('${response}') aux = string[index_start:].split('\n') # Get the list of params of the function, they are the keys if ' ... ' in aux[1]: request = aux[0].split(' ')[1] # We are in the case that the attributes are in following lines for i in range(1, len(aux)): if ' ... ' in aux[i]: regex = '\s{4}\.{3}\s{4}(.*)' param = re.match(pattern=regex, string=aux[i]) if aux: params.append(param.groups()[0]) else: break else: # the attributes are in the same line regex = r"\s*\$\{response\}=\s{4}(.*)\n" matches = re.finditer(regex, string, re.MULTILINE) request = aux[0].split(' ')[1] # We have two options from here, or the parameters are defined in the same line or the parameters are defined in # following lines, next lines for match in matches: # Check that we have 1 group matched if len(match.groups()) == 1: aux = match.group(1) # Get the list of keys params = aux.split(' ')[1:] else: print("Error, unexpected format") return request, params def get_step_data(self, test: str): string = self.robot.get_substring(initial_string=test, final_string=self.suite.name, include=False) request, params = self.get_params(string=string) self.check_header_parameters(params=params, test=test) verb, url = self.apiutil.get_response(keyword=request) index = None for i, item in enumerate(self.test_cases): if 'name' in item and item['name'] == test: index = i break self.test_cases[index]['http_verb'] = verb self.test_cases[index]['endpoint'] = self.get_header_value(key=url) expected_status_code = self.robot.get_expected_status_code(keyword='Check Response Status Code') self.test_cases[index]['expected_status_code'] = expected_status_code def check_header_parameters(self, params: list, test: str): value = str() # 1st case: value of the parameters are sent as it is header_key = [x for x in params if x in self.headers.keys()] if len(header_key) != 0: key = header_key[0] value = self.get_header_value(key=key) else: # 2nd case, maybe the params are defined in the way = for k in self.headers: aux = [x for x in params if k in x] if len(aux) != 0: key = aux[0].split('=')[1] value = self.get_header_value(key=key) else: key = None value = None if key is not None and value is not None: # Iterate over the list and find the index index = None for i, item in enumerate(self.test_cases): if 'name' in item and item['name'] == test: index = i break self.test_cases[index]['params'] = params self.test_cases[index][self.headers[key]] = value def get_header_value(self, key: str): value = str() # We can have a simple variable or a compound of two variables count = key.count('$') if count == 1: # Get the value of the Header key try: value = self.apiutil.variables[key] except KeyError: # It is not defined in ApiUtils, maybe in Robot File try: value = self.robot.variables[key] except KeyError: # ERROR, the header key is not defined print(f'ERROR, the header key {key} is undefined') elif count == 2: keys = key.split("$") key = f'${keys[1]}' second_key = f'${keys[2]}' try: second_key = self.ids[key] except KeyError: print(f"ERROR: Need to manage the {second_key} in GenerateRobotData::self.ids") # Get the value of the Header key try: value = self.apiutil.variables[key] value = f'{value}{second_key}' except KeyError: # It is not defined in ApiUtils, maybe in Robot File try: value = self.robot.variables[key] value = f'{value}{second_key}' except KeyError: # ERROR, the header key is not defined print(f'ERROR, the header key {key} is undefined') return value def start_suite(self): """Modify suite's tests to contain only every Xth.""" version = 'v1.3.1' tp_id = self.generate_name() reference, pics = self.generate_reference(version=version) self.test_suite = { 'tp_id': tp_id, 'test_objective': self.suite.doc, 'reference': reference, 'config_id': '', 'parent_release': version, 'pics_selection': pics, 'keywords': self.suite.keywords, 'teardown': self.suite.teardown, 'initial_condition': self.suite.setup, 'test_cases': list() } def visit_test(self, test): # Get Tags associated to the test if len(test.tags) == 0 and self.tags_template is not None: tags = self.tags_template else: tags = list(test.tags) # Get the Documentation associated to the test if len(test.doc) == 0 and self.documentation_template is not None: documentation = self.documentation_template else: documentation = test.doc # Get the Content-Type and Body associated to the Test if len(self.args) != 0: params = self.args[test.name] index = [index for index, value in enumerate(self.arguments) if value == '${content_type}'][0] content_type = params[index] body = self.get_body(string=test.name) else: # Need to develop this case content_type = '' body = '' # Generate Checks for Test Data list_checks = self.robot.get_checks(test_name=test.name, apiutils=self.apiutil) test_case = { 'name': test.name, 'permutation_tp_id': f'{self.base_TP_id}/{test.name.split(" ")[0]}', 'doc': documentation, 'tags': tags, 'setup': test.setup.name, 'teardown': test.teardown.name, 'template': test.template, 'content-type': content_type, 'body': body } try: self.test_suite['initial_condition'] = self.initial_conditions[test.setup.name] except KeyError: self.test_suite['initial_condition'] = self.initial_conditions['Initial State'] self.test_cases.append(test_case) def get_body(self, string: str) -> str: aux = string.split(' ')[1:] if len(aux) >= 2: aux = [self.get_body(x) for x in aux] aux = ' '.join(aux) elif len(aux) == 1: aux = re.sub(r'([a-z])([A-Z])', r'\1 \2', aux[0]) else: aux = re.sub(r'([a-z])([A-Z])', r'\1 \2', string) return aux def generate_name(self): current_path = getcwd() tp_id = str(self.suite.source.parent)[len(current_path):] if tp_id[0:4] == '/../': tp_id = tp_id[4:] for key, value in self.identifier.items(): tp_id = tp_id.replace(key, value) self.base_TP_id = tp_id name = self.suite.name.replace(" ", "_") tp_id = f'{self.base_TP_id}/{name}' return tp_id def generate_reference(self, version): # Get the list of tags in the different tests tags = [x.tags for x in self.suite.tests] tags = [element for sublist in tags for element in sublist if element[0].isdigit()] if len(tags) == 0: # We have different tests cases that call a test template reference, pics = self.generate_reference_template(version=version) else: # We have normal tests cases reference, pics = self.generate_reference_testcases(tags=tags, version=version) return reference, pics def generate_reference_template(self, version): # Get the list of arguments, we select the first one because the 2nd keyword corresponds # to the teardown operation args = [list(x.keywords)[0] for x in self.suite.tests] args = [{str(x.parent): list(x.args)} for x in args] self.args = dict() _ = [self.args.update(x) for x in args] template_name = list(set([list(x.keywords)[0].name for x in self.suite.tests]))[0] # Due to the information of the tags are contained in the Keyword description of the template, we need to # analyse the Keyword. string = self.robot.get_substring(initial_string='** Keywords ***', final_string='', include=False) reference, pics = self.get_info_from_template(name=template_name, string=string, version=version) return reference, pics def get_info_from_template(self, name: str, string: str, version: str): # Check that the name of the template is in the string receive print(name) # Get the Tags line and the tag value tags = self.get_substring(string=string, key='[Tags]') self.tags_template = tags[1:] tag = list(set([element for sublist in tags for element in tags if element[0].isdigit()]))[0] reference = f'{self.references[version]}{tag.replace("_", ".")}' pics = f'PICS_{tag}' # Get the arguments self.arguments = self.get_substring(string=string, key='[Arguments]') self.arguments = self.arguments[1:] # Get the documentation self.documentation_template = self.get_substring(string=string, key='[Documentation]') self.documentation_template = self.documentation_template[1:][0] return reference, pics def get_substring(self, string: str, key: str): pos1 = string.find(key) - 1 pos2 = string[pos1:].find('\n') result = string[pos1:pos1+pos2].split(' ') return result def generate_reference_testcases(self, tags: list, version: str): check_tags = all(item == tags[0] for item in tags) if check_tags is False or len(tags) == 0: print(f'ERROR: the Test Suite {self.suite.name} has different clauses or no clauses (Tags): {tags}\n' f'Unable to select the corresponding Reference of this Test Suite') reference = '' pics = '' else: # All the clauses are the same, so we select the first one reference = f'{self.references[version]}{tags[0].replace("_", ".")}' pics = f'PICS_{tags[0]}' return reference, pics