Merge two unicode Robot Framework reports so that report 'b' is merged
    into report 'a'. This merge may not be complete and may be is lossy. Still,
    note that the original single test reports will remain untouched.

def merge(a, b):
    """Merge two unicode Robot Framework reports so that report 'b' is merged
    into report 'a'. This merge may not be complete and may be is lossy. Still,
    note that the original single test reports will remain untouched.

    """
    global loglevel

    # Iterate throughout the currently merged node set
    for child in b.iterchildren():

        # Merge suites
        if child.tag == 'suite':
            source = child.get('source')
            suites = a.xpath('suite[@source="%s"]' % source)
            # When matching suite is found, merge
            if suites:
                merge(suites[0], child)
            # When no matching suite is found, append and fix ids
            else:
                suites = a.xpath('suite')
                child_id = child.get('id')
                parent_id = a.get('id', '')
                if parent_id:
                    suite_id = '%s-s%s' % (parent_id, str(len(suites) + 1))
                else:
                    suite_id = 's%s' % str(len(suites) + 1)
                for node in child.xpath('//*[contains(@id, "%s")]' % child_id):
                    node.set('id', re.sub('^%s' % child_id, suite_id,
                                          node.get('id')))
                a.append(child)
                # Gather separate top-level suites into a single top-level
                # suite
            if a.tag == 'robot' and a.xpath('suite[@source]'):
                for merge_root in a.xpath('suite[not(@source)]'):
                    mergeable = etree.Element('robot')
                    for suite in a.xpath('suite[@source]'):
                        mergeable.append(suite)
                    merge(merge_root, mergeable)
        # Merge keywords
        elif child.tag == 'kw':
            name = child.get('name')
            type_ = child.get('type')
            keywords = a.xpath('kw[@name="%s" and @type="%s"]' % (name, type_))
            # When matching keyword is found, merge
            if len(keywords) == 1:
                merge(keywords[0], child)
            # When multiple matching keywords is found, merge with position
            elif len(keywords) > 1:
                child_keywords = child.getparent().xpath(
                    'kw[@name="%s" and @type="%s"]' % (name, type_))
                child_position = child_keywords.index(child)
                merge(keywords[min(child_position, len(keywords) - 1)], child)
            # When no matching suite is found, append
            else:
                a.append(child)

        # Merge (append) tests
        elif child.tag == 'test':
            tests = a.xpath('test')
            child.set('id', '%s-t%s' % (a.get('id'), str(len(tests) + 1)))
            a.append(child)

        # Merge (append) statuses
        elif child.tag == 'status':
            a.append(child)

        # Merge statistics
        elif child.tag == 'statistics':
            statistics = a.xpath('statistics')
            # When no statistics are found, append to root
            if not statistics:
                a.append(child)
            # When statistics are found, merge matching or append
            else:
                for grandchild in child.xpath('total'):
                    totals = a.xpath('statistics/total')
                    if totals:
                        merge(totals[0], grandchild)
                    else:
                        statistics.append(child)
                for grandchild in child.xpath('suite'):
                    suites = a.xpath('statistics/suite')
                    if suites:
                        merge(suites[0], grandchild)
                    else:
                        statistics.append(child)

        # Merge individual statistics
        elif child.tag == 'stat':
            stats = a.xpath('stat[text() = "%s"]' % child.text)
            if stats:
                stats[0].set('fail', str(int(stats[0].get('fail'))
                                         + int(child.get('fail'))))
                stats[0].set('pass', str(int(stats[0].get('pass'))
                                         + int(child.get('pass'))))
            else:
                suites = a.xpath('//suite[@name="%s"]' % child.get('name'))
                if suites:
                    child.set('id', suites[0].get('id'))
                a.append(child)

        # Merge errors
        elif child.tag == 'errors':
            errors = a.xpath('errors')
            # Filter by loglevel
            for grandchild in tuple(child.iterchildren()):
                try:
                    level = int(getattr(logging, grandchild.get('level')))
                except (TypeError, AttributeError, ValueError):
                    level = 0
                if level < loglevel:
                    child.remove(grandchild)
            # When no errors are found, append to root
            if not errors:
                a.append(child)
            # When errors are found, append the children
            else:
                for grandchild in child.iterchildren():
                    errors[0].append(grandchild)