Ejemplo n.º 1
0
def checkPythonPxdHeader(src_path, bin_path, ignorefilename, pxds_out,
                         print_pxd, output_format, generate_pxd):
    """ Checks a set of doxygen xml file against a set of pxd header files

    For each C++ class found in the doxygen XML files, it tries to identify the
    corresponding pxd file. If a pxd file exists, it checks whether

    i)   all public functions, enums and attributes are wrapped in Python
    ii)  all void return types are correct in Python (these are not checked at
         compile time)
    iii) all fields of an enum are accessible from Python

    If it finds a method missing, the script suggests an addition and if a
    whole class is missing, the script writes suggestion .pxd file to a
    specified location (pxds_out).

    The output format can either be in text form (human readable) or in xml
    form which will try to overwrite the cdash Test.xml file to proivide an
    output to cdash. Please only specify xml output if in your binary
    you have executed "ctest -D Nightly" or similar.

    TODO also look at ./doc/doxygen/doxygen-error.log ?

    Make sure to build the doxygen xmls first with
    $ make doc_xml

    """

    xml_output_path = os.path.join(bin_path, "doc", "xml_output")
    xml_files = glob.glob(xml_output_path + "/*.xml")
    print "Found %s doxygen xml files" % (len(xml_files))
    if len(xml_files) == 0:
        raise Exception("No doxygen files found in directory:\n%s,\n" % xml_output_path + \
                        "Please make sure you build the doxygen xmls (make doc_xml)\n" +\
                        "and that you specified the correct directory." )

    print "Creating pxd file map"
    pxd_file_matching = create_pxd_file_map(src_path)
    print "Found %s matching pxd files" % len(pxd_file_matching)
    cnt = Counter()
    cnt.total = len(xml_files)
    ignorefile = IgnoreFile()
    if len(ignorefilename) > 0:
        ignorefile.load(ignorefilename)

    if len(generate_pxd) > 0:
        print "Will only consider class", generate_pxd

    def pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd):
        if print_pxd:
            print ""
            print pxd_text
        if len(pxds_out) > 0 and pxd_text is not None:
            fname = os.path.join(pxds_out,
                                 "%s.pxd" % comp_name.split("::")[-1])
            with open(fname, "w") as f:
                f.write(pxd_text)

    testresults = TestResultHandler()

    # Iterate through all xml files generated by doxygen (these are all the
    # classes available in OpenMS)
    for class_cntr, f in enumerate(xml_files):

        # Only look out for one specific pxd file (see option --generate_pxd_for)
        if len(generate_pxd) > 0:
            if f.find(generate_pxd) == -1:
                continue

        # Try to parse the doxygen file
        dfile = DoxygenXMLFile(f)
        res = dfile.parse_doxygen()
        if dfile.parsing_error:
            # e.g. <computeroutput><bold></computeroutput>
            cnt.skipped_could_not_parse += 1
            msg = "Skip:: No-parse :: could not parse file %s with error %s" % (
                f, dfile.parsing_error_message)
            tres = TestResult(False, msg, name="%s_test" % f)
            testresults.append([tres])
            continue
        elif os.path.basename(f) == "index.xml":
            # Skip the index file
            continue

        # Parse class and namespace
        # Skip certain namespaces or those without any namespace (we are only
        # interested in the OpenMS and OpenSwath namespace).
        compound = res.get_compounddef()
        comp_name = compound.get_compoundname()
        if len(comp_name.split("::")) == 1:
            # Not inside a namespace -> skip
            continue

        namespace = comp_name.split("::")[0]
        if namespace in ["std", "Ui", "xercesc", "seqan"]:
            # Namespace std, xerces, UI -> skip
            continue
        elif comp_name.startswith("ms::numpress"):
            # MS Numpress namespace
            continue
        elif not (comp_name.startswith("OpenMS")
                  or comp_name.startswith("OpenSwath")):
            # Continue without checking or generating a testreport
            print "Unknown namespace", comp_name
            continue

        # Skip files which are listed in the "ignore" file
        if ignorefile.isNameIgnored(comp_name):
            msg = "Skip:: Ignored :: Class %s (file %s)" % (comp_name, f)
            tres = TestResult(True,
                              msg,
                              log_level=10,
                              name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_ignored += 1
            continue

        # Ignore private/protected classes
        if compound.prot != "public":
            msg = "Skip:: Protected :: Compound %s is not public, skip" % (
                comp_name)
            tres = TestResult(True,
                              msg,
                              log_level=10,
                              name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_protected += 1
            continue

        # Get file location and skip empty files
        file_location = dfile.getCompoundFileLocation()
        if file_location is None:
            msg = "Skip:: No-data :: there is no source file for %s" % f
            tres = TestResult(True,
                              msg,
                              log_level=10,
                              name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_no_location += 1
            continue

        # Skip empty classes
        openms_file = OpenMSSourceFile(file_location)
        maintainer = openms_file.getMaintainer()
        if dfile.isEmpty(True):
            msg = "Skip:: No-data :: File is empty (no section definitions found or only definitions found) in file %s" % f
            tres = TestResult(True,
                              msg,
                              log_level=10,
                              name="%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_sections += 1
            continue

        # Retrieve all associated pxd files with this specific header file
        file_location_key = file_location
        if len(file_location.split("include/")) > 1:
            file_location_key = file_location.split("include/")[1]

        if file_location_key in pxd_file_matching:
            pxdfiles = pxd_file_matching[file_location_key]
        else:
            msg = "Skip:: No-pxd :: No pxd file exists for Class %s (File %s) %s" % (
                comp_name, file_location, f)
            tres = TestResult(False, msg, name="Missing_%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_pxd_file += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location,
                                                xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        # Parse the pxd files corresponding to this doxygen XML file
        try:
            pxd_class = PXDFile.parse_multiple_files(pxdfiles, comp_name)
            pxdfile = pxd_class.pxdfile
        except PXDFileParseError as e:
            # TODO specific exception
            msg = "Skip:: No-pxd :: " + e.message + "for %s (in pxd file %s)" % (
                comp_name, pxdfiles)
            tres = TestResult(False, msg, name="Missing_%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_pxd_match += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location,
                                                xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        # Count the current file
        cnt.parsed += 1

        # Loop through all methods which are listed in the doxygen XML file and match them to the pxd file
        classtestresults = []
        for method_cntr, mdef in enumerate(dfile.iterMemberDef()):

            if mdef.get_name() in ignorefile.getIgnoredMethods(comp_name):
                msg = "Ignore member function/attribute : %s %s %s " % (
                    mdef.kind, mdef.prot, mdef.name)
                tres = TestResult(True, msg, log_level=10)
            else:
                tres = handle_member_definition(mdef, pxd_class, cnt)

            testname = "%s_%s::%s" % (comp_name, method_cntr, mdef.name)
            testname = testname.replace("::", "_")
            testname = re.sub('[^a-zA-Z0-9_]+', '', testname)
            tres.comp_name = comp_name
            tres.file_location = file_location
            tres.pxdfile = pxdfile
            tres.maintainer = maintainer
            tres.name = testname
            classtestresults.append(tres)

        testresults.append(classtestresults)

    writeOutput(testresults, output_format, cnt, bin_path)
Ejemplo n.º 2
0
def checkPythonPxdHeader(src_path, bin_path, ignorefilename, pxds_out, print_pxd, output_format, generate_pxd):
    """ Checks a set of doxygen xml file against a set of pxd header files

    For each C++ class found in the doxygen XML files, it tries to identify the
    corresponding pxd file. If a pxd file exists, it checks whether

    i)   all public functions, enums and attributes are wrapped in Python
    ii)  all void return types are correct in Python (these are not checked at
         compile time)
    iii) all fields of an enum are accessible from Python

    If it finds a method missing, the script suggests an addition and if a
    whole class is missing, the script writes suggestion .pxd file to a
    specified location (pxds_out).

    The output format can either be in text form (human readable) or in xml
    form which will try to overwrite the cdash Test.xml file to proivide an
    output to cdash. Please only specify xml output if in your binary
    you have executed "ctest -D Nightly" or similar.

    TODO also look at ./doc/doxygen/doxygen-error.log ?

    Make sure to build the doxygen xmls first with
    $ make doc_xml

    """

    xml_output_path = os.path.join(bin_path, "doc", "xml_output")
    xml_files = glob.glob(xml_output_path + "/*.xml")
    print "Found %s doxygen xml files" % (len(xml_files))
    if len(xml_files) == 0:
        raise Exception("No doxygen files found in directory:\n%s,\n" % xml_output_path + \
                        "Please make sure you build the doxygen xmls (make dox_xml)\n" +\
                        "and that you specified the correct directory." )

    print "Creating pxd file map"
    pxd_file_matching = create_pxd_file_map(src_path)
    cnt = Counter()
    cnt.total = len(xml_files)
    ignorefile = IgnoreFile()
    if len(ignorefilename) > 0:
        ignorefile.load(ignorefilename)

    def pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd):
        if print_pxd:
            print ""
            print pxd_text
        if len(pxds_out) > 0 and pxd_text is not None:
            fname =  os.path.join(pxds_out, "%s.pxd" % comp_name.split("::")[-1] )
            with open(fname, "w" ) as f:
                f.write(pxd_text)

    testresults = TestResultHandler()
    for class_cntr, f in enumerate(xml_files):
        if len(generate_pxd) > 0:
            if f.find(generate_pxd) == -1:
                continue
        dfile = DoxygenXMLFile(f)
        res = dfile.parse_doxygen()
        if dfile.parsing_error:
            # e.g. <computeroutput><bold></computeroutput>
            cnt.skipped_could_not_parse += 1
            msg = "Skip:: No-parse :: could not parse file %s with error %s" % (f, dfile.parsing_error_message)
            tres = TestResult(False, msg, name="%s_test" % f )
            testresults.append([ tres ])
            continue
        elif os.path.basename(f) == "index.xml":
            # Skip the index
            continue
        compound = res.get_compounddef()
        comp_name = compound.get_compoundname()
        if not (comp_name.startswith("OpenMS") or comp_name.startswith("OpenSwath") ):
            # Continue without checking or generating a testreport
            continue
        if ignorefile.isNameIgnored(comp_name):
            msg = "Skip:: Ignored :: Class %s (file %s)" % (comp_name, f)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_ignored += 1
            continue
        if compound.prot != "public":
            msg = "Skip:: Protected :: Compound %s is not public, skip" % (comp_name)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_protected += 1
            continue
        file_location = dfile.getCompoundFileLocation()
        if file_location is None:
            msg = "Skip:: No-data :: there is no source file for %s" % f
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_no_location += 1
            continue
        openms_file = OpenMSSourceFile(file_location)
        maintainer = openms_file.getMaintainer()
        if dfile.isEmpty(True):
            msg = "Skip:: No-data :: File is empty (no section definitions found or only definitions found) in file %s" % f
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_sections += 1
            continue
        if file_location in pxd_file_matching:
            pxdfiles = pxd_file_matching[file_location]
        else:
            msg = "Skip:: No-pxd :: No pxd file exists for Class %s (File %s) %s" % (comp_name, file_location, f)
            tres = TestResult(False, msg,  name="Missing_%s_test" % comp_name )
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_pxd_file += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue
        try:
            pxd_class = PXDFile.parse_multiple_files(pxdfiles, comp_name)
            pxdfile = pxd_class.pxdfile
        except PXDFileParseError as e:
            # TODO specific exception
            msg = "Skip:: No-pxd :: " + e.message + "for %s (in pxd file %s)" % (comp_name, pxdfiles)
            tres = TestResult(False, msg,  name="Missing_%s_test" % comp_name )
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_pxd_match += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        cnt.parsed += 1
        # Loop through all methods
        classtestresults = []
        for method_cntr,mdef in enumerate(dfile.iterMemberDef()):

            if mdef.get_name() in ignorefile.getIgnoredMethods(comp_name):
                msg = "Ignore member function/attribute : %s %s %s " % (mdef.kind, mdef.prot, mdef.name)
                tres = TestResult(True, msg, log_level=10)
            else:
                tres = handle_member_definition(mdef, pxd_class, cnt)

            testname = "%s_%s::%s" % (comp_name, method_cntr, mdef.name)
            testname = testname.replace("::", "_")
            testname = re.sub('[^a-zA-Z0-9_]+', '', testname)
            tres.comp_name = comp_name
            tres.file_location = file_location
            tres.pxdfile = pxdfile
            tres.maintainer = maintainer
            tres.name = testname
            classtestresults.append(tres)

        testresults.append(classtestresults)

    ###################################
    #   Output
    ###################################
    if output_format in ["text", "text-verbose", "text-quiet"]:
        for classtestresults in testresults:
            if len(classtestresults) > 1:
                t = classtestresults[0]
                lenfailed = len([t for t in classtestresults if not t.isPassed() ] )
                if lenfailed > 0:
                    print "== Test results for element %s - from Cpp file %s with maintainer %s and corresponding pxd file %s" % (
                        t.comp_name, t.file_location, t.maintainer, t.pxdfile)
            for tres in classtestresults:
                if not tres.isPassed():
                    print tres.message
                elif tres.log_level >= 10 and output_format in ["text", "text-verbose"]:
                    print tres.message
                elif tres.log_level >= 0 and output_format in ["text-verbose"]:
                    print tres.message

    elif output_format == "xml":

        # check if all files required to report in CDash are present
        tag_file = os.path.join(bin_path, "Testing", "TAG" )
        try:
            # read the first line of tagfile (TAG) -> if it does not exist,
            # an IOError is thrown
            with open(tag_file) as f:
                ctestReportingPath = f.readline().strip()
                ctestReportingPath = os.path.join(bin_path, "Testing", ctestReportingPath)
                if not os.path.exists( ctestReportingPath ):
                    raise Exception("Missing directory at %s" % ( ctestReportingPath ) )
        except IOError:
            raise Exception("Missing nightly test information at %s" % (tag_file) )

        template_path = os.path.join(ctestReportingPath, "Test.xml" )
        testresults.to_cdash_xml(template_path, template_path)

    else:
        raise Exception("Unknown output format %s" % output_format)

    cnt.print_stats()
    cnt.print_skipping_reason()
def checkPythonPxdHeader(src_path, bin_path, ignorefilename, pxds_out, print_pxd, output_format, generate_pxd):
    """ Checks a set of doxygen xml file against a set of pxd header files

    For each C++ class found in the doxygen XML files, it tries to identify the
    corresponding pxd file. If a pxd file exists, it checks whether

    i)   all public functions, enums and attributes are wrapped in Python
    ii)  all void return types are correct in Python (these are not checked at
         compile time)
    iii) all fields of an enum are accessible from Python

    If it finds a method missing, the script suggests an addition and if a
    whole class is missing, the script writes suggestion .pxd file to a
    specified location (pxds_out).

    The output format can either be in text form (human readable) or in xml
    form which will try to overwrite the cdash Test.xml file to proivide an
    output to cdash. Please only specify xml output if in your binary
    you have executed "ctest -D Nightly" or similar.

    TODO also look at ./doc/doxygen/doxygen-error.log ?

    Make sure to build the doxygen xmls first with
    $ make doc_xml

    """

    xml_output_path = os.path.join(bin_path, "doc", "xml_output")
    xml_files = glob.glob(xml_output_path + "/*.xml")
    print "Found %s doxygen xml files" % (len(xml_files))
    if len(xml_files) == 0:
        raise Exception("No doxygen files found in directory:\n%s,\n" % xml_output_path + \
                        "Please make sure you build the doxygen xmls (make doc_xml)\n" +\
                        "and that you specified the correct directory." )

    print "Creating pxd file map"
    pxd_file_matching = create_pxd_file_map(src_path)
    print "Found %s matching pxd files" % len(pxd_file_matching)
    cnt = Counter()
    cnt.total = len(xml_files)
    ignorefile = IgnoreFile()
    if len(ignorefilename) > 0:
        ignorefile.load(ignorefilename)

    if len(generate_pxd) > 0:
        print "Will only consider class", generate_pxd

    def pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd):
        if print_pxd:
            print ""
            print pxd_text
        if len(pxds_out) > 0 and pxd_text is not None:
            fname =  os.path.join(pxds_out, "%s.pxd" % comp_name.split("::")[-1] )
            with open(fname, "w" ) as f:
                f.write(pxd_text)

    testresults = TestResultHandler()

    # Iterate through all xml files generated by doxygen (these are all the
    # classes available in OpenMS)
    for class_cntr, f in enumerate(xml_files):

        # Only look out for one specific pxd file (see option --generate_pxd_for)
        if len(generate_pxd) > 0:
            if f.find(generate_pxd) == -1:
                continue

        # Try to parse the doxygen file
        dfile = DoxygenXMLFile(f)
        res = dfile.parse_doxygen()
        if dfile.parsing_error:
            # e.g. <computeroutput><bold></computeroutput>
            cnt.skipped_could_not_parse += 1
            msg = "Skip:: No-parse :: could not parse file %s with error %s" % (f, dfile.parsing_error_message)
            tres = TestResult(False, msg, name="%s_test" % f )
            testresults.append([ tres ])
            continue
        elif os.path.basename(f) == "index.xml":
            # Skip the index file
            continue

        # Parse class and namespace
        # Skip certain namespaces or those without any namespace (we are only
        # interested in the OpenMS and OpenSwath namespace).
        compound = res.get_compounddef()
        comp_name = compound.get_compoundname()
        if len(comp_name.split("::") ) == 1:
            # Not inside a namespace -> skip
            continue

        namespace = comp_name.split("::")[0]
        if namespace in ["std", "Ui", "xercesc", "seqan"]:
            # Namespace std, xerces, UI -> skip
            continue
        elif comp_name.startswith("ms::numpress"):
            # MS Numpress namespace
            continue
        elif not (comp_name.startswith("OpenMS") or comp_name.startswith("OpenSwath") ):
            # Continue without checking or generating a testreport
            print "Unknown namespace", comp_name
            continue

        # Skip files which are listed in the "ignore" file
        if ignorefile.isNameIgnored(comp_name):
            msg = "Skip:: Ignored :: Class %s (file %s)" % (comp_name, f)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_ignored += 1
            continue

        # Ignore private/protected classes
        if compound.prot != "public":
            msg = "Skip:: Protected :: Compound %s is not public, skip" % (comp_name)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_protected += 1
            continue

        # Get file location and skip empty files
        file_location = dfile.getCompoundFileLocation()
        if file_location is None:
            msg = "Skip:: No-data :: there is no source file for %s" % f
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([ tres ])
            cnt.skipped_no_location += 1
            continue

        # Skip empty classes
        openms_file = OpenMSSourceFile(file_location)
        maintainer = openms_file.getMaintainer()
        if dfile.isEmpty(True):
            msg = "Skip:: No-data :: File is empty (no section definitions found or only definitions found) in file %s" % f
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_sections += 1
            continue

        # Retrieve all associated pxd files with this specific header file
        file_location_key = file_location
        if len(file_location.split("include/")) > 1:
            file_location_key = file_location.split("include/")[1]

        if file_location_key in pxd_file_matching:
            pxdfiles = pxd_file_matching[file_location_key]
        else:
            msg = "Skip:: No-pxd :: No pxd file exists for Class %s (File %s) %s" % (comp_name, file_location, f)
            tres = TestResult(False, msg,  name="Missing_%s_test" % comp_name )
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_pxd_file += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        # Parse the pxd files corresponding to this doxygen XML file
        try:
            pxd_class = PXDFile.parse_multiple_files(pxdfiles, comp_name)
            pxdfile = pxd_class.pxdfile
        except PXDFileParseError as e:
            # TODO specific exception
            msg = "Skip:: No-pxd :: " + e.message + "for %s (in pxd file %s)" % (comp_name, pxdfiles)
            tres = TestResult(False, msg,  name="Missing_%s_test" % comp_name )
            tres.maintainer = maintainer
            testresults.append([ tres ])
            cnt.skipped_no_pxd_match += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        # Count the current file
        cnt.parsed += 1

        # Loop through all methods which are listed in the doxygen XML file and match them to the pxd file
        classtestresults = []
        for method_cntr,mdef in enumerate(dfile.iterMemberDef()):

            if mdef.get_name() in ignorefile.getIgnoredMethods(comp_name):
                msg = "Ignore member function/attribute : %s %s %s " % (mdef.kind, mdef.prot, mdef.name)
                tres = TestResult(True, msg, log_level=10)
            else:
                tres = handle_member_definition(mdef, pxd_class, cnt)

            testname = "%s_%s::%s" % (comp_name, method_cntr, mdef.name)
            testname = testname.replace("::", "_")
            testname = re.sub('[^a-zA-Z0-9_]+', '', testname)
            tres.comp_name = comp_name
            tres.file_location = file_location
            tres.pxdfile = pxdfile
            tres.maintainer = maintainer
            tres.name = testname
            classtestresults.append(tres)

        testresults.append(classtestresults)

    writeOutput(testresults, output_format, cnt, bin_path)
Ejemplo n.º 4
0
def checkPythonPxdHeader(src_path, bin_path, ignorefilename, pxds_out, print_pxd, output_format, generate_pxd):
    """ Checks a set of doxygen xml file against a set of pxd header files

    For each C++ class found in the doxygen XML files, it tries to identify the
    corresponding pxd file. If a pxd file exists, it checks whether

    i)   all public functions, enums and attributes are wrapped in Python
    ii)  all void return types are correct in Python (these are not checked at
         compile time)
    iii) all fields of an enum are accessible from Python

    If it finds a method missing, the script suggests an addition and if a
    whole class is missing, the script writes suggestion .pxd file to a
    specified location (pxds_out).

    The output format can either be in text form (human readable) or in xml
    form which will try to overwrite the cdash Test.xml file to proivide an
    output to cdash. Please only specify xml output if in your binary
    you have executed "ctest -D Nightly" or similar.

    TODO also look at ./doc/doxygen/doxygen-error.log ?

    Make sure to build the doxygen xmls first with 
    $ make doc_xml

    """

    xml_output_path = os.path.join(bin_path, "doc", "xml_output")
    xml_files = glob.glob(xml_output_path + "/*.xml")
    print "Found %s doxygen xml files" % (len(xml_files))
    if len(xml_files) == 0:
        raise Exception(
            "No doxygen files found in directory:\n%s,\n" % xml_output_path
            + "Please make sure you build the doxygen xmls (make dox_xml)\n"
            + "and that you specified the correct directory."
        )

    print "Creating pxd file map"
    pxd_file_matching = create_pxd_file_map(src_path)
    cnt = Counter()
    cnt.total = len(xml_files)
    ignorefile = IgnoreFile()
    if len(ignorefilename) > 0:
        ignorefile.load(ignorefilename)

    def pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd):
        if print_pxd:
            print ""
            print pxd_text
        if len(pxds_out) > 0 and pxd_text is not None:
            fname = os.path.join(pxds_out, "%s.pxd" % comp_name.split("::")[-1])
            with open(fname, "w") as f:
                f.write(pxd_text)

    testresults = TestResultHandler()
    for class_cntr, f in enumerate(xml_files):
        if len(generate_pxd) > 0:
            if f.find(generate_pxd) == -1:
                continue
        dfile = DoxygenXMLFile(f)
        res = dfile.parse_doxygen()
        if dfile.parsing_error:
            # e.g. <computeroutput><bold></computeroutput>
            cnt.skipped_could_not_parse += 1
            msg = "Skip:: No-parse :: could not parse file %s with error %s" % (f, dfile.parsing_error_message)
            tres = TestResult(False, msg, name="%s_test" % f)
            testresults.append([tres])
            continue
        elif os.path.basename(f) == "index.xml":
            # Skip the index
            continue
        compound = res.get_compounddef()
        comp_name = compound.get_compoundname()
        if not (comp_name.startswith("OpenMS") or comp_name.startswith("OpenSwath")):
            # Continue without checking or generating a testreport
            continue
        if ignorefile.isNameIgnored(comp_name):
            msg = "Skip:: Ignored :: Class %s (file %s)" % (comp_name, f)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_ignored += 1
            continue
        if compound.prot != "public":
            msg = "Skip:: Protected :: Compound %s is not public, skip" % (comp_name)
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_protected += 1
            continue
        file_location = dfile.getCompoundFileLocation()
        if file_location is None:
            msg = "Skip:: No-data :: there is no source file for %s" % f
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            testresults.append([tres])
            cnt.skipped_no_location += 1
            continue
        openms_file = OpenMSSourceFile(file_location)
        maintainer = openms_file.getMaintainer()
        if dfile.isEmpty(True):
            msg = (
                "Skip:: No-data :: File is empty (no section definitions found or only definitions found) in file %s"
                % f
            )
            tres = TestResult(True, msg, log_level=10, name="%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_sections += 1
            continue
        if file_location in pxd_file_matching:
            pxdfiles = pxd_file_matching[file_location]
        else:
            msg = "Skip:: No-pxd :: No pxd file exists for Class %s (File %s) %s" % (comp_name, file_location, f)
            tres = TestResult(False, msg, name="Missing_%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_pxd_file += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue
        try:
            pxd_class = PXDFile.parse_multiple_files(pxdfiles, comp_name)
            pxdfile = pxd_class.pxdfile
        except PXDFileParseError as e:
            # TODO specific exception
            msg = "Skip:: No-pxd :: " + e.message + "for %s (in pxd file %s)" % (comp_name, pxdfiles)
            tres = TestResult(False, msg, name="Missing_%s_test" % comp_name)
            tres.maintainer = maintainer
            testresults.append([tres])
            cnt.skipped_no_pxd_match += 1
            pxd_text = dfile.get_pxd_from_class(dfile, file_location, xml_output_path)
            pxd_text_printout(pxd_text, pxds_out, comp_name, print_pxd)
            continue

        cnt.parsed += 1
        # Loop through all methods
        classtestresults = []
        for method_cntr, mdef in enumerate(dfile.iterMemberDef()):

            if mdef.get_name() in ignorefile.getIgnoredMethods(comp_name):
                msg = "Ignore member function/attribute : %s %s %s " % (mdef.kind, mdef.prot, mdef.name)
                tres = TestResult(True, msg, log_level=10)
            else:
                tres = handle_member_definition(mdef, pxd_class, cnt)

            testname = "%s_%s::%s" % (comp_name, method_cntr, mdef.name)
            testname = testname.replace("::", "_")
            testname = re.sub("[^a-zA-Z0-9_]+", "", testname)
            tres.comp_name = comp_name
            tres.file_location = file_location
            tres.pxdfile = pxdfile
            tres.maintainer = maintainer
            tres.name = testname
            classtestresults.append(tres)

        testresults.append(classtestresults)

    ###################################
    #   Output
    ###################################
    if output_format in ["text", "text-verbose", "text-quiet"]:
        for classtestresults in testresults:
            if len(classtestresults) > 1:
                t = classtestresults[0]
                lenfailed = len([t for t in classtestresults if not t.isPassed()])
                if lenfailed > 0:
                    print "== Test results for element %s - from Cpp file %s with maintainer %s and corresponding pxd file %s" % (
                        t.comp_name,
                        t.file_location,
                        t.maintainer,
                        t.pxdfile,
                    )
            for tres in classtestresults:
                if not tres.isPassed():
                    print tres.message
                elif tres.log_level >= 10 and output_format in ["text", "text-verbose"]:
                    print tres.message
                elif tres.log_level >= 0 and output_format in ["text-verbose"]:
                    print tres.message

    elif output_format == "xml":

        # check if all files required to report in CDash are present
        tag_file = os.path.join(bin_path, "Testing", "TAG")
        try:
            # read the first line of tagfile (TAG) -> if it does not exist,
            # an IOError is thrown
            with open(tag_file) as f:
                ctestReportingPath = f.readline().strip()
                ctestReportingPath = os.path.join(bin_path, "Testing", ctestReportingPath)
                if not os.path.exists(ctestReportingPath):
                    raise Exception("Missing directory at %s" % (ctestReportingPath))
        except IOError:
            raise Exception("Missing nightly test information at %s" % (tag_file))

        template_path = os.path.join(ctestReportingPath, "Test.xml")
        testresults.to_cdash_xml(template_path, template_path)

    else:
        raise Exception("Unknown output format %s" % output_format)

    cnt.print_stats()
    cnt.print_skipping_reason()