import pytest import os from lxml import etree """ To add tests, go down to the setup function. """ class schema_test: """ schema_test is a base object for conducting tests on schemas. """ def __init__(self, schema_name , schema_path): """ Starts object. schema_name - Name to refer to schema by schema_path - Path to retrieve RelaxNG schema """ self.__schema_name = schema_name self.__schema_path = schema_path self.__test_set_list = [] #Creates an empty list of test sets self.__validate_and_compile() def __validate_and_compile(self): """ Validates and compiles the schemas specified on instantiation. """ self.__validate_file(self.__schema_path , "RNG") #Read schema file relax_file_handler = file(self.__schema_path , 'r') #Parse schema file relax_parsed = etree.parse(relax_file_handler) #Compile schema file self.__compiled = etree.RelaxNG(relax_parsed) #Close schema file relax_file_handler.close() def __validate_file(self, file_name , extension): """ Ensures file exists and has the proper extension. """ if not os.path.exists(file_name): raise Exception("File does not exist - {}.".format(file_name)) if not file_name.upper().endswith("."+extension.upper()): raise Exception("File does not end with proper extension {} - {}".format(extension , file_name)) return True def __get_parsed_relaxng(self, file_path): ''' Returns root tag assuming file path is correct ''' #Read schema file handler = file(file_path , 'r') #Parse schema file parsed = etree.parse(handler) #Close schema file handler.close() return parsed def add_test(self , test_name , xml_path , error_class , parsed_xml = None): """ Add test case to object. test_name - Way of identifiying the test xml_path - Path to xml test file error_class - What sort of error that is going to be thrown. If error_class == None, it is assumed that the test will pass without raising exceptions. parsed_xml - Add the etree of the XML if available. """ test_set = (test_name , xml_path , error_class , parsed_xml) self.__test_set_list.append(test_set) def parse_and_add_directory(self, list_of_root_tags , directory): """ Parses through directory and all subdirectories and adds tests to object test lists list_of_root_tags - list of root tags to check for directory - directory to look in """ #Check if directory exists and list_of_root_tags isn't empty if len(list_of_root_tags) == 0: raise Exception("{} : List of root tags empty in parse_and_add_directory!".format(self.__schema_name)) if not os.path.isdir(directory): raise Exception("{} : Directory {} does not exist in parse_and_add_directory!".format(self.__schema_name , directory)) for subdir, dirs, files in os.walk(directory): for file in files: if file.upper().endswith(".XML"): try: new_path = os.path.join(subdir, file) parsed = self.__get_parsed_relaxng(new_path) root_tag = parsed.getroot().tag if root_tag in list_of_root_tags: self.add_test("Path Added: " + file , new_path , None , parsed_xml = parsed) except: pass def run_all_tests(self): """ Runs all the tests consecutivley. """ for index in range(len(self.__test_set_list)): self.run_test(index) def get_test_amount(self): """ Returns the amount of tests in the object. """ return len(self.__test_set_list) def run_test(self, index): """ Runs test of index """ if index >= len(self.__test_set_list): raise Exception("Illegal index was accessed") test_set = self.__test_set_list[index] xml_parsed = test_set[3] if not xml_parsed: self.__validate_file(test_set[1] , "XML") xml_file_handler = file(test_set[1] , 'r') xml_parsed = etree.parse(xml_file_handler) xml_file_handler.close() if test_set[2]: with pytest.raises(test_set[2]) as excinfo: self.__compiled.assert_(xml_parsed) if excinfo: print "Schema " + self.__schema_name + " failed validating the current file." print "\n" print test_set[0] + " raised the wrong exception or passed, when fail was expected (Exception " + str(test_set[2]) + "." print "File path - " + test_set[1] print excinfo print "\n" raise else: try: self.__compiled.assert_(xml_parsed) except: print "Schema " + self.__schema_name + " failed validating the current file." print "\n" print test_set[0] + " raised an exception but was supposed to pass." print "File path - " + test_set[1] print "\n" raise def print_header(self): """ Prints a header string for a schema_test object. """ print("\nTesting {} - {}\n".format(self.__schema_name , self.__schema_path)) def setup(): """ Sets up and returns test_list, which is a set of schema_test objects. """ test_list = [] #Create schema object topology_test = schema_test("Topology" , "ISF/topology_schema.rng") component_test = schema_test("Component" , "ISF/component_schema.rng") command_test = schema_test("Command" , "ISF/command_schema.rng") parameter_test = schema_test("Parameter" , "ISF/parameters_schema.rng") channel_test = schema_test("Channel" , "ISF/channel_schema.rng") interface_test = schema_test("Interface" , "ISF/interface_schema.rng") serializable_test = schema_test("Serializable" , "ISF/serializable_schema.rng") event_test = schema_test("Event" , "ISF/event_schema.rng") #Declare schema tests channel_test.add_test("All working" , "sample_XML_files/channel/allWorking.xml" , None) channel_test.add_test("High Orange string instead of number" , "sample_XML_files/channel/colorString.xml" , AssertionError) channel_test.add_test("Missing comments" , "sample_XML_files/channel/missingComments.xml" , AssertionError) channel_test.add_test("Missing data type" , "sample_XML_files/channel/missingDataType.xml" , AssertionError) channel_test.add_test("Missing enum" , "sample_XML_files/channel/missingEnum.xml" , AssertionError) command_test.add_test("All working" , "sample_XML_files/command/allWorking.xml" , None) command_test.add_test("Command size is negative" , "sample_XML_files/command/negativeCommandSize.xml" , AssertionError) command_test.add_test("Enum missing when type ENUM is specified" , "sample_XML_files/command/missingEnum.xml" , AssertionError) command_test.add_test("Kind not sync nor async" , "sample_XML_files/command/kindMixed.xml" , AssertionError) command_test.add_test("String size not defined" , "sample_XML_files/command/noStringSize.xml" , AssertionError) component_test.add_test("Base all working" , "sample_XML_files/component/baseAllWorking.xml" , None) component_test.add_test("Complex all working" , "sample_XML_files/component/complexAllWorking.xml" , None) component_test.add_test("No ports" , "sample_XML_files/component/noPorts.xml" , AssertionError) component_test.add_test(" tag instead of tag" , "sample_XML_files/component/interfaceOnly.xml" , AssertionError) event_test.add_test("All working" , "sample_XML_files/event/allWorking.xml" , None) event_test.add_test("Event throttle negative" , "sample_XML_files/event/negativeThrottle.xml" , AssertionError) event_test.add_test("Formot string missing" , "sample_XML_files/event/missingFormatString.xml" , AssertionError) event_test.add_test("Severity not valid" , "sample_XML_files/event/unknownSeverity.xml" , AssertionError) interface_test.add_test("All working" , "sample_XML_files/interface/allWorking.xml" , None) interface_test.add_test("Multiple return tags" , "sample_XML_files/interface/multipleReturns.xml" , AssertionError) interface_test.add_test("No ENUM in return type ENUM" , "sample_XML_files/interface/noEnumInReturn.xml" , AssertionError) interface_test.add_test("No return tags" , "sample_XML_files/interface/noReturns.xml" , None) interface_test.add_test("Priority attribute is a string" , "sample_XML_files/interface/stringPriority.xml" , AssertionError) parameter_test.add_test("All working" , "sample_XML_files/parameter/allWorking.xml" , None) parameter_test.add_test("Float in native integer type default attribute" , "sample_XML_files/parameter/floatInInt.xml" , AssertionError) parameter_test.add_test("Negative in unsigned 8 bit default attribute" , "sample_XML_files/parameter/negativeInUnsigned.xml" , AssertionError) parameter_test.add_test("No size attribute for string type attribute" , "sample_XML_files/parameter/noStringSize.xml" , AssertionError) parameter_test.add_test("String in 32 bit float attribute" , "sample_XML_files/parameter/stringInFloat.xml" , AssertionError) serializable_test.add_test("All working" , "sample_XML_files/serializable/allWorking.xml" , None) serializable_test.add_test("Multiple members tags" , "sample_XML_files/serializable/multipleMembers.xml" , AssertionError) serializable_test.add_test("No members tag" , "sample_XML_files/serializable/noMembers.xml" , AssertionError) serializable_test.add_test("No name in root" , "sample_XML_files/serializable/noName.xml" , AssertionError) serializable_test.add_test("No type in member" , "sample_XML_files/serializable/noType.xml" , AssertionError) topology_test.add_test("All working" , "sample_XML_files/topology/allWorking.xml" , None) topology_test.add_test("Negative connection number." , "sample_XML_files/topology/negativeConnectionNumber.xml" , AssertionError) topology_test.add_test("No Imports" , "sample_XML_files/topology/noImports.xml" , None) topology_test.add_test("No connections made" , "sample_XML_files/topology/noConnections.xml" , AssertionError) topology_test.add_test("No instances" , "sample_XML_files/topology/noInstances.xml" , AssertionError) topology_test.add_test("No types" , "sample_XML_files/topology/noTypes.xml" , AssertionError) #Add more schema tests channel_test.parse_and_add_directory(["telemetry"] , '../test') command_test.parse_and_add_directory(["commands"] , '../test') component_test.parse_and_add_directory(["component"] , '../test') event_test.parse_and_add_directory(["events"] , '../test') interface_test.parse_and_add_directory(["interface" , "port"] , '../test') parameter_test.parse_and_add_directory(["parameters"] , '../test') serializable_test.parse_and_add_directory(["serializable"] , '../test') topology_test.parse_and_add_directory(["deployment" , "assembly"] , '../test') #Add schemas to test_list test_list.append(topology_test) test_list.append(component_test) test_list.append(command_test) test_list.append(parameter_test) test_list.append(channel_test) test_list.append(interface_test) test_list.append(serializable_test) test_list.append(event_test) return test_list def get_test_list(): test_list = setup() out = [] for test_obj in test_list: tl = test_obj.get_test_amount() for ti in range(tl): out.append((test_obj.run_test , ti)) return out @pytest.fixture(scope="module", params=get_test_list()) def current_test(request): return request.param def tests_all(current_test): func = current_test[0] index = current_test[1] func(index) if __name__ == "__main__": test_list = setup() for test in test_list: test.print_header() test.run_all_tests()