<commit_before># Copyright (C) 2019-20 <NAME>ität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.

"""
GNRC-related shell interactions

Defines GNRC-related shell command interactions
"""

import re

from riotctrl.shell import ShellInteraction, ShellInteractionParser


# ==== Parsers ====

class GNRCICMPv6EchoParser(ShellInteractionParser):
    def __init__(self):
        self.c_reply = re.compile(r"\d+ bytes from "
                                  r"(?P<source>[0-9a-f:]+(%\S+)?): "
                                  r"icmp_seq=(?P<seq>\d+) ttl=(?P<ttl>\d+)"
                                  r"( rssi=(?P<rssi>-?\d+) dBm)?"
                                  r"( time=(?P<rtt>\d+.\d+) ms)?"
                                  r"(?P<dup> \(DUP\))?")
        self.c_stats = re.compile(r"(?P<tx>\d+) packets transmitted, "
                                  r"(?P<rx>\d+) packets received, "
                                  r"((?P<dup>\d+) duplicates, )?"
                                  r"(?P<packet_loss>\d+)% packet loss")
        self.c_rtts = re.compile(r"round-trip min/avg/max = (?P<min>\d+.\d+)/"
                                 r"(?P<avg>\d+.\d+)/(?P<max>\d+.\d+) ms")

    @staticmethod
    def _add_reply(res, reply):
        reply["seq"] = int(reply["seq"])
        reply["ttl"] = int(reply["ttl"])
        if reply.get("rtt") is not None:
            reply["rtt"] = float(reply["rtt"])
        else:
            reply.pop("rtt", None)
        if reply.get("dup"):
            reply["dup"] = True
        else:
            reply.pop("dup", None)
        if reply.get("rssi") is not None:
            reply["rssi"] = int(reply["rssi"])
        else:
            reply.pop("rssi", None)
        if "replies" in res:
            res["replies"].append(reply)
        else:
            res["replies"] = [reply]

    @staticmethod
    def _set_stats(res, stats):
        stats["packet_loss"] = int(stats["packet_loss"])
        stats["rx"] = int(stats["rx"])
        stats["tx"] = int(stats["tx"])
        if stats.get("dup") is not None:
            stats["dup"] = int(stats["dup"])
        else:
            stats.pop("dup")
        res["stats"] = stats

    @staticmethod
    def _set_rtts(res, rtts):
        rtts["min"] = float(rtts["min"])
        rtts["avg"] = float(rtts["avg"])
        rtts["max"] = float(rtts["max"])
        res["rtts"] = rtts

    def parse(self, cmd_output):
        """
        Parses output of GNRCIPv6NIB::nib_neigh_show()

        >>> parser = GNRCICMPv6EchoParser()
        >>> res = parser.parse(
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=0 ttl=64 rssi=-34 dBm time=8.839 ms\\n"
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=1 ttl=64 rssi=-34 dBm time=6.925 ms\\n"
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=2 ttl=64 rssi=-34 dBm time=7.885 ms\\n"
        ...     "--- fe80::385d:f965:106b:1114 PING statistics ---\\n"
        ...     "3 packets transmitted, 3 packets received, 0% packet loss\\n"
        ...     "round-trip min/avg/max = 6.925/7.883/8.839 ms\\n")
        >>> sorted(res)
        ['replies', 'rtts', 'stats']
        >>> len(res["replies"])
        3
        >>> sorted(res["replies"][0])
        ['rssi', 'rtt', 'seq', 'source', 'ttl']<commit_msg>riotctrl_shell.gnrc: fix documentation for GNRCICMPv6EchoParser.parse()<commit_after># Copyright (C) 2019-20 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.

"""
GNRC-related shell interactions

Defines GNRC-related shell command interactions
"""

import re

from riotctrl.shell import ShellInteraction, ShellInteractionParser


# ==== Parsers ====

class GNRCICMPv6EchoParser(ShellInteractionParser):
    def __init__(self):
        self.c_reply = re.compile(r"\d+ bytes from "
                                  r"(?P<source>[0-9a-f:]+(%\S+)?): "
                                  r"icmp_seq=(?P<seq>\d+) ttl=(?P<ttl>\d+)"
                                  r"( rssi=(?P<rssi>-?\d+) dBm)?"
                                  r"( time=(?P<rtt>\d+.\d+) ms)?"
                                  r"(?P<dup> \(DUP\))?")
        self.c_stats = re.compile(r"(?P<tx>\d+) packets transmitted, "
                                  r"(?P<rx>\d+) packets received, "
                                  r"((?P<dup>\d+) duplicates, )?"
                                  r"(?P<packet_loss>\d+)% packet loss")
        self.c_rtts = re.compile(r"round-trip min/avg/max = (?P<min>\d+.\d+)/"
                                 r"(?P<avg>\d+.\d+)/(?P<max>\d+.\d+) ms")

    @staticmethod
    def _add_reply(res, reply):
        reply["seq"] = int(reply["seq"])
        reply["ttl"] = int(reply["ttl"])
        if reply.get("rtt") is not None:
            reply["rtt"] = float(reply["rtt"])
        else:
            reply.pop("rtt", None)
        if reply.get("dup"):
            reply["dup"] = True
        else:
            reply.pop("dup", None)
        if reply.get("rssi") is not None:
            reply["rssi"] = int(reply["rssi"])
        else:
            reply.pop("rssi", None)
        if "replies" in res:
            res["replies"].append(reply)
        else:
            res["replies"] = [reply]

    @staticmethod
    def _set_stats(res, stats):
        stats["packet_loss"] = int(stats["packet_loss"])
        stats["rx"] = int(stats["rx"])
        stats["tx"] = int(stats["tx"])
        if stats.get("dup") is not None:
            stats["dup"] = int(stats["dup"])
        else:
            stats.pop("dup")
        res["stats"] = stats

    @staticmethod
    def _set_rtts(res, rtts):
        rtts["min"] = float(rtts["min"])
        rtts["avg"] = float(rtts["avg"])
        rtts["max"] = float(rtts["max"])
        res["rtts"] = rtts

    def parse(self, cmd_output):
        """
        Parses output of GNRCICMPv6Echo::ping6()

        >>> parser = GNRCICMPv6EchoParser()
        >>> res = parser.parse(
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=0 ttl=64 rssi=-34 dBm time=8.839 ms\\n"
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=1 ttl=64 rssi=-34 dBm time=6.925 ms\\n"
        ...     "12 bytes from fe80::385d:f965:106b:1114%6: "
        ...         "icmp_seq=2 ttl=64 rssi=-34 dBm time=7.885 ms\\n"
        ...     "--- fe80::385d:f965:106b:1114 PING statistics ---\\n"
        ...     "3 packets transmitted, 3 packets received, 0% packet loss\\n"
        ...     "round-trip min/avg/max = 6.925/7.883/8.839 ms\\n")
        >>> sorted(res)
        ['replies', 'rtts', 'stats']
        >>> len(res["replies"])
        3
        >>> sorted(res["replies"][0])
        ['rssi', 'rtt', 'seq', 'source', 'ttl']
{'fraction_non_alphanumeric': 0.18180525941719972, 'fraction_numerical': 0.036105188343994316, 'mean_word_length': 1.7965023847376789, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3384140', 'n_tokens_mistral': 2608, 'n_tokens_neox': 2395, 'n_words': 472}