messages.py 34.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: utf-8 -*-

"""
This module provides the API message formats used in F-Interop.

The idea is to be able to have an
- organized and centralized way of dealing with the big amount of messages formats used in the platform;
- to be able to import (or just copy/paste) these messages formats from any component in the F-Interop platform,
- re-use this also for the integration testing;
- to have version control the messages e.g. messages_testcase_start API v1 and API v2;
- to have a direct way of exporting this as doc.

Usage:
------
15
>>> from messages import * # doctest: +SKIP
16
>>> m = MsgTestCaseSkip()
17
18
>>> m
MsgTestCaseSkip(_type = testcoordination.testcase.skip, _api_version = 0.1.2, testcase_id = TD_COAP_CORE_02_v01, )
19
20
>>> m.routing_key
'control.testcoordination'
21
22
>>> m.message_id # doctest: +SKIP
'802012eb-24e3-45c4-9dcc-dc293c584f63'
23
24
>>> m.testcase_id
'TD_COAP_CORE_02_v01'
25

26
27
28
# also we can modify some of the fields (rewrite the default ones)
>>> m = MsgTestCaseSkip(testcase_id = 'TD_COAP_CORE_03_v01')
>>> m
29
MsgTestCaseSkip(_type = testcoordination.testcase.skip, _api_version = 0.1.2, testcase_id = TD_COAP_CORE_03_v01, )
30
31
>>> m.testcase_id
'TD_COAP_CORE_03_v01'
32

33
34
# and even export the message in json format (for example for sending the message though the amqp event bus)
>>> m.to_json()
35
'{"_type": "testcoordination.testcase.skip", "_api_version": "0.1.2", "testcase_id": "TD_COAP_CORE_03_v01"}'
Federico Sismondi's avatar
add doc    
Federico Sismondi committed
36

37
# We can use the Message class to import json into Message objects:
Federico Sismondi's avatar
add doc    
Federico Sismondi committed
38
39
>>> m=MsgTestSuiteStart()
>>> m.to_json()
40
'{"_type": "testcoordination.testsuite.start", "_api_version": "0.1.2"}'
Federico Sismondi's avatar
add doc    
Federico Sismondi committed
41
42
43
>>> json_message = m.to_json()
>>> obj=Message.from_json(json_message)
>>> type(obj)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<class '__main__.MsgTestSuiteStart'>

# We can use the library for generating error responses to the requests:
# the request:
>>> m = MsgSniffingStart()
>>>
# the error reply (note that we pass the message of the request to build the reply):
>>> err = MsgErrorReply(m)
>>> err
MsgErrorReply(_type = sniffing.start, _api_version = 0.1.2, ok = False, error_code = Some error code TBD, error_message = Some error message TBD, )
>>> m.reply_to
'control.sniffing.service.reply'
>>> err.routing_key
'control.sniffing.service.reply'

>>> m.correlation_id # doctest: +SKIP
'360b0f67-4455-43e3-a00f-eca91f2e84da'
>>> err.correlation_id # doctest: +SKIP
'360b0f67-4455-43e3-a00f-eca91f2e84da'
Federico Sismondi's avatar
add doc    
Federico Sismondi committed
63

64
65
66
67
68
69
70
"""

from collections import OrderedDict
import json
import uuid
import logging

71
API_VERSION = '0.1.11'
72

73
74
75
76
77
78
79
80
81
82
83

# TODO use metaclasses instead?

class NonCompliantMessageFormatError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


84
85
class Message:
    def __init__(self, **kwargs):
86
87
88
        global API_VERSION

        # hard copy the message template
89
        self._msg_data = {k: v for k, v in self._msg_data_template.items()}
90

91
92
        # init properties
        self._properties = dict(
93
94
                content_type='application/json',
                message_id=str(uuid.uuid4()),
95
96
        )

97
98
99
100
101
        try:
            if self.routing_key.endswith('.service'):
                self._properties['reply_to'] = '%s.%s' % (self.routing_key, 'reply')
                self._properties['correlation_id'] = self._properties['message_id']
        except AttributeError:
102
            pass
103

104
        # rewrite default data fields with the passed args
105
106
107
108
109
110
111
112
113
        self._msg_data.update(kwargs)

        # add API's version
        self._msg_data['_api_version'] = API_VERSION

        # add values as objects attributes
        for key in self._msg_data:
            setattr(self, key, self._msg_data[key])

114
115
116
117
        # add props as objects attributes
        for key in self._properties:
            setattr(self, key, self._properties[key])

118
119
    def to_dict(self) -> OrderedDict:
        resp = OrderedDict()
120
121
        # let's use sorted so API returns items inside always in the same order
        for field in sorted(self._msg_data.keys()):
122
            resp[field] = getattr(self, field)
123

124
        # for readability
125
126
127
128
129
        if 'ok' in resp:
            resp.move_to_end('ok', False)
        if '_api_version' in resp:
            resp.move_to_end('_api_version', False)
        if '_type' in resp:
130
            resp.move_to_end('_type', False)
131
132
133
134
135
136

        return resp

    def to_json(self):
        return json.dumps(self.to_dict())

137
    def get_properties(self) -> dict:
138
139
140
141
        resp = OrderedDict()
        for field in self._properties:
            resp[field] = getattr(self, field)
        return resp
142

143
    def __str__(self):
144
145
        str = ' - ' * 20 + '\n'
        str += 'Message routing key: %s' % self.routing_key
146
        str += '\n'
147
        str += 'Message properties: %s' % json.dumps(self.get_properties())
148
        str += '\n'
149
        str += 'Message body: %s' % self.to_json()
150
151
152
        str += '\n' + ' - ' * 20
        return str

153
154
155
156
157
    def update_properties(self, **kwargs):
        for key, value in kwargs.items():
            if key in self._properties:
                setattr(self, key, value)

158
159
    @classmethod
    def from_json(cls, body):
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
        """
        :param body: json string or string encoded as utf-8
        :return:  Message object generated from the body
        :raises NonCompliantMessageFormatError: If the message cannot be build from the provided json
        """

        if type(body) is str:
            message_dict = json.loads(body)
        # Note: pika re-encodes json.dumps strings as utf-8 for some reason, the following line undoes this
        elif type(body) is bytes:
            message_dict = json.loads(body.decode('utf-8'))
        else:
            raise NonCompliantMessageFormatError('Not a Json')

        # check fist if it's a response
        if 'ok' in message_dict:
            # cannot build a complete reply message just from the json representation
177
            return
178
179
180
181
182

        message_type = message_dict['_type']
        if message_type in message_types_dict:
            return message_types_dict[message_type](**message_dict)
        else:
183
            raise NonCompliantMessageFormatError('Cannot load json message: %s' % str(body))
184

185
    def __repr__(self):
186
        ret = '%s(' % self.__class__.__name__
187
        for key, value in self.to_dict().items():
188
            ret += '%s = %s, ' % (key, value)
189
190
191
        ret += ')'
        return ret

192

193
194
195
196
197
class MsgReply(Message):
    """
    Auxiliary class which creates replies messages with fields based on the request.
    Routing key, corr_id and _type are generated based on the request message
    """
198

199
200
201
202
203
204
205
206
    def __init__(self, request_message, **kwargs):
        assert request_message

        self.routing_key = request_message.routing_key + ".reply"

        # if not data template, then let's build one for a reply
        # (possible when creating a MsgReply directly and not by using subclass)
        if not hasattr(self, '_msg_data_template'):
207
208
209
            self._msg_data_template = {
                '_type': request_message._type,
                'ok': True,
210
211
212
213
            }

        super().__init__(**kwargs)

214
        # overwrite correlation id template and attribute
215
216
217
218
219
220
221
222
223
224
225
226
        self._properties['correlation_id'] = request_message.correlation_id
        self.correlation_id = request_message.correlation_id


class MsgErrorReply(MsgReply):
    """
    F-Interop conventions:
        - if event is a service request then the routing keys is control.someFunctionality.service
        also, its reply will be control.someFunctionality.service.reply
        - reply.correlation_id = request.correlation_id

    """
227

Federico Sismondi's avatar
Federico Sismondi committed
228
229
    def __init__(self, request_message, **kwargs):
        assert request_message
230
        # msg_data_template doesnt include _type cause this class is generic, we can only get this at init from request
Federico Sismondi's avatar
Federico Sismondi committed
231
232
233
        # so, let's copy the _type from request and let the MsgReply handle the rest of the fields
        self._msg_data_template['_type'] = request_message._type
        super().__init__(request_message, **kwargs)
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

    _msg_data_template = {
        'ok': False,
        'error_message': 'Some error message TBD',
        'error_code': 'Some error code TBD'
    }


###### SESSION MESSAGES ######

class MsgSessionTerminate(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI, (or Orchestrator?) -> Testing Tool
    Testing tool should stop all it's processes gracefully.
    """
    routing_key = 'control.session.terminate'

    _msg_data_template = {
        '_type': 'session.terminate',
    }

256

257
258
259
260
261
262
263
264
###### TEST COORDINATION MESSAGES ######

class MsgTestSuiteStart(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI -> Testing Tool
    """

265
    routing_key = "control.testcoordination"
266
267
268
269
270
271

    _msg_data_template = {
        '_type': "testcoordination.testsuite.start",
    }


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
class MsgTestCaseReady(Message):
    """
    Testing Tool MUST-implement notification.
    Testing Tool -> GUI

    Used to indicate to the GUI (or automated-iut) which is the next test case to be executed.
    This message is normally followed by a MsgTestCaseStart (from GUI-> Testing Tool)
    """

    routing_key = 'control.testcoordination'

    _msg_data_template = {
        "_type": "testcoordination.testcase.ready",
        "message": "Next test case to be executed is TD_COAP_CORE_01_v01",
        "testcase_id": "TD_COAP_CORE_01_v01",
        "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_01_v01",
        "objective": "Perform GET transaction(CON mode)",
        "state": None
    }


293
294
295
296
297
298
class MsgTestCaseStart(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI -> Testing Tool
    Message used for indicating the testing tool to start the test case (the one previously selected)
    """
299
    routing_key = "control.testcoordination"
300
301
302
303
304
305

    _msg_data_template = {
        '_type': "testcoordination.testcase.start",
    }


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
class MsgTestCaseConfiguration(Message):
    """
    Testing Tool MUST-implement notification
    Testing Tool -> GUI
    Messages used to indicate GUI which configuration to use.
    """
    routing_key = "control.testcoordination"

    _msg_data_template = {
        "_type": "testcoordination.testcase.configuration",
        "configuration_id": "COAP_CFG_01_v01",
        "node": "coap_server",
        "message":
            ["CoAP servers running service at [bbbb::2]:5683",
             "CoAP servers are requested to offer the following resources",
             ["/test", "Default test resource", "Should not exceed 64bytes"],
             ["/seg1/seg2/seg3", "Long path ressource", "Should not exceed 64bytes"],
             ["/query", "Ressource accepting query parameters", "Should not exceed 64bytes"],
             ["/separate",
              "Ressource which cannot be served immediately and which cannot be acknowledged in a piggy-backed way",
              "Should not exceed 64bytes"],
             ["/large", "Large resource (>1024 bytes)", "shall not exceed 2048bytes"],
             ["/large_update", "Large resource that can be updated using PUT method (>1024 bytes)",
              "shall not exceed 2048bytes"],
             ["/large_create", "Large resource that can be  created using POST method (>1024 bytes)",
              "shall not exceed 2048bytes"],
             ["/obs", "Observable resource which changes every 5 seconds", "shall not exceed 2048bytes"],
             ["/.well-known/core", "CoRE Link Format", "may require usage of Block options"]
             ]
    }


338
339
340
341
342
343
344
class MsgTestCaseStop(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI -> Testing Tool
    Message used for indicating the testing tool to stop the test case (the one running)
    """

345
    routing_key = 'control.testcoordination'
346
347
348
349
350
351
352
353
354
355
356
357

    _msg_data_template = {
        '_type': 'testcoordination.testcase.stop',
    }


class MsgTestCaseRestart(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI -> Testing Tool
    """

358
    routing_key = 'control.testcoordination'
359
360
361
362
363

    _msg_data_template = {
        '_type': 'testcoordination.testcase.restart',
    }

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

class MsgStepExecute(Message):
    """
    Testing Tool MUST-implement notification.
    Testing Tool -> GUI

    Used to indicate to the GUI (or automated-iut) which is the step to be executed by the user (or automated-IUT)
    """

    routing_key = 'control.testcoordination'

    _msg_data_template = {
        "_type": "testcoordination.step.execute",
        "message": "Next test step to be executed is TD_COAP_CORE_01_v01_step_01",
        "step_id": "TD_COAP_CORE_01_v01_step_01",
        "step_type": "stimuli",
        "step_info": [
            "Client is requested to send a GET request with",
            "Type = 0(CON)",
            "Code = 1(GET)"
        ],
        "step_state": "executing",
        "node": "coap_client",
        "node_execution_mode": "user_assisted"
    }

390
391
392
393
394
395
396

class MsgStimuliExecuted(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI (or automated-IUT)-> Testing Tool
    """

397
    routing_key = 'control.testcoordination'
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412

    _msg_data_template = {
        '_type': 'testcoordination.step.stimuli.executed',
    }


class MsgCheckResponse(Message):
    """
    Testing Tools'internal call.
    In the context of IUT to IUT test execution, this message is used for indicating that the previously executed
    messages (stimuli message and its reply) CHECK or comply to what is described in the Test Description.
    Testing tools' coordinator -> Testing Tool's analyzer (TAT)
    Not used in CoAP testing Tool (analysis of traces is done post mortem)
    """

413
414
    routing_key = 'control.testcoordination'

415
416
    _msg_data_template = {
        '_type': 'testcoordination.step.check.response',
417
418
        'partial_verdict': 'pass',
        'description': 'TAT says: step complies (checks) with specification'
419
420
421
422
423
424
425
426
427
428
429
    }


class MsgVerifyResponse(Message):
    """
    Testing Tool MUST-implement API endpoint
    Message provided by user declaring if the IUT VERIFY the step previously executed as described in the Test
    Description.
    GUI (or automated-IUT)-> Testing Tool
    """

430
    routing_key = 'control.testcoordination'
431
432
433
434
435
436
437

    _msg_data_template = {
        '_type': 'testcoordination.step.verify.response',
        'verify_response': True,
        'response_type': 'bool'
    }

438

439
440
441
442
443
444
445
class MsgTestCaseFinish(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI (or automated-IUT)-> Testing Tool
    Not used in CoAP Testing Tool (test coordinator deduces it automatically by using the testcase's step sequence)
    """

446
    routing_key = 'control.testcoordination'
447
448
449
450
451

    _msg_data_template = {
        '_type': 'testcoordination.testcase.finish',
    }

452

453
454
455
456
class MsgTestCaseSkip(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI (or automated-IUT)-> Testing Tool
457
458

    - testcase_id (optional) : if not provided then current tc is skipped
459
460
    """

461
    routing_key = 'control.testcoordination'
462
463
464
465
466
467
468
469
470
471
472
473
474

    _msg_data_template = {
        '_type': 'testcoordination.testcase.skip',
        'testcase_id': 'TD_COAP_CORE_02_v01',
    }


class MsgTestCaseSelect(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI (or automated-IUT)-> Testing Tool
    """

475
    routing_key = 'control.testcoordination'
476
477
478
479
480
481

    _msg_data_template = {
        '_type': 'testcoordination.testcase.select',
        'testcase_id': 'TD_COAP_CORE_03_v01',
    }

482

483
484
485
486
487
488
class MsgTestSuiteAbort(Message):
    """
    Testing Tool MUST-implement API endpoint
    GUI (or automated-IUT)-> Testing Tool
    """

489
    routing_key = 'control.testcoordination'
490
491
492
493
494

    _msg_data_template = {
        '_type': 'testcoordination.testsuite.abort',
    }

495

496
497
class MsgTestSuiteGetStatus(Message):
    """
498
    Testing Tool SHOULD-implement API endpoint
499
500
501
502
503
504
    Describes current state of the test suite.
    Format for the response not standardised.

    GUI -> Testing Tool
    """

505
    routing_key = 'control.testcoordination.service'
506
507
508
509
510

    _msg_data_template = {
        '_type': 'testcoordination.testsuite.getstatus',
    }

511

512
class MsgTestSuiteGetStatusReply(MsgReply):
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
    """
    Testing Tool SHOULD-implement API endpoint
    Describes current state of the test suite.
    Format for the response not standardised.

    Testing Tool -> GUI

    """

    routing_key = 'control.testcoordination.service.reply'

    _msg_data_template = {
        '_type': 'testcoordination.testsuite.getstatus.reply',
        'ok': True,
        "status": {
            "current_tc":
                {
                    "state": "executing",
                    "testcase_id": "TD_COAP_CORE_01_v01"
                },
            "current_step":
                {
                    "step_id": "TD_COAP_CORE_01_v01_step_01",
                    "step_type": "stimuli",
                    "step_info":
                        ["Client is requested to send a GET request with", "Type = 0(CON)", "Code = 1(GET)"],
                    "step_state": "executing",
                    "node": "coap_client",
                    "node_execution_mode": "user_assisted"
                }
        }
    }

546

547
548
class MsgTestSuiteGetTestCases(Message):
    """
549
    Testing Tool's MUST-implement API endpoint
550
551
552
553
    GUI -> Testing Tool
    GUI MUST implement
    """

554
    routing_key = 'control.testcoordination.service'
555
556
557
558
559

    _msg_data_template = {
        '_type': 'testcoordination.testsuite.gettestcases',
    }

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636

class MsgTestSuiteGetTestCasesReply(MsgReply):
    """
    Testing Tool's MUST-implement API endpoint
    Testing Tool -> GUI
    """

    routing_key = 'control.testcoordination.service.reply'

    _msg_data_template = {
        '_type': 'testcoordination.testsuite.gettestcases.reply',
        'ok': True,
        "tc_list": [
            {
                "testcase_id": "TD_COAP_CORE_01_v01",
                "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_01_v01",
                "objective": "Perform GET transaction(CON mode)",
                "state": None
            },
            {
                "testcase_id": "TD_COAP_CORE_02_v01",
                "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_02_v01",
                "objective": "Perform DELETE transaction (CON mode)",
                "state": None
            },
            {
                "testcase_id": "TD_COAP_CORE_03_v01",
                "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_03_v01",
                "objective": "Perform PUT transaction (CON mode)",
                "state": None
            }
        ]
    }


class MsgTestCaseVerdict(Message):
    """
    Testing Tool MUST-implement notification.
    Testing Tool -> GUI

    Used to indicate to the GUI (or automated-iut) which is the final verdict of the testcase.
    """

    routing_key = 'control.testcoordination'

    _msg_data_template = {
        "_type": "testcoordination.testcase.verdict",
        "verdict": "pass",
        "description": "No interoperability error was detected,",
        "partial_verdicts": [
            ["TD_COAP_CORE_01_v01_step_02", None, "CHECK postponed", ""],
            ["TD_COAP_CORE_01_v01_step_03", None, "CHECK postponed", ""],
            ["TD_COAP_CORE_01_v01_step_04", "pass",
             "VERIFY step: User informed that the information was displayed correclty on his/her IUT", ""],
            ["CHECK_1_post_mortem_analysis", "pass",
             "<Frame   3: [bbbb::1 -> bbbb::2] CoAP [CON 43211] GET /test> Match: CoAP(type=0, code=1)"],
            ["CHECK_2_post_mortem_analysis", "pass",
             "<Frame   4: [bbbb::2 -> bbbb::1] CoAP [ACK 43211] 2.05 Content > Match: CoAP(code=69, mid=0xa8cb, tok=b'', pl=Not(b''))"],
            ["CHECK_3_post_mortem_analysis", "pass",
             "<Frame   4: [bbbb::2 -> bbbb::1] CoAP [ACK 43211] 2.05 Content > Match: CoAP(opt=Opt(CoAPOptionContentFormat()))"]],
        "testcase_id": "TD_COAP_CORE_01_v01",
        "testcase_ref": "http://f-interop.paris.inria.fr/tests/TD_COAP_CORE_01_v01",
        "objective": "Perform GET transaction(CON mode)", "state": "finished"
    }


class MsgTestSuiteReport(Message):
    """
    Testing Tool MUST-implement notification.
    Testing Tool -> GUI

    Used to indicate to the GUI (or automated-iut) the final results of the test session.
    """

    routing_key = 'control.testcoordination'

    _msg_data_template = {
637
        "_type": "testcoordination.testsuite.report",
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
        "TD_COAP_CORE_01_v01":
            {
                "verdict": "pass",
                "description": "No interoperability error was detected,",
                "partial_verdicts":
                    [
                        ["TD_COAP_CORE_01_v01_step_02", None, "CHECK postponed", ""],
                        ["TD_COAP_CORE_01_v01_step_03", None, "CHECK postponed", ""],
                        ["TD_COAP_CORE_01_v01_step_04", "pass",
                         "VERIFY step: User informed that the information was displayed correclty on his/her IUT", ""],
                        ["CHECK_1_post_mortem_analysis", "pass",
                         "<Frame   3: [bbbb::1 -> bbbb::2] CoAP [CON 43211] GET /test> Match: CoAP(type=0, code=1)"],
                        ["CHECK_2_post_mortem_analysis", "pass",
                         "<Frame   4: [bbbb::2 -> bbbb::1] CoAP [ACK 43211] 2.05 Content > Match: CoAP(code=69, mid=0xa8cb, tok=b'', pl=Not(b''))"],
                        [
                            "CHECK_3_post_mortem_analysis",
                            "pass",
                            "<Frame   4: [bbbb::2 -> bbbb::1] CoAP [ACK 43211] 2.05 Content > Match: CoAP(opt=Opt(CoAPOptionContentFormat()))"]
                    ]
            },

        "TD_COAP_CORE_02_v01":
            {
                "verdict": "pass",
                "description": "No interoperability error was detected,",
                "partial_verdicts": [
                    ["TD_COAP_CORE_02_v01_step_02", None, "CHECK postponed", ""],
                    ["TD_COAP_CORE_02_v01_step_03", None, "CHECK postponed", ""],
                    ["TD_COAP_CORE_02_v01_step_04", "pass",
                     "VERIFY step: User informed that the information was displayed correclty on his/her IUT",
                     ""], ["CHECK_1_post_mortem_analysis", "pass",
                           "<Frame   3: [bbbb::1 -> bbbb::2] CoAP [CON 43213] DELETE /test> Match: CoAP(type=0, code=4)"],
                    ["CHECK_2_post_mortem_analysis", "pass",
                     "<Frame   4: [bbbb::2 -> bbbb::1] CoAP [ACK 43213] 2.02 Deleted > Match: CoAP(code=66, mid=0xa8cd, tok=b'')"]]
            }
    }

675

676
677
678
679
680
681
682
683
684
###### SNIFFING SERVICES REQUEST MESSAGES ######

class MsgSniffingStart(Message):
    """
    Testing Tools'internal call.
    Coordinator -> Sniffer
    Testing Tool SHOULD implement (design recommendation)
    """

685
    routing_key = 'control.sniffing.service'
686
687
688
689
690
691
692
693

    _msg_data_template = {
        '_type': 'sniffing.start',
        'capture_id': 'TD_COAP_CORE_01',
        'filter_if': 'tun0',
        'filter_proto': 'udp port 5683'
    }

694

695
696
697
698
699
700
701
702
703
704
705
706
707
708
class MsgSniffingStartReply(MsgReply):
    """
    Testing Tools'internal call.
    Sniffer -> Coordinator
    Testing Tool SHOULD implement (design recommendation)
    """

    routing_key = 'control.sniffing.service.reply'

    _msg_data_template = {
        '_type': 'sniffing.start.reply',
        'ok': True
    }

709

710
711
712
713
714
715
716
class MsgSniffingStop(Message):
    """
    Testing Tools'internal call.
    Coordinator -> Sniffer
    Testing Tool SHOULD implement (design recommendation)
    """

717
    routing_key = 'control.sniffing.service'
718
719
720
721
722

    _msg_data_template = {
        '_type': 'sniffing.stop',
    }

723

724
725
726
727
728
729
730
731
732
733
734
735
736
737
class MsgSniffingStoptReply(MsgReply):
    """
    Testing Tools'internal call.
    Sniffer -> Coordinator
    Testing Tool SHOULD implement (design recommendation)
    """

    routing_key = 'control.sniffing.service.reply'

    _msg_data_template = {
        '_type': 'sniffing.stop.reply',
        'ok': True
    }

738

739
740
741
742
743
744
745
class MsgSniffingGetCapture(Message):
    """
    Testing Tools'internal call.
    Coordinator -> Sniffer
    Testing Tool SHOULD implement (design recommendation)
    """

746
    routing_key = 'control.sniffing.service'
747
748
749
750
751
752
753

    _msg_data_template = {
        '_type': 'sniffing.getcapture',
        "capture_id": "TD_COAP_CORE_01",

    }

754

755
class MsgSniffingGetCaptureReply(MsgReply):
756
757
758
759
760
761
762
763
764
765
    routing_key = 'control.sniffing.service.reply'

    _msg_data_template = {
        '_type': 'sniffing.getcapture.reply',
        'ok': True,
        'file_enc': 'pcap_base64',
        'filename': 'TD_COAP_CORE_01.pcap',
        'value': '1MOyoQIABAAAAAAAAAAAAMgAAAAAAAAA',  # empty PCAP
    }

766

767
768
769
770
771
772
773
class MsgSniffingGetCaptureLast(Message):
    """
    Testing Tools'internal call.
    Coordinator -> Sniffer
    Testing Tool SHOULD implement (design recommendation)
    """

774
    routing_key = 'control.sniffing.service'
775
776
777
778

    _msg_data_template = {
        '_type': 'sniffing.getlastcapture',
    }
779

780

781
class MsgSniffingGetCaptureLastReply(MsgReply):
782
783
784
785
786
787
788
789
790
791
    routing_key = 'control.sniffing.service.reply'

    _msg_data_template = {
        '_type': 'sniffing.getlastcapture.reply',
        'ok': True,
        'file_enc': 'pcap_base64',
        'filename': 'TD_COAP_CORE_01.pcap',
        'value': '1MOyoQIABAAAAAAAAAAAAMgAAAAAAAAA',  # empty PCAP
    }

792
793

###### ANALYSIS MESSAGES ######
794

795
class MsgInteropTestCaseAnalyze(Message):
796
797
798
799
800
801
802
803
    """
    Testing Tools'internal call.
    Coordinator -> Analyzer
    Testing Tool SHOULD implement (design recommendation)
    """

    PCAP_empty_base64 = '1MOyoQIABAAAAAAAAAAAAMgAAAAAAAAA'

804
    routing_key = 'control.analysis.service'
805
806

    _msg_data_template = {
807
        '_type': 'analysis.interop.testcase.analyze',
808
        "testcase_id": "TD_COAP_CORE_01",
809
        "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_01_v01",
810
811
812
813
814
        "file_enc": "pcap_base64",
        "filename": "TD_COAP_CORE_01.pcap",
        "value": PCAP_empty_base64,
    }

815

816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
class MsgInteropTestCaseAnalyzeReply(MsgReply):
    """
    Testing Tools'internal call.
    Analyzer -> Coordinator
    Testing Tool SHOULD implement (design recommendation)

    The recommended structure for the partial_verdicts field is a list of partial verdicts with the following
    requirements:
     - each one of those elements of the list correspond to one CHECK or VERIFY steps of the test description
     - first value of the list MUST be a "pass", "fail", "inconclusive" or eventually "error" partial verdict (string)
     - the second value MUST be a string with a description of partial verdict (intended for the user)
     - more values elements MAY be added to the list

    """

    _msg_data_template = {
832
833
        '_type': 'analysis.interop.testcase.analyze.reply',
        'ok': True,
834
835
836
        'verdict': 'pass',
        'analysis_type': 'postmortem',
        'description': 'The test purpose has been verified without any fault detected',
837
838
        'review_frames': [],
        'token': '0lzzb_Bx30u8Gu-xkt1DFE1GmB4',
839
840
841
        "partial_verdicts": [
            [
                "pass", "<Frame   1: [127.0.0.1 -> 127.0.0.1] CoAP [CON 43521] GET /test> Match: CoAP(type=0, code=1)"
842
            ],
843
844
845
846
847

            [
                "pass",
                "<Frame   2: [127.0.0.1 -> 127.0.0.1] CoAP [ACK 43521] 2.05 Content > Match: CoAP(code=69, mid=0xaa01, \
                tok=b'b\\xda', pl=Not(b''))"
848
            ],
849
850
851
852
            [
                "pass",
                "<Frame   2: [127.0.0.1 -> 127.0.0.1] CoAP [ACK 43521] 2.05 Content > \
                Match: CoAP(opt=Opt(CoAPOptionContentFormat()))"
853
            ]
854
855
856
857
858
        ],
        "testcase_id": "TD_COAP_CORE_01",
        "testcase_ref": "http://doc.f-interop.eu/tests/TD_COAP_CORE_01_v01",
    }

859

860
861
###### DISSECTION MESSAGES ######

862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
class MsgDissectionDissectCapture(Message):
    """
    Testing Tools'internal call.
    Coordinator -> Dissector
    and
    Analyzer -> Dissector
    Testing Tool SHOULD implement (design recommendation)
    """

    PCAP_COAP_GET_OVER_TUN_INTERFACE_base64 = "1MOyoQIABAAAAAAAAAAAAMgAAABlAAAAqgl9WK8aBgA7AAAAOwAAAGADPxUAExFAu7s" \
                                              "AAAAAAAAAAAAAAAAAAbu7AAAAAAAAAAAAAAAAAALXvBYzABNZUEABcGO0dGVzdMECqg" \
                                              "l9WMcaBgCQAAAAkAAAAGAAAAAAaDr//oAAAAAAAAAAAAAAAAAAA7u7AAAAAAAAAAAAA" \
                                              "AAAAAGJAAcTAAAAALu7AAAAAAAAAAAAAAAAAAK7uwAAAAAAAAAAAAAAAAACBAgAAAAA" \
                                              "AABgAz8VABMRQLu7AAAAAAAAAAAAAAAAAAG7uwAAAAAAAAAAAAAAAAAC17wWMwATWVB" \
                                              "AAXBjtHRlc6oJfVjSGgYAOwAAADsAAABgAz8VABMRP7u7AAAAAAAAAAAAAAAAAAG7uw" \
                                              "AAAAAAAAAAAAAAAAAC17wWMwATWVBAAXBjtHRlc3TBAg=="

879
    routing_key = 'control.dissection.service'
880
881
882
883
884
885
886
887
888
889

    _msg_data_template = {
        '_type': 'dissection.dissectcapture',
        "file_enc": "pcap_base64",
        "filename": "TD_COAP_CORE_01.pcap",
        "value": PCAP_COAP_GET_OVER_TUN_INTERFACE_base64,
        "protocol_selection": 'coap',
    }


890
891
892
893
894
895
896
897
898
class MsgDissectionDissectCaptureReply(MsgReply):
    """
    Testing Tools'internal call.
    Dissector -> Coordinator
    and
    Dissector -> Analyzer
    Testing Tool SHOULD implement (design recommendation)
    """

Federico Sismondi's avatar
Federico Sismondi committed
899
    _frames_example = [
900
901
902
903
        {
            "_type": "frame",
            "id": 1,
            "timestamp": 1464858393.547275,
Federico Sismondi's avatar
Federico Sismondi committed
904
            "error": None,
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
            "protocol_stack": [
                {
                    "_type": "protocol",
                    "_protocol": "NullLoopback",
                    "AddressFamily": "2",
                    "ProtocolFamily": "0"
                },
                {
                    "_type": "protocol",
                    "_protocol": "IPv4",
                    "Version": "4",
                    "HeaderLength": "5",
                    "TypeOfService": "0x00",
                    "TotalLength": "41",
                    "Identification": "0x71ac",
                    "Reserved": "0",
                    "DontFragment": "0",
                    "MoreFragments": "0",
                    "FragmentOffset": "0",
                    "TimeToLive": "64",
                    "Protocol": "17",
                    "HeaderChecksum": "0x0000",
                    "SourceAddress": "127.0.0.1",
                    "DestinationAddress": "127.0.0.1",
                    "Options": "b''"
930
                }
Federico Sismondi's avatar
Federico Sismondi committed
931
932
933
            ]
        },
    ]
934
935

    _msg_data_template = {
936
937
938
939
        '_type': 'dissection.dissectcapture.reply',
        'ok': True,
        'token': '0lzzb_Bx30u8Gu-xkt1DFE1GmB4',
        'frames': _frames_example
940
941
942
943
944
945
946
947
948
949
950
951
952
953
    }


class MsgDissectionAutoDissect(Message):
    """
    Testing Tool's MUST-implement.
    Testing Tool -> GUI
    GUI MUST display this info during execution:
     - interop session
     - conformance session
     - performance ?
     - privacy?

    """
Federico Sismondi's avatar
Federico Sismondi committed
954
    routing_key = 'control.dissection.auto'
955

Federico Sismondi's avatar
Federico Sismondi committed
956
    _frames_example = MsgDissectionDissectCaptureReply._frames_example
957
958

    _msg_data_template = {
959
960
961
        '_type': 'dissection.autotriggered',
        'token': '0lzzb_Bx30u8Gu-xkt1DFE1GmB4',
        'frames': _frames_example
962
963
964
965
    }


message_types_dict = {
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
    "testcoordination.testsuite.start": MsgTestSuiteStart, # GUI -> TestingTool
    "testcoordination.testcase.ready": MsgTestCaseReady,  # TestingTool -> GUI
    "testcoordination.testcase.start": MsgTestCaseStart, # GUI -> TestingTool
    "testcoordination.step.execute": MsgStepExecute, # TestingTool -> GUI
    "testcoordination.testcase.configuration": MsgTestCaseConfiguration, # TestingTool -> GUI
    "testcoordination.testcase.stop": MsgTestCaseStop, # GUI -> TestingTool
    "testcoordination.testcase.restart": MsgTestCaseRestart, # GUI -> TestingTool
    "testcoordination.step.stimuli.executed": MsgStimuliExecuted, # GUI -> TestingTool
    "testcoordination.step.check.response": MsgCheckResponse, # GUI -> TestingTool
    "testcoordination.step.verify.response": MsgVerifyResponse, # GUI -> TestingTool
    "testcoordination.testcase.skip": MsgTestCaseSkip, # GUI -> TestingTool
    "testcoordination.testcase.select": MsgTestCaseSelect, # GUI -> TestingTool
    "testcoordination.testcase.finish": MsgTestCaseFinish, # GUI -> TestingTool
    "testcoordination.testcase.verdict": MsgTestCaseVerdict, # TestingTool -> GUI
    "testcoordination.testsuite.abort": MsgTestSuiteAbort, # GUI -> TestingTool
    "testcoordination.testsuite.getstatus": MsgTestSuiteGetStatus, # GUI -> TestingTool
    "testcoordination.testsuite.getstatus.reply": MsgTestSuiteGetStatusReply,# TestingTool -> GUI (reply)
    "testcoordination.testsuite.gettestcases": MsgTestSuiteGetTestCases,# GUI -> TestingTool
    "testcoordination.testsuite.gettestcases.reply": MsgTestSuiteGetTestCasesReply,# TestingTool -> GUI (reply)
    "testcoordination.testsuite.report" : MsgTestSuiteReport, # TestingTool -> GUI
    "sniffing.start": MsgSniffingStart, # Testing Tool Internal
    "sniffing.stop": MsgSniffingStop, # Testing Tool Internal
    "sniffing.getcapture": MsgSniffingGetCapture,  # Testing Tool Internal
    "sniffing.getlastcapture": MsgSniffingGetCaptureLast,  # Testing Tool Internal
    "analysis.interop.testcase.analyze": MsgInteropTestCaseAnalyze,  # Testing Tool Internal
    "analysis.interop.testcase.analyze.reply": MsgInteropTestCaseAnalyzeReply,  # Testing Tool Internal
    "dissection.dissectcapture": MsgDissectionDissectCapture,  # Testing Tool Internal
    "dissection.dissectcapture.reply": MsgDissectionDissectCaptureReply,  # Testing Tool Internal
    "session.terminate": MsgSessionTerminate, # GUI (or Orchestrator?) -> TestingTool
    "control.dissection.auto": MsgDissectionAutoDissect, # TestingTool -> GUI
996
997
998
999
1000
}

if __name__ == '__main__':
    # m1=MsgTestCaseStart()
    # print(json.dumps(m1.to_dict()))