<commit_before>"""
Utilities to aid unit testing L{eliot} and code that uses it.
"""

from __future__ import unicode_literals

from collections import namedtuple
from functools import wraps

from ._output import MemoryLogger


def issuperset(a, b):
    """
    Use L{assertContainsFields} instead.

    @type a: C{dict}

    @type b: C{dict}

    @return: Boolean indicating whether C{a} has all key/value pairs that C{b}
        does.
    """
    aItems = a.items()
    return all(pair in aItems for pair in b.items())


def assertContainsFields(test, message, fields):
    """
    Assert that the given message contains the given fields.

    @param test: L{unittest.TestCase} being run.

    @param message: C{dict}, the message we are checking.

    @param fields: C{dict}, the fields we expect the message to have.

    @raises AssertionError: If the message doesn't contain the fields.
    """
    messageSubset = dict([(key, value) for key, value in message.items()
                          if key in fields])
    test.assertEqual(messageSubset, fields)



class LoggedAction(namedtuple(
        "LoggedAction", "startMessage endMessage children")):
    """
    An action whose start and finish messages have been logged.

    @ivar startMessage: A C{dict}, the start message contents. Also
        available as C{start_message}.

    @ivar endMessage: A C{dict}, the end message contents (in both success and
        failure cases). Also available as C{end_message}.

    @ivar children: A C{list} of direct child L{LoggedMessage} and
        L{LoggedAction} instances.
    """
    @property
    def start_message(self):
        return self.startMessage


    @property
    def end_message(self):
        return self.endMessage


    @classmethod
    def fromMessages(klass, uuid, level, messages):
        """
        Given a task uuid and level (identifying an action) and a list of
        dictionaries, create a L{LoggedAction}.

        All child messages and actions will be added as L{LoggedAction} or
        L{LoggedMessage} children. Note that some descendant messages may be
        missing if you end up logging to two or more different ILogger
        providers.

        @param uuid: The uuid of the task (C{unicode}).

        @param level: The action level, e.g. C{"/1/2/"}.

        @param messages: A list of message C{dict}s.

        @return: L{LoggedAction} constructed from start and finish messages for
            this specific action.

        @raises: L{ValueError} if one or both of the action's messages cannot be
            found.
        """
        startMessage = None
        endMessage = None
        children = []
        for message in messages:
            if message["task_uuid"] != uuid:
                # Different task altogether:
                continue

            if message["task_level"] == level:
                status = message.get("action_status")
                if status == "started":
                    startMessage = message
                elif status in ("succeeded", "failed"):
                    endMessage = message
                else:
                    # Presumably a message in this action:
                    children.append(LoggedMessage(message))
            elif "action_type" in message:
                messageLevel = message["task_level"]
                # If parent level is /1/, /2/ is a sibling, /1/2/ is a direct
                # child and /1/2/1/ is a grandchild. We only want direct
                # children.
                if (messageLevel.startswith(level) and
                    messageLevel[len(level):].count("/") == 1 and
                    message["action_status"] == "started"):
                    # Passing in all messages is inefficient, but probably fine
                    # given we'll only testing with small numbers of messages.
                    child = klass.fromMessages(uuid, messageLevel, messages)
                    children.append(child)
        if startMessage is None or endMessage is None:
            raise ValueError(uuid, level)
        return klass(startMessage, endMessage, children)


    # PEP 8 variant:
    from_messages = fromMessages


    @classmethod
    def ofType(klass, messages, actionType):
        """
        Find all L{LoggedAction} of the specified type.

        @param messages: A list of message C{dict}s.

        @param actionType: A L{eliot.ActionType}, the type of the actions to
            find.

        @return: A C{list} of L{LoggedAction}.
        """
        result = []
        for message in messages:
            if (message.get("action_type") == actionType.action_type and
                message["action_status"] == "started"):
                result.append(klass.fromMessages(message["task_uuid"],
                                                 message["task_level"],
                                                 messages))<commit_msg>Update testing infrastructure to new task_level information.<commit_after>"""
Utilities to aid unit testing L{eliot} and code that uses it.
"""

from __future__ import unicode_literals

from collections import namedtuple
from functools import wraps

from ._output import MemoryLogger


def issuperset(a, b):
    """
    Use L{assertContainsFields} instead.

    @type a: C{dict}

    @type b: C{dict}

    @return: Boolean indicating whether C{a} has all key/value pairs that C{b}
        does.
    """
    aItems = a.items()
    return all(pair in aItems for pair in b.items())


def assertContainsFields(test, message, fields):
    """
    Assert that the given message contains the given fields.

    @param test: L{unittest.TestCase} being run.

    @param message: C{dict}, the message we are checking.

    @param fields: C{dict}, the fields we expect the message to have.

    @raises AssertionError: If the message doesn't contain the fields.
    """
    messageSubset = dict([(key, value) for key, value in message.items()
                          if key in fields])
    test.assertEqual(messageSubset, fields)



class LoggedAction(namedtuple(
        "LoggedAction", "startMessage endMessage children")):
    """
    An action whose start and finish messages have been logged.

    @ivar startMessage: A C{dict}, the start message contents. Also
        available as C{start_message}.

    @ivar endMessage: A C{dict}, the end message contents (in both success and
        failure cases). Also available as C{end_message}.

    @ivar children: A C{list} of direct child L{LoggedMessage} and
        L{LoggedAction} instances.
    """
    @property
    def start_message(self):
        return self.startMessage


    @property
    def end_message(self):
        return self.endMessage


    @classmethod
    def fromMessages(klass, uuid, level, messages):
        """
        Given a task uuid and level (identifying an action) and a list of
        dictionaries, create a L{LoggedAction}.

        All child messages and actions will be added as L{LoggedAction} or
        L{LoggedMessage} children. Note that some descendant messages may be
        missing if you end up logging to two or more different ILogger
        providers.

        @param uuid: The uuid of the task (C{unicode}).

        @param level: The C{task_level} of action
            start messages is C{"/1/2/1"} then the action level would be
            C{"/1/2/"}.

        @param messages: A list of message C{dict}s.

        @return: L{LoggedAction} constructed from start and finish messages for
            this specific action.

        @raises: L{ValueError} if one or both of the action's messages cannot be
            found.
        """
        startMessage = None
        endMessage = None
        children = []
        levelPrefix = level.split("/")[:-1]

        for message in messages:
            if message["task_uuid"] != uuid:
                # Different task altogether:
                continue

            messageLevel = message["task_level"].split("/")

            if messageLevel[:-1] == levelPrefix:
                status = message.get("action_status")
                if status == "started":
                    startMessage = message
                elif status in ("succeeded", "failed"):
                    endMessage = message
                else:
                    # Presumably a message in this action:
                    children.append(LoggedMessage(message))
            elif (len(messageLevel) == len(levelPrefix) + 2 and
                  messageLevel[:-2] == levelPrefix and
                  messageLevel[-1] == "1"):
                # If start message level is /1, /1/2/1 implies first
                # message of a direct child.
                child = klass.fromMessages(
                    uuid, message["task_level"], messages)
                children.append(child)
        if startMessage is None or endMessage is None:
            raise ValueError(uuid, level)
        return klass(startMessage, endMessage, children)


    # PEP 8 variant:
    from_messages = fromMessages


    @classmethod
    def ofType(klass, messages, actionType):
        """
        Find all L{LoggedAction} of the specified type.

        @param messages: A list of message C{dict}s.

        @param actionType: A L{eliot.ActionType}, the type of the actions to
            find.

        @return: A C{list} of L{LoggedAction}.
        """
        result = []
        for message in messages:
            if (message.get("action_type") == actionType.action_type and
                message["action_status"] == "started"):
                result.append(klass.fromMessages(message["task_uuid"],
                                                 message["task_level"],
                                                 messages))
{'fraction_non_alphanumeric': 0.08674254075669025, 'fraction_numerical': 0.0027683789603199014, 'mean_word_length': 2.0198142414860683, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7101811', 'n_tokens_mistral': 2562, 'n_tokens_neox': 2459, 'n_words': 916}