From a051c12f2caa82e51e29225d26ba85e34689a1a1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Atli=20Gu=C3=B0mundsson?= Date: Tue, 25 Dec 2018 14:13:40 +0000 Subject: [PATCH] Added a generic dissector validator and some ASTERIX unit tests that use it. This patch extends the test suite with: * a way for tests to check if a given byte sequence dissects into an expected dissection result. Unit tests included: * ASTERIX I019 * ASTERIX I063 * ASTERIX I065 Change-Id: Ib168382ec15b0b610ff5913806120ba1bf1d1503 Reviewed-on: https://code.wireshark.org/review/31083 Petri-Dish: Peter Wu Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu --- CMakeLists.txt | 1 + test/suite_dissectors/__init__.py | 16 + test/suite_dissectors/dissectorstest.py | 119 ++ test/suite_dissectors/group_asterix.py | 1483 +++++++++++++++++++++++ 4 files changed, 1619 insertions(+) create mode 100644 test/suite_dissectors/__init__.py create mode 100644 test/suite_dissectors/dissectorstest.py create mode 100644 test/suite_dissectors/group_asterix.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 5975b78d68..9e4610e9c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3122,6 +3122,7 @@ set(_test_group_list suite_dfilter.group_tvb suite_dfilter.group_uint64 suite_dissection + suite_dissectors.group_asterix suite_fileformats suite_follow suite_io diff --git a/test/suite_dissectors/__init__.py b/test/suite_dissectors/__init__.py new file mode 100644 index 0000000000..79ccaf1221 --- /dev/null +++ b/test/suite_dissectors/__init__.py @@ -0,0 +1,16 @@ +# +# Copyright (C) 2013 by Gilbert Ramirez +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os.path +import unittest + +# Run by unittest.defaultTestLoader.discover in test.py + + +def load_tests(loader, standard_tests, pattern): + this_dir = os.path.dirname(__file__) + package_tests = loader.discover(start_dir=this_dir, pattern='group_*.py') + standard_tests.addTests(package_tests) + return standard_tests diff --git a/test/suite_dissectors/dissectorstest.py b/test/suite_dissectors/dissectorstest.py new file mode 100644 index 0000000000..7c799d8795 --- /dev/null +++ b/test/suite_dissectors/dissectorstest.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Wireshark dissector tests +# By Atli Guðmundsson +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# Standard modules +import inspect +import json + +# Wireshark modules +import fixtures +import subprocesstest + + +class _dissection_validator_real: + ''' + Collects a set of byte bundles, matching json objects and a protocol + name and verifies that a byte bundle converts into the matching json + object using the following execution chain: + + byte bundle -> text2pcap -> tshark -> json + + Note: The idea for this approach came about when it was realized that + calling text2pcap and tshark for each byte bundle resulted in + unacceptable overhead during execution of the unittests. + ''' + + def __init__(self, protocol, request, cmd_tshark, cmd_text2pcap): + self.dissection_list = [] + self.protocol = protocol + self.cmd_tshark = cmd_tshark + self.cmd_text2pcap = cmd_text2pcap + self.test_case = request.instance + + def add_dissection(self, byte_list, expected_result, line_no=None): + '''Adds a byte bundle and an expected result to the set of byte + bundles to verify. + + byte bundles must be iterable.''' + + hex_string = ' '.join('{:02x}'.format(ele) for ele in bytes(byte_list)) + + if line_no is None: + caller = inspect.getframeinfo(inspect.stack()[1][0]) + line_no = caller.lineno + + self.dissection_list.append((line_no, hex_string, expected_result)) + +# Uncomment the following lines to record in a text file all the dissector byte +# bundles, in the order they are presented: +# +# with open("full.txt", 'a') as f: +# f.write("0 {}\n".format(hex_string)) + +# Then use the following command to convert full.txt into a pcap file, +# replacing with the default port of your protocol: +# # text2pcap -u , full.txt out.pcap + + def check_dissections(self): + '''Processes and verifies all added byte bundles and their expected + results. At the end of processing the current set is emptied.''' + + text_file = self.test_case.filename_from_id('txt') + pcap_file = self.test_case.filename_from_id('pcap') + + # create our text file of hex encoded messages + with open(text_file, 'w') as f: + for line_no, hex_string, expected_result in self.dissection_list: + f.write("0 {}\n".format(hex_string)) + + # generate our pcap file by feeding the messages to text2pcap + self.test_case.runProcess(( + self.cmd_text2pcap, + '-u', '1234,1234', + text_file, pcap_file + )) + + # generate our dissection from our pcap file + tshark_proc = self.test_case.runProcess(( + self.cmd_tshark, + '-r', pcap_file, + '-T', 'json', + '-d', 'udp.port==1234,{}'.format(self.protocol), + '-J', self.protocol + )) + + dissections = json.loads(tshark_proc.stdout_str) + for (line_no, hex_string, expected_result), dissection in zip(self.dissection_list, dissections): + + # strip away everything except the protocol + result = dissection['_source']['layers'] + self.test_case.assertIn(self.protocol, result) + result = result[self.protocol] + + # verify that the dissection is as expected + self.test_case.assertEqual( + expected_result, + result, + "expected != result, while dissecting [{}] from line {}.".format(hex_string, line_no)) + + # cleanup for next test + self.dissection_list = [] + + +@fixtures.fixture +def dissection_validator(request, cmd_tshark, cmd_text2pcap): + + def generate_validator(protocol): + retval = _dissection_validator_real( + protocol, + request, + cmd_tshark, + cmd_text2pcap) + return retval + + return generate_validator diff --git a/test/suite_dissectors/group_asterix.py b/test/suite_dissectors/group_asterix.py new file mode 100644 index 0000000000..44e21178c7 --- /dev/null +++ b/test/suite_dissectors/group_asterix.py @@ -0,0 +1,1483 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Wireshark ASTERIX dissector tests +# By Atli Guðmundsson +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +'''ASTERIX dissector tests''' + +# Standard modules +import inspect + +# Wireshark modules +import fixtures +import subprocesstest +import suite_dissectors.dissectorstest + + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_asterix(subprocesstest.SubprocessTestCase): + + def test_for_asterix(self, dissection_validator): + '''Verifies that the asterix dissector is installed and accessible''' + + tester = dissection_validator('asterix') + tester.add_dissection( + [0x13, 0x00, 0x03], + { + "asterix.category": "19", + "asterix.length": "3" + } + ) + tester.check_dissections() + + +class _asterix_validator_real: + + def __init__(self, category, dissection_validator): + self.category = category + self.validator = dissection_validator("asterix") + + def add_dissection(self, byte_list, field, expected_message, line_no=None): + '''pre-wrap asterix category messages with proper asterix structure''' + + total_length = len(byte_list) + 3 + byte_list = [ + self.category, + (total_length // 256) % 256, + total_length % 256 + ] + byte_list + expected_result = { + "asterix.category": "{}".format(self.category), + "asterix.length": "{}".format(total_length), + "asterix.message": + { + "asterix.fspec": "", + field: expected_message + } + } + if line_no is None: + caller = inspect.getframeinfo(inspect.stack()[1][0]) + line_no = caller.lineno + self.validator.add_dissection(byte_list, expected_result, line_no) + + def check_dissections(self): + self.validator.check_dissections() + + +@fixtures.fixture +def asterix_validator(dissection_validator): + + def generate_asterix_validator(category): + retval = _asterix_validator_real(category, dissection_validator) + return retval + + return generate_asterix_validator + + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_category_019(subprocesstest.SubprocessTestCase): + ''' + Unittest case for ASTERIX Category 019 + + Online specification: + https://www.eurocontrol.int/publications/cat019-multilateration-system-status-messages-part-18 + + Part 18 : Category 019 (1.3) + Multilateration System + Status Messages + + Standard User Application Profile + + FRN Data Item Information Length + 1 I019/010 Data Source Identifier 2 + 2 I019/000 Message Type 1 + 3 I019/140 Time of Day 3 + 4 I019/550 System Status 1 + 5 I019/551 Tracking Processor Detailed Status 1 + 6 I019/552 Remote Sensor Detailed Status 1+ + 7 I019/553 Reference Transponder Detailed Status 1+ + FX - Field Extension Indicator - + 8 I019/600 Position of the MLT System Reference point 8 + 9 I019/610 Height of the MLT System Reference point 2 + 10 I019/620 WGS-84 Undulation 1 + 11 - Spare - + 12 - Spare - + 13 RE Reserved Expansion Field - + 14 SP Special Purpose Field - + FX - Field Extension Indicator - + ''' + + maxDiff = None + + def test_for_fields(self, asterix_validator): + '''verifies existence of all fields and their maximum value''' + + validator = asterix_validator(19) + + validator.add_dissection( + [0x80, 0xff, 0x00], + "asterix.019_010", + { + "asterix.SAC": "255", + "asterix.SIC": "0" + } + ) + validator.add_dissection( + [0x80, 0x00, 0xff], + "asterix.019_010", + { + "asterix.SAC": "0", + "asterix.SIC": "255" + } + ) + validator.add_dissection( + [0x40, 0x03], + "asterix.019_000", + { + "asterix.019_000_MT": "3" + } + ) + validator.add_dissection( + [0x20, 0xa8, 0xbf, 0xff], + "asterix.019_140", + { + "asterix.TOD": "86399.9921875" + } + ) + validator.add_dissection( + [0x10, 0xc0], + "asterix.019_550", + { + "asterix.019_550_NOGO": "3", + "asterix.019_550_OVL": "0", + "asterix.019_550_TSV": "0", + "asterix.019_550_TTF": "0" + } + ) + validator.add_dissection( + [0x10, 0x20], + "asterix.019_550", + { + "asterix.019_550_NOGO": "0", + "asterix.019_550_OVL": "1", + "asterix.019_550_TSV": "0", + "asterix.019_550_TTF": "0" + } + ) + validator.add_dissection( + [0x10, 0x10], + "asterix.019_550", + { + "asterix.019_550_NOGO": "0", + "asterix.019_550_OVL": "0", + "asterix.019_550_TSV": "1", + "asterix.019_550_TTF": "0" + } + ) + validator.add_dissection( + [0x10, 0x08], + "asterix.019_550", + { + "asterix.019_550_NOGO": "0", + "asterix.019_550_OVL": "0", + "asterix.019_550_TSV": "0", + "asterix.019_550_TTF": "1" + } + ) + validator.add_dissection( + [0x08, 0x80], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "1", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x40], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "1", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x20], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "1", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x10], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "1", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x08], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "1", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x04], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "1", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x02], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "1", + "asterix.019_551_SP4_GOOD": "0" + } + ) + validator.add_dissection( + [0x08, 0x01], + "asterix.019_551", + { + "asterix.019_551_SP1_EXEC": "0", + "asterix.019_551_SP1_GOOD": "0", + "asterix.019_551_SP2_EXEC": "0", + "asterix.019_551_SP2_GOOD": "0", + "asterix.019_551_SP3_EXEC": "0", + "asterix.019_551_SP3_GOOD": "0", + "asterix.019_551_SP4_EXEC": "0", + "asterix.019_551_SP4_GOOD": "1" + } + ) + validator.add_dissection( + [0x04, 0x00], + "asterix.019_552", + { + "asterix.counter": "0" + } + ) + validator.add_dissection( + [0x04, 0x01, 0xff, 0x00], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "255", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x40], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "1", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x20], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "1", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x10], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "1", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x08], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "1", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x04], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "1" + } + } + ) + validator.add_dissection( + [0x04, 0x03, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x0c], + "asterix.019_552", + { + "asterix.counter": "3", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "18", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "1", + "asterix.019_552_Transmitter_1090_MHz": "1", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "1" + }, + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "86", + "asterix.019_552_Receiver_1090_MHz": "1", + "asterix.019_552_Transmitter_1030_MHz": "1", + "asterix.019_552_Transmitter_1090_MHz": "1", + "asterix.019_552_RS_Status": "1", + "asterix.019_552_RS_Operational": "0" + }, + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "154", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "1", + "asterix.019_552_RS_Operational": "1" + } + } + ) + validator.add_dissection( + [0x02, 0xc0], + "asterix.019_553", + { + "asterix.019_553_Ref_Trans_1_Status": "3", + "asterix.019_553_Ref_Trans_2_Status": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x02, 0x0c], + "asterix.019_553", + { + "asterix.019_553_Ref_Trans_1_Status": "0", + "asterix.019_553_Ref_Trans_2_Status": "3", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x02, 0x01, 0x01, 0x0c], + "asterix.019_553", + { + "asterix.019_553_Ref_Trans_1_Status": "0", + "asterix.019_553_Ref_Trans_2_Status": "0", + "asterix.019_553_Ref_Trans_3_Status": "0", + "asterix.019_553_Ref_Trans_4_Status": "0", + "asterix.019_553_Ref_Trans_5_Status": "0", + "asterix.019_553_Ref_Trans_6_Status": "3", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "90", + "asterix.019_600_Longitude": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "-90", + "asterix.019_600_Longitude": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "0", + "asterix.019_600_Longitude": "180" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "0", + "asterix.019_600_Longitude": "-180" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x7f, 0xff], + "asterix.019_610", + { + "asterix.019_610_Height": "8191.75" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x80, 0x00], + "asterix.019_610", + { + "asterix.019_610_Height": "-8192" + } + ) + validator.add_dissection( + [0x01, 0x20, 0x7f], + "asterix.019_620", + { + "asterix.019_620_Undulation": "127" + } + ) + validator.add_dissection( + [0x01, 0x20, 0x81], + "asterix.019_620", + { + "asterix.019_620_Undulation": "-127" + } + ) + + validator.check_dissections() + + def test_undefined_value_handling(self, asterix_validator): + '''verifies that the dissector can dissect undefined field values by setting + the maximum value of bits or by setting all undefined bits''' + + validator = asterix_validator(19) + + validator.add_dissection( + [0x40, 0xff], + "asterix.019_000", + { + "asterix.019_000_MT": "255" + } + ) + validator.add_dissection( + [0x20, 0xff, 0xff, 0xff], + "asterix.019_140", + { + "asterix.TOD": "131071.9921875" + } + ) + validator.add_dissection( + [0x10, 0x07], + "asterix.019_550", + { + "asterix.019_550_NOGO": "0", + "asterix.019_550_OVL": "0", + "asterix.019_550_TSV": "0", + "asterix.019_550_TTF": "0" + } + ) + validator.add_dissection( + [0x04, 0x01, 0x00, 0x83], + "asterix.019_552", + { + "asterix.counter": "1", + "asterix.019_552": + { + "asterix.019_552_RS_Identification": "0", + "asterix.019_552_Receiver_1090_MHz": "0", + "asterix.019_552_Transmitter_1030_MHz": "0", + "asterix.019_552_Transmitter_1090_MHz": "0", + "asterix.019_552_RS_Status": "0", + "asterix.019_552_RS_Operational": "0" + } + } + ) + validator.add_dissection( + [0x02, 0x32], + "asterix.019_553", + { + "asterix.019_553_Ref_Trans_1_Status": "0", + "asterix.019_553_Ref_Trans_2_Status": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x02, 0x33, 0x33, 0x32], + "asterix.019_553", + { + "asterix.019_553_Ref_Trans_1_Status": "0", + "asterix.019_553_Ref_Trans_2_Status": "0", + "asterix.019_553_Ref_Trans_3_Status": "0", + "asterix.019_553_Ref_Trans_4_Status": "0", + "asterix.019_553_Ref_Trans_5_Status": "0", + "asterix.019_553_Ref_Trans_6_Status": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "359.999999832362", + "asterix.019_600_Longitude": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "-360", + "asterix.019_600_Longitude": "0" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff], + "asterix.019_600", + { + "asterix.019_600_Latitude": "0", + "asterix.019_600_Longitude": "359.999999832362" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00], + "asterix.019_600", + { + "asterix.019_600_Latitude": "0", + "asterix.019_600_Longitude": "-360" + } + ) + validator.add_dissection( + [0x01, 0x20, 0x80], + "asterix.019_620", + { + "asterix.019_620_Undulation": "-128" + } + ) + validator.add_dissection( + [0x01, 0x10], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x08], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x04, 0x02, 0x00], + "asterix.019_RE", + { + "asterix.re_field_len": "2", + "asterix.fspec": "" + } + ) + validator.add_dissection( + [0x01, 0x04, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff], + "asterix.019_RE", + { + "asterix.fspec": "", + "asterix.re_field_len": "16" + } + ) + validator.add_dissection( + [0x01, 0x02, 0x01], + "asterix.019_SP", + "" + ) + validator.add_dissection( + [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff], + "asterix.019_SP", + "" + ) + + validator.check_dissections() + + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_category_063(subprocesstest.SubprocessTestCase): + ''' + Unittest case for ASTERIX Category 063 + + Online specification: + https://www.eurocontrol.int/publications/cat063-sensor-status-messages-part-10 + + Part 10: Category 63 (1.4) + Sensor Status Messages + + Standard User Application Profile + + FRN Data Item Information Length + 1 I063/010 Data Source Identifier 2 + 2 I063/015 Service Identification 1 + 3 I063/030 Time of Message 3 + 4 I063/050 Sensor Identifier 2 + 5 I063/060 Sensor Configuration and Status 1+1 + 6 I063/070 Time Stamping Bias 2 + 7 I063/080 SSR/Mode S Range Gain and Bias 4 + FX - Field extension indicator - + 8 I063/081 SSR/Mode S Azimuth Bias 2 + 9 I063/090 PSR Range Gain and Bias 4 + 10 I063/091 PSR Azimuth Bias 2 + 11 I063/092 PSR Elevation Bias 2 + 12 - spare - + 13 RE Reserved Expansion Field 1+1+ + 14 SP Special Purpose Field 1+1+ + FX - Field extension indicator - + ''' + + maxDiff = None + + def test_for_fields(self, asterix_validator): + '''verifies existence of all fields and their maximum value''' + + validator = asterix_validator(63) + + validator.add_dissection( + [0x80, 0xff, 0x00], + "asterix.063_010", + { + "asterix.SAC": "255", + "asterix.SIC": "0" + } + ) + validator.add_dissection( + [0x80, 0x00, 0xff], + "asterix.063_010", + { + "asterix.SAC": "0", + "asterix.SIC": "255" + } + ) + validator.add_dissection( + [0x40, 0xff], + "asterix.063_015", + { + "asterix.063_015_SI": "255" + } + ) + validator.add_dissection( + [0x20, 0xa8, 0xbf, 0xff], + "asterix.063_030", + { + "asterix.TOD": "86399.9921875" + } + ) + validator.add_dissection( + [0x10, 0xff, 0x00], + "asterix.063_050", + { + "asterix.SAC": "255", + "asterix.SIC": "0" + } + ) + validator.add_dissection( + [0x10, 0x00, 0xff], + "asterix.063_050", + { + "asterix.SAC": "0", + "asterix.SIC": "255" + } + ) + validator.add_dissection( + [0x08, 0xc0], + "asterix.063_060", + { + "asterix.063_060_CON": "3", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x20], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "1", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x10], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "1", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x08], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "1", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x04], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "1", + "asterix.063_060_MLT": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x02], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "1", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x80], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "1", + "asterix.063_060_ODP": "0", + "asterix.063_060_OXT": "0", + "asterix.063_060_MSC": "0", + "asterix.063_060_TSV": "0", + "asterix.063_060_NPW": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x40], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "0", + "asterix.063_060_ODP": "1", + "asterix.063_060_OXT": "0", + "asterix.063_060_MSC": "0", + "asterix.063_060_TSV": "0", + "asterix.063_060_NPW": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x20], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "0", + "asterix.063_060_ODP": "0", + "asterix.063_060_OXT": "1", + "asterix.063_060_MSC": "0", + "asterix.063_060_TSV": "0", + "asterix.063_060_NPW": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x10], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "0", + "asterix.063_060_ODP": "0", + "asterix.063_060_OXT": "0", + "asterix.063_060_MSC": "1", + "asterix.063_060_TSV": "0", + "asterix.063_060_NPW": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x08], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "0", + "asterix.063_060_ODP": "0", + "asterix.063_060_OXT": "0", + "asterix.063_060_MSC": "0", + "asterix.063_060_TSV": "1", + "asterix.063_060_NPW": "0", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x08, 0x01, 0x04], + "asterix.063_060", + { + "asterix.063_060_CON": "0", + "asterix.063_060_PSR": "0", + "asterix.063_060_SSR": "0", + "asterix.063_060_MDS": "0", + "asterix.063_060_ADS": "0", + "asterix.063_060_MLT": "0", + "asterix.063_060_OPS": "0", + "asterix.063_060_ODP": "0", + "asterix.063_060_OXT": "0", + "asterix.063_060_MSC": "0", + "asterix.063_060_TSV": "0", + "asterix.063_060_NPW": "1", + "asterix.FX": "0" + } + ) + validator.add_dissection( + [0x04, 0xff, 0xff], + "asterix.063_070", + { + "asterix.063_070_TSB": "65535" + } + ) + validator.add_dissection( + [0x02, 0x7f, 0xff, 0x00, 0x00], + "asterix.063_080", + { + "asterix.063_080_SRG": "0.32767", + "asterix.063_080_SRB": "0" + } + ) + validator.add_dissection( + [0x02, 0x80, 0x00, 0x00, 0x00], + "asterix.063_080", + { + "asterix.063_080_SRG": "-0.32768", + "asterix.063_080_SRB": "0" + } + ) + validator.add_dissection( + [0x02, 0x00, 0x00, 0x7f, 0xff], + "asterix.063_080", + { + "asterix.063_080_SRG": "0", + "asterix.063_080_SRB": "255.9921875" + } + ) + validator.add_dissection( + [0x02, 0x00, 0x00, 0x80, 0x00], + "asterix.063_080", + { + "asterix.063_080_SRG": "0", + "asterix.063_080_SRB": "-256" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x7f, 0xff], + "asterix.063_081", + { + "asterix.063_081_SAB": "179.994506835938" + } + ) + validator.add_dissection( + [0x01, 0x80, 0x80, 0x00], + "asterix.063_081", + { + "asterix.063_081_SAB": "-180" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x7f, 0xff, 0x00, 0x00], + "asterix.063_090", + { + "asterix.063_090_PRG": "0.32767", + "asterix.063_090_PRB": "0" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x80, 0x00, 0x00, 0x00], + "asterix.063_090", + { + "asterix.063_090_PRG": "-0.32768", + "asterix.063_090_PRB": "0" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x00, 0x00, 0x7f, 0xff], + "asterix.063_090", + { + "asterix.063_090_PRG": "0", + "asterix.063_090_PRB": "255.9921875" + } + ) + validator.add_dissection( + [0x01, 0x40, 0x00, 0x00, 0x80, 0x00], + "asterix.063_090", + { + "asterix.063_090_PRG": "0", + "asterix.063_090_PRB": "-256" + } + ) + validator.add_dissection( + [0x01, 0x20, 0x7f, 0xff], + "asterix.063_091", + { + "asterix.063_091_PAB": "179.994506835938" + } + ) + validator.add_dissection( + [0x01, 0x20, 0x80, 0x00], + "asterix.063_091", + { + "asterix.063_091_PAB": "-180" + } + ) + validator.add_dissection( + [0x01, 0x10, 0x7f, 0xff], + "asterix.063_092", + { + "asterix.063_092_PEB": "179.994506835938" + } + ) + validator.add_dissection( + [0x01, 0x10, 0x80, 0x00], + "asterix.063_092", + { + "asterix.063_092_PEB": "-180" + } + ) + + validator.check_dissections() + + def test_undefined_value_handling(self, asterix_validator): + '''verifies that the dissector can dissect undefined field values by + setting the maximum value of bits or by setting all undefined bits''' + + validator = asterix_validator(63) + + validator.add_dissection( + [0x01, 0x08], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x04, 0x02, 0x00], + "asterix.063_RE", + { + "asterix.re_field_len": "2", + "asterix.fspec": "" + } + ) + validator.add_dissection( + [0x01, 0x04, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff], + "asterix.063_RE", + { + "asterix.fspec": "", + "asterix.re_field_len": "16" + } + ) + validator.add_dissection( + [0x01, 0x02, 0x01], + "asterix.063_SP", + "" + ) + validator.add_dissection( + [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff], + "asterix.063_SP", + "" + ) + + validator.check_dissections() + + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_category_065(subprocesstest.SubprocessTestCase): + ''' + Unittest case for ASTERIX Category 065 + + Online specification: + https://www.eurocontrol.int/publications/cat065-surveillance-data-processing-system-sdps-service-status-messages-part-15 + https://www.eurocontrol.int/publications/cat065-coding-rules-reserved-expansion-field-part-15-appendix + + Part 15 Category 65 (1.4) + SDPS Service Status Reports + + Standard User Application Profile + + FRN Data Item Information Length + 1 I065/010 Data Source Identifier 2 + 2 I065/000 Message Type 1 + 3 I065/015 Service Identification 1 + 4 I065/030 Time of Message 3 + 5 I065/020 Batch Number 1 + 6 I065/040 SDPS Configuration and Status 1 + 7 I065/050 Service Status Report 1 + FX - Field extension indicator - + 8 - Spare - + 9 - Spare - + 10 - Spare - + 11 - Spare - + 12 - Spare - + 13 RE Reserved Expansion Field 1+1+ + 14 SP Special Purpose Field 1+1+ + FX - Field extension indicator - + ''' + + maxDiff = None + + def test_for_fields(self, asterix_validator): + '''verifies existence of all fields and their maximum value''' + + validator = asterix_validator(65) + + validator.add_dissection( + [0x80, 0xff, 0x00], + "asterix.065_010", + { + "asterix.SAC": "255", + "asterix.SIC": "0" + } + ) + validator.add_dissection( + [0x80, 0x00, 0xff], + "asterix.065_010", + { + "asterix.SAC": "0", + "asterix.SIC": "255" + } + ) + validator.add_dissection( + [0x40, 0x03], + "asterix.065_000", + { + "asterix.065_000_MT": "3" + } + ) + validator.add_dissection( + [0x20, 0xff], + "asterix.065_015", + { + "asterix.065_015_SI": "255" + } + ) + validator.add_dissection( + [0x10, 0xa8, 0xbf, 0xff], + "asterix.065_030", + { + "asterix.TOD": "86399.9921875" + } + ) + validator.add_dissection( + [0x08, 0xff], + "asterix.065_020", + { + "asterix.065_020_BTN": "255" + } + ) + validator.add_dissection( + [0x04, 0xc0], + "asterix.065_040", + { + "asterix.065_040_NOGO": "3", + "asterix.065_040_OVL": "0", + "asterix.065_040_TSV": "0", + "asterix.065_040_PSS": "0", + "asterix.065_040_STTN": "0" + } + ) + validator.add_dissection( + [0x04, 0x20], + "asterix.065_040", + { + "asterix.065_040_NOGO": "0", + "asterix.065_040_OVL": "1", + "asterix.065_040_TSV": "0", + "asterix.065_040_PSS": "0", + "asterix.065_040_STTN": "0" + } + ) + validator.add_dissection( + [0x04, 0x10], + "asterix.065_040", + { + "asterix.065_040_NOGO": "0", + "asterix.065_040_OVL": "0", + "asterix.065_040_TSV": "1", + "asterix.065_040_PSS": "0", + "asterix.065_040_STTN": "0" + } + ) + validator.add_dissection( + [0x04, 0x0c], + "asterix.065_040", + { + "asterix.065_040_NOGO": "0", + "asterix.065_040_OVL": "0", + "asterix.065_040_TSV": "0", + "asterix.065_040_PSS": "3", + "asterix.065_040_STTN": "0" + } + ) + validator.add_dissection( + [0x04, 0x02], + "asterix.065_040", + { + "asterix.065_040_NOGO": "0", + "asterix.065_040_OVL": "0", + "asterix.065_040_TSV": "0", + "asterix.065_040_PSS": "0", + "asterix.065_040_STTN": "1" + } + ) + validator.add_dissection( + [0x02, 0xff], + "asterix.065_050", + { + "asterix.065_050_REP": "255" + } + ) + validator.add_dissection( + [0x01, 0x04, 0x02, 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "2", + "asterix.fspec": "" + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "90", + "asterix.065_RE_SRP_Longitude": "0" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "-90", + "asterix.065_RE_SRP_Longitude": "0" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "0", + "asterix.065_RE_SRP_Longitude": "180" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "0", + "asterix.065_RE_SRP_Longitude": "-180" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x04, 0x40, 0xff, 0xfc], + "asterix.065_RE", + { + "asterix.re_field_len": "4", + "asterix.fspec": "", + "asterix.065_RE_ARL": + { + "asterix.065_RE_ARL_ARL": "65532" + } + } + ) + + validator.check_dissections() + + def test_undefined_value_handling(self, asterix_validator): + '''verifies that the dissector can dissect undefined field values by + setting the maximum value of bits or by setting all undefined bits''' + + validator = asterix_validator(65) + + validator.add_dissection( + [0x40, 0xff], + "asterix.065_000", + { + "asterix.065_000_MT": "255" + } + ) + validator.add_dissection( + [0x10, 0xff, 0xff, 0xff], + "asterix.065_030", + { + "asterix.TOD": "131071.9921875" + } + ) + validator.add_dissection( + [0x04, 0x01], + "asterix.065_040", + { + "asterix.065_040_NOGO": "0", + "asterix.065_040_OVL": "0", + "asterix.065_040_TSV": "0", + "asterix.065_040_PSS": "0", + "asterix.065_040_STTN": "0" + } + ) + validator.add_dissection( + [0x01, 0x80], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x40], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x20], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x10], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x08], + "asterix.spare", + "" + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "359.999999832362", + "asterix.065_RE_SRP_Longitude": "0" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "-360", + "asterix.065_RE_SRP_Longitude": "0" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, + 0xff], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "0", + "asterix.065_RE_SRP_Longitude": "359.999999832362" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00], + "asterix.065_RE", + { + "asterix.re_field_len": "10", + "asterix.fspec": "", + "asterix.065_RE_SRP": + { + "asterix.065_RE_SRP_Latitude": "0", + "asterix.065_RE_SRP_Longitude": "-360" + } + } + ) + validator.add_dissection( + [0x01, 0x04, 0x04, 0x40, 0xff, 0xff], + "asterix.065_RE", + { + "asterix.re_field_len": "4", + "asterix.fspec": "", + "asterix.065_RE_ARL": + { + "asterix.065_RE_ARL_ARL": "65535" + } + } + ) + validator.add_dissection( + [0x01, 0x02, 0x01], + "asterix.065_SP", + "" + ) + validator.add_dissection( + [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff], + "asterix.065_SP", + "" + ) + + validator.check_dissections() -- 2.34.1