File: test_cbor_decoding.py

package info (click to toggle)
python-botocore 1.37.9%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 121,768 kB
  • sloc: python: 73,696; xml: 14,880; javascript: 181; makefile: 155
file content (132 lines) | stat: -rw-r--r-- 3,776 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import json
import os
import struct

import pytest

from botocore.parsers import ResponseParserError, RpcV2CBORParser

IGNORE_CASES = [
    # We ignore all the tag tests since none of them are supported tags in AWS.
    # The majority of these aren't even defined tags in CBOR and just test that we
    # can properly parse the number
    'tag - 0/min',
    'tag - 1/min',
    'tag - 2/min',
    'tag - 4/min',
    'tag - 8/min',
    'tag - 0/max',
    'tag - 1/max',
    'tag - 2/max',
    'tag - 4/max',
    'tag - 8/max',
    # We are expected to drop keys with null values, which is the opposite of the
    # assertion in these two map tests
    'map - {_ null}',
    'map - {null}',
]

TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)))


@pytest.fixture(scope="module")
def parser():
    return RpcV2CBORParser()


def _get_cbor_decoding_success_tests():
    success_test_file_name = os.path.join(
        TEST_DIR, 'decode-success-tests.json'
    )
    success_test_data = json.load(open(success_test_file_name))
    for case in success_test_data:
        if case['description'] in IGNORE_CASES:
            continue
        yield case['description'], case['input'], case['expect']


def _get_cbor_decoding_error_tests():
    error_test_file_name = os.path.join(TEST_DIR, 'decode-error-tests.json')
    error_test_data = json.load(open(error_test_file_name))
    for case in error_test_data:
        yield case['description'], case['input'], case['error']


@pytest.mark.parametrize(
    "json_description, input, expect", _get_cbor_decoding_success_tests()
)
def test_cbor_decoding_success(json_description, input, expect, parser):
    stream = parser.get_peekable_stream_from_bytes(bytearray.fromhex(input))
    parsed = parser.parse_data_item(stream)
    _assert_expected_value(parsed, expect)


@pytest.mark.parametrize(
    "json_description, input, error", _get_cbor_decoding_error_tests()
)
def test_cbor_decoding_error(json_description, input, error, parser):
    stream = parser.get_peekable_stream_from_bytes(bytearray.fromhex(input))
    with pytest.raises(ResponseParserError):
        parser.parse_data_item(stream)


def assert_null(actual):
    assert actual is None


def assert_bytestring(actual, value):
    assert actual == bytes(value)


def assert_list(actual, value):
    assert isinstance(actual, list)
    for act_val, exp_val in zip(actual, value):
        _assert_expected_value(act_val, exp_val)


def assert_map(actual, value):
    assert isinstance(actual, dict)
    for key, val in value.items():
        assert key in actual
        _assert_expected_value(actual[key], val)


def assert_tag(actual, value):
    assert actual.tag == value['id']
    assert actual.value == value['value']['uint']


def assert_float(actual, expected_key, value):
    struct_format = '<f' if expected_key == 'float32' else '<d'
    packed_value = struct.pack(
        '<I' if expected_key == 'float32' else '<Q', value
    )
    unpacked_value = struct.unpack(struct_format, packed_value)[0]
    assert actual == unpacked_value


def assert_default(actual, value):
    assert actual == value


ASSERTION_MAP = {
    'null': assert_null,
    'undefined': assert_null,
    'bytestring': assert_bytestring,
    'list': assert_list,
    'map': assert_map,
    'tag': assert_tag,
    'float32': assert_float,
    'float64': assert_float,
}


def _assert_expected_value(actual, expected):
    for expected_key, value in expected.items():
        assertion_func = ASSERTION_MAP.get(expected_key, assert_default)
        if expected_key in ['null']:
            assertion_func(actual)
        elif expected_key in ['float32', 'float64']:
            assertion_func(actual, expected_key, value)
        else:
            assertion_func(actual, value)