Пример #1
0
 def preprocess(
         cls,
         capture: Capture,
         expected_frames_pattern: list_of(Value)
 ) -> (list_of(Conversation), list_of(Frame)):
     """
     Pre-process and filter the frames of the capture into test case related
     conversations. This has to be implemented into the protocol's common
     test case class.
     This method depends on the protocol features, so it cannot be
     implemented in a generic way.
     """
     raise NotImplementedError()
Пример #2
0
    def import_test_cases(self, testcases: optional(list_of(str)) = None) -> list:
        """
        Imports test cases classes from TESTCASES_DIR

        :param testcases: The wanted test cases as a list of string

        :return: List of test cases class in the same order than the param list

        :raises FileNotFoundError: If no test case was found

        .. note::
            Assumptions are the following:
                - Test cases are defined inside a .py file, each file contains only one test case
                - All test cases must be named td_*
                - All test cases are contained into ttproto/[env]/testcases
                - Filenames corresponds to the TC id in lower case
                - Class names corresponds to the TC id
        """

        # The return values
        tc_fetched = []

        # If no TCs provided, fetch all the test cases found
        if not testcases:
            tc_fetched = self.__get_testcases_from_pathname(EVERY_TC_WILDCARD)

        # If testcases list are provided, fetch those
        else:
            # For every test case given
            for test_case_name in testcases:
                tc_name_query = test_case_name.lower() + TC_FILE_EXTENSION
                tc_fetched += self.__get_testcases_from_pathname(tc_name_query)

        # Return the test cases classes
        return tc_fetched
Пример #3
0
    def get_dissection(
        self,
        protocol: optional(is_protocol) = None,
    ) -> list_of(OrderedDict):
        """
        Function to get dissection of a capture as a list of frames represented as strings

        :param protocol: Protocol class for filtering purposes
        :type protocol: type
        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: A list of Frame represented as API's dict form
        :rtype: [OrderedDict]

        """
        # log.debug('Starting dissection.')
        # Check the protocol is one entered
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # Then return the list of dictionary frame representation
        return [frame.dict() for frame in fs]
Пример #4
0
    def dissect(
            self,
            protocol: optional(is_protocol) = None) -> list_of(OrderedDict):
        """
        The dissect function to dissect a pcap file into list of frames

        :param protocol: Protocol class for filtering purposes
        :type protocol: type
        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: A list of Frame represented as API's dict form
        :rtype: [OrderedDict]
        """
        # log.debug('Starting dissection.')
        # Check the protocol is one entered
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        # For speeding up the process
        with Data.disable_name_resolution():

            # Get the list of frames
            frames = self.__capture.frames

            # Filter the frames for the selected protocol
            if protocol is not None:
                frames, _ = Frame.filter_frames(frames, protocol)

        # Then return the list of dictionary frame representation
        return [frame.dict() for frame in frames]
Пример #5
0
    def get_implemented_protocols(cls) -> list_of(type):
        """
        Allow to get the implemented protocols

        :return: Implemented protocols
        :rtype: [type]
        """
        logging.warning(
            "Deprecation warning in favour of package's get_dissectable_protocols method!"
        )
        # Singleton pattern
        if cls.__implemented_protocols is None:
            # Just directly get the PacketValue and InetPacketValue subclasses
            cls.__implemented_protocols = []
            # cls.__implemented_protocols += PacketValue.__subclasses__()
            # cls.__implemented_protocols += InetPacketValue.__subclasses__()

            # NOTE: This may ben needed if we change the protocol getter system
            add_subclass(cls.__implemented_protocols, PacketValue)

            # Remove the InetPacketValue class
            cls.__implemented_protocols.remove(InetPacketValue)

        # Return the singleton value
        return cls.__implemented_protocols
Пример #6
0
def get_test_steps(tc: str) -> list_of(OrderedDict):
    """
    Get an OrderedDict representing the different steps of a test case

    :param tc: The id of the test case
    :type tc: str

    :return: The steps of a TC as a list of OrderedDict
    :rtype: [OrderedDict]
    """

    # The return list of OrderedDict
    steps = []

    # Get the test case informations
    raw_tcs = Analyzer('tat_coap').get_implemented_testcases([tc], True)
    assert len(raw_tcs) == 1

    # Parse the documentation with yaml reader and get it as dictionnary
    doc_reader = YamlReader(raw_tcs[0][3], raw_text=True)
    doc_dict = doc_reader.as_dict

    # For every step, put its informations inside the steps dict
    for step_id, step in enumerate(doc_dict[tc]['seq']):
        step_dict = OrderedDict()
        step_dict['_type'] = 'step'
        step_dict['step_id'] = step_id
        step_dict['step_type'], step_dict['step_info'] = step.popitem()
        steps.append(step_dict)

    # Return the step list of this tc
    return steps
Пример #7
0
    def get_implemented_testcases(
            self,
            testcases: optional(list_of(str)) = None,
            verbose: bool = False
    ) -> list_of((str, str, str, str)):
        """
        Get more informations about the test cases

        :param testcases: A list of test cases to get their informations
        :param verbose: True if we want more informations about the TC
        :type testcase_id: optional([str])
        :type verbose: bool

        :raises FileNotFoundError: If one of the test case is not found

        :return: List of descriptions of test cases composed of:
                    - tc_identifier
                    - tc_objective
                    - tc_sourcecode
                    - tc_doc
        :rtype: [(str, str, str, str)]
        """

        # The return value
        ret = []

        # Get the tc classes
        tc_classes = self.import_test_cases(testcases)

        # Add the infos of each of them to the return value
        for tc in tc_classes:

            # If verbose is asked, we provide the source code and doc too
            source_code = ''
            source_doc = ''
            if verbose:
                source_code = inspect.getsource(tc)
                source_doc = inspect.getdoc(tc)

            # Add the tuple to the return value
            ret.append(
                (tc.__name__, tc.get_test_purpose(), source_code, source_doc)
            )

        # Return the list of tuples
        return ret
Пример #8
0
    def get_nodes_identification_templates(cls) -> list_of(Node):
        """
        Get the nodes of this test case. This has to be be implemented into
        each test cases class.

        :return: The nodes of this TC
        :rtype: [Node]
        """
        raise NotImplementedError()
Пример #9
0
    def get_stimulis(cls) -> list_of(Value):
        """
        Get the stimulis of this test case. This has to be be implemented into
        each test cases class.

        :return: The stimulis of this TC
        :rtype: [Value]
        """
        raise NotImplementedError()
Пример #10
0
    def summary(self,
                protocol: optional(is_protocol) = None) -> list_of((int, str)):
        """
        The summaries function to get the summary of frames

        :param protocol: Protocol class for filtering purposes
        :type protocol: type

        :Example:

        from ttproto.core.lib.all import Ieee802154

        for s in dissector.summary(protocol = Ieee802154):

            print(s)

        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: Basic informations about frames like the underlying example
        :rtype: [(int, str)]

        :Example:

            [
                (13, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38515] GET /test'),

                (14, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38515] 2.05 Content'),

                (21, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38516] PUT /test'),

                (22, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38516] 2.04 Changed')]
            ]

        .. note:: With the protocol option we can filter the response
        """

        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # Return list of frames summary
        return [frame.summary() for frame in fs]
Пример #11
0
    def summary(self,
                protocol: optional(is_protocol) = None) -> list_of((int, str)):
        """
        The summaries function to get the summary of frames

        :param protocol: Protocol class for filtering purposes
        :type protocol: type

        :Example:

        from ttproto.core.lib.all import Ieee802154

        for s in dissector.summary(protocol = Ieee802154):

            print(s)

        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: Basic informations about frames like the underlying example
        :rtype: [(int, str)]

        :Example:

            [
                (13, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38515] GET /test'),

                (14, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38515] 2.05 Content'),

                (21, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38516] PUT /test'),

                (22, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38516] 2.04 Changed')]
            ]

        .. todo:: Filter uninteresting frames ? (to decrease the load)
        .. note:: With the protocol option we can filter
        """

        # Check the protocol
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        # Disable the name resolution in order to improve performances
        with Data.disable_name_resolution():

            # Get the frames from the capture
            frames = self.__capture.frames

            # Filter the frames for the selected protocol
            if protocol is not None:
                frames, _ = Frame.filter_frames(frames, protocol)

        # Then give the summary of every frames
        return [frame.summary() for frame in frames]
Пример #12
0
    def filter_frames(
            cls, frames: list_of(this_class), protocol: is_protocol
    ) -> (list_of(this_class), list_of(this_class)):
        """
        Allow to filter frames on a protocol

        :param frames: The frames to filter
        :param protocol:  Protocol class for filtering purposes
        :type frames: [Frame]
        :type protocol: type

        :raises TypeError: If protocol is not a protocol class
                           or if the list contains a non Frame object

        :return: A tuple containing the filtered frames and the ignored ones
        :rtype: ([Frame], [Frame])
        """

        # The return list
        filtered_frames = []
        ignored_frames = []

        # Check the protocol is one entered
        if not is_protocol(protocol):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        # Remove all frames which doesn't include this protocol
        for frame in frames:

            # If an element of the list isn't a Frame
            if not isinstance(frame, Frame):
                raise TypeError('Parameter frames contains a non Frame object')

            # If the protocol is contained into this frame
            if protocol in frame:
                filtered_frames.append(frame)
            else:
                ignored_frames.append(frame)

        # Return the newly created list
        return filtered_frames, ignored_frames
Пример #13
0
def get_dissectable_protocols() -> list_of(type):
    """
    Return list of the implemented protocols (PacketValue classes)
    :return: Implemented protocols
    :rtype: [type]
    """
    # Just directly get the PacketValue and InetPacketValue subclasses
    implemented_protocols = []
    add_subclass(implemented_protocols, PacketValue)
    # Remove the InetPacketValue class
    implemented_protocols.remove(InetPacketValue)
    return implemented_protocols
Пример #14
0
    def get_dissection_simple_format(
        self,
        protocol: optional(is_protocol) = None,
    ) -> list_of(str):
        """
        Function to get dissection of a capture as a list of frames represented as strings

        :param protocol: Protocol class for filtering purposes
        :type protocol: type
        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: A list of Frame represented as plain non-structured text
        :rtype: [str]

        """
        # log.debug('Starting dissection.')
        # Check the protocol is one entered
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # fixme modify Message class from ttproto.data structure so I can get text display wihtout this patch
        class WritableObj(object):
            def __init__(self, text=''):
                self.val = text

            def __str__(self):
                return self.val

            def write(self, text):
                self.val += text

        frame_dissection_list = []

        for f in fs:
            text_output = WritableObj()
            f.message.display(output=text_output)
            frame_dissection_list.append(str(text_output))

        # Then return the list of frames,each as a simple text dissection
        return frame_dissection_list
Пример #15
0
    def __init__(self, nodes: list_of(Node)):
        """
        Function to initialize a Conversation object with its nodes

        :param nodes: The communicating nodes
        :type nodes: [Node]

        :raises ValueError: If not enough nodes received (less than 2)
        """

        # If not enough nodes
        if len(nodes) < 2:
            raise ValueError(
                'At least 2 nodes are required but %d received'
                %
                len(nodes)
            )

        # The node dictionary
        self._nodes = {}
        for node in nodes:
            self._nodes[node.name] = node.value
Пример #16
0
    def run_test_case(self) -> (
            str,
            list_of(int),
            str,
            list_of((str, str)),
            list_of((type, Exception, is_traceback))
    ):
        """
        Run the test case

        :return: A tuple with the informations about the test results which are
                 - The verdict as a string
                 - The list of the result important frames
                 - A string with the log of the test case
                 - A list of all the partial verdicts and their messages
                 - A list of typles representing the exceptions that occurred
        :rtype: (str, [int], str,[(str,str)], [(type, Exception, traceback)])
        """
        # Next line is before for 6lowpan TC, where nodes_identification_templates
        # is not generic.
        TestCase.get_nodes_identification_templates = self.get_nodes_identification_templates

        # Pre-process / filter conversations corresponding to the TC

        self._conversations, self._ignored = self.preprocess(
            capture=self._capture,
            expected_frames_pattern=self.get_stimulis()
        )

        # print("----conversations----")
        # print(self._conversations)
        # print("----ignored----")
        # print(self._ignored)

        if self._conversations == [[]] or self._conversations == []:
            self.set_verdict(
                'inconclusive',
                'Capture doesnt match expected pattern: \n\tgot %s, \n\texpected %s' %
                (str(self._capture.frames), str(self.get_stimulis()))
            )

        else:
            # Run the test case for every conversations
            for conv in self._conversations:
                if logger.getEffectiveLevel() == logging.DEBUG:
                    for frame in conv: logger.debug(frame)
                try:
                    # Get an iterator on the current conversation frames
                    # and its list of nodes
                    self._iter = iter(conv)
                    self._nodes = conv.nodes
                    self.next()

                    # Run the test case
                    self.run()

                except self.Stop:
                    # Ignore this testcase result if the first frame gives an
                    # inconclusive verdict
                    if all((
                                self._verdict.get_value() == 'inconclusive',
                                self._frame == conv[0]
                    )):
                        self.set_verdict('none', 'no match')

                except Exception as e:
                    # Get the execution information, it's a tuple with
                    #     - The type of the exception being handled
                    #     - The exception instance
                    #     - The traceback object
                    _exception_type, _exception_value, _exception_traceback = sys.exc_info()

                    logger.error(e)
                    traceback.print_exc(file=sys.stdout)

                    # Add those exception information to the list
                    self._exceptions.append((
                        _exception_type,
                        _exception_value,
                        _exception_traceback
                    ))

                    # Put the verdict and log the exception
                    self.set_verdict('error', 'unhandled exception')
                    self.log(_exception_value)

        # Return the results
        return (
            self._verdict.get_value(),
            self._failed_frames,
            self._text,
            self._verdict.get_traceback(),
            self._exceptions,
        )
Пример #17
0
    def analyse(
            self,
            filename: str,
            tc_id: str
    ) -> (str, str, list_of(int), str, list_of((str, str)), list_of((type, Exception, is_traceback))):
        """
        Analyse a dump file associated to a test case

        :param filename: The name of the file to analyse
        :param tc_id: The unique id of the test case to confront the given file
        :type filename: str
        :type tc_id: str

        :return: A tuple with the information about the analysis results:
                 - The id of the test case
                 - The verdict as a string
                 - The list of the result important frames
                 - A string with logs
                 - A list of all the partial verdicts
                 - A list of tuples representing the exceptions that occurred
        :rtype: (str, str, [int], str,[(str, str)], [(type, Exception, traceback)])

        :raises FileNotFoundError: If the test env of the tc is not found
        :raises ReaderError: If the capture didn't manage to read and decode
        :raises ObsoleteTestCase: If the test case if obsolete

        .. example::
            ('TD_COAP_CORE_03', 'fail', [21, 22], [('fail', "some comment"),('fail', "some other comment")] , 'verdict description', '')

        .. note::
            Allows multiple occurrences of executions of the testcase, returns as verdict:
                - fail: if at least one on the occurrences failed
                - inconclusive: if all occurrences returned a inconclusive verdict
                - pass: all occurrences are inconclusive or at least one is PASS and
                        the rest is inconclusive
        """

        # Get the test case class
        test_case_class = self.import_test_cases([tc_id])
        assert len(test_case_class) == 1
        test_case_class = test_case_class[0]

        # Disable name resolution for performance improvements
        with Data.disable_name_resolution():
            # Get the capture from the file
            capture = Capture(filename)
            # Initialize the TC with the list of conversations
            test_case = test_case_class(capture)
            verdict, rev_frames, log, partial_verdicts, exceps = test_case.run_test_case()

            # print('##### capture')
            # print(capture)
            # print('#####')
            #
            # # Here we execute the test case and return the result
            #
            # print('##### Verdict given')
            # print(verdict)
            # print('#####')
            # print('##### Review frames')
            # print(rev_frames)
            # print('#####')
            # print('##### Text')
            # print(log, partial_verdicts)
            # print('#####')
            # print('##### Exceptions')
            # print(exceptions)
            # print('#####')

            return tc_id, verdict, rev_frames, log, partial_verdicts, exceps
Пример #18
0
    def preprocess(
        cls, capture: Capture, expected_frames_pattern: list_of(Value)
    ) -> (list_of(Conversation), list_of(Frame)):
        """
        Preprocess and filter the frames of the capture into test case related
        conversations.

        :param Capture: The capture which will be filtered/preprocessed
        :return:
        """

        protocol = SixlowpanTestCase.get_protocol()
        nodes = TestCase.get_nodes_identification_templates()
        conversations = []
        ignored = []

        # TODO what happens if no protocol declared on the test case?
        if not nodes or len(nodes) < 2:
            raise ValueError(
                'Expected at leaset two nodes declaration from the test case')
        if not protocol:
            raise ValueError(
                'Expected a protocol under test declaration from the test case'
            )
        # If there is no stimuli at all
        if not expected_frames_pattern or len(expected_frames_pattern) == 0:
            raise NoStimuliFoundForTestcase(
                'Expected stimuli declaration from the test case')

        # Get the frames filtered on the protocol
        frames, ignored = Frame.filter_frames(capture.frames, protocol)

        # Get a counter of the current stimuli
        sti_count = 0
        current_conversation = None
        nb_stimulis = len(expected_frames_pattern)
        for frame in frames:

            # If the frame matches a stimuli
            if expected_frames_pattern[sti_count].match(frame[protocol]):

                # If it's the first stimuli
                if sti_count == 0:

                    # If there is already a conversation pending, save it
                    if current_conversation:
                        self._conversations.append(current_conversation)

                    # Get the nodes as a list of nodes
                    # TODO already done at begining. why isnde the iteeration?
                    # nodes = testcase.get_nodes_identification_templates()

                    # And create the new one
                    current_conversation = Conversation(nodes)

                # If intermediate stimulis, just increment the counter
                sti_count = (sti_count + 1) % nb_stimulis

            # If there is a current_conversation, put the frame into it
            if current_conversation:
                current_conversation.append(frame)

            # If no conversation pending
            else:
                ignored.append(frame)

        # At the end, if there is a current conversation pending, close it
        if current_conversation:

            # If not all stimulis were consumed
            if sti_count != 0:
                raise FilterError(
                    'Not all stimulis were consumed, %d left and next one should have been %s'
                    % (nb_stimulis - sti_count, stimulis[sti_count]))

            # Close the current conversation by adding it to list
            conversations.append(current_conversation)

        return conversations, ignored