xref: /aosp_15_r20/external/pigweed/pw_transfer/integration_test/cross_language_medium_write_test.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1#!/usr/bin/env python3
2# Copyright 2022 The Pigweed Authors
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5# use this file except in compliance with the License. You may obtain a copy of
6# the License at
7#
8#     https://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations under
14# the License.
15"""Cross-language pw_transfer tests that take several seconds each.
16
17Usage:
18
19   bazel run pw_transfer/integration_test:cross_language_medium_write_test
20
21Command-line arguments must be provided after a double-dash:
22
23   bazel run pw_transfer/integration_test:cross_language_medium_write_test -- \
24       --server-port 3304
25
26Which tests to run can be specified as command-line arguments:
27
28  bazel run pw_transfer/integration_test:cross_language_medium_write_test -- \
29      MediumTransferWriteIntegrationTest.test_medium_client_write_1_java
30
31"""
32
33import itertools
34from parameterized import parameterized
35import random
36
37from google.protobuf import text_format
38
39from pw_transfer.integration_test import config_pb2
40from pw_transfer.integration_test import test_fixture
41from test_fixture import TransferIntegrationTestHarness, TransferConfig
42
43_ALL_LANGUAGES = ("cpp", "java", "python")
44_ALL_VERSIONS = (
45    config_pb2.TransferAction.ProtocolVersion.V1,
46    config_pb2.TransferAction.ProtocolVersion.V2,
47)
48_ALL_LANGUAGES_AND_VERSIONS = tuple(
49    itertools.product(_ALL_LANGUAGES, _ALL_VERSIONS)
50)
51_ALL_LANGUAGES_V2 = tuple(
52    itertools.product(
53        _ALL_LANGUAGES, [config_pb2.TransferAction.ProtocolVersion.V2]
54    )
55)
56
57
58class MediumTransferWriteIntegrationTest(test_fixture.TransferIntegrationTest):
59    # Each set of transfer tests uses a different client/server port pair to
60    # allow tests to be run in parallel.
61    HARNESS_CONFIG = TransferIntegrationTestHarness.Config(
62        server_port=3316, client_port=3317
63    )
64
65    @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS)
66    def test_medium_client_write(self, client_type, protocol_version):
67        payload = random.Random(67336391945).randbytes(512)
68        config = self.default_config()
69        resource_id = 5
70        self.do_single_write(
71            client_type, config, resource_id, payload, protocol_version
72        )
73
74    @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS)
75    def test_large_hdlc_escape_client_write(
76        self, client_type, protocol_version
77    ):
78        # Use bytes that will be escaped by HDLC to ensure transfer over a
79        # HDLC channel doesn't cause frame corruption due to insufficient
80        # buffer space. ~10KB is relatively arbitrary, but is to ensure that
81        # more than a small handful of packets are sent between the server
82        # and client.
83        payload = b"~" * 98731
84        config = self.default_config()
85        resource_id = 5
86        self.do_single_write(
87            client_type, config, resource_id, payload, protocol_version
88        )
89
90    @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS)
91    def test_pattern_drop_client_write(self, client_type, protocol_version):
92        """Drops packets with an alternating pattern."""
93        payload = random.Random(67336391945).randbytes(1234)
94        config = TransferConfig(
95            self.default_server_config(),
96            self.default_client_config(),
97            text_format.Parse(
98                """
99                client_filter_stack: [
100                    { hdlc_packetizer: {} },
101                    { keep_drop_queue: {keep_drop_queue: [5, 1]} }
102                ]
103
104                server_filter_stack: [
105                    { hdlc_packetizer: {} },
106                    { keep_drop_queue: {keep_drop_queue: [5, 1]} }
107            ]""",
108                config_pb2.ProxyConfig(),
109            ),
110        )
111        # Resource ID is arbitrary, but deliberately set to be >1 byte.
112        resource_id = 1337
113
114        # This test deliberately causes flakes during the opening handshake of
115        # a transfer, so allow the resource_id of this transfer to be reused
116        # multiple times.
117        self.do_single_write(
118            client_type,
119            config,
120            resource_id,
121            payload,
122            protocol_version,
123            permanent_resource_id=True,
124        )
125
126    @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS)
127    def test_parameter_drop_client_write(self, client_type, protocol_version):
128        """Drops the first few transfer initialization packets."""
129        payload = random.Random(67336391945).randbytes(1234)
130        config = TransferConfig(
131            self.default_server_config(),
132            self.default_client_config(),
133            text_format.Parse(
134                """
135                client_filter_stack: [
136                    { hdlc_packetizer: {} },
137                    { keep_drop_queue: {keep_drop_queue: [2, 1, -1]} }
138                ]
139
140                server_filter_stack: [
141                    { hdlc_packetizer: {} },
142                    { keep_drop_queue: {keep_drop_queue: [1, 2, -1]} }
143            ]""",
144                config_pb2.ProxyConfig(),
145            ),
146        )
147        # Resource ID is arbitrary, but deliberately set to be >2 bytes.
148        resource_id = 597419
149        self.do_single_write(
150            client_type, config, resource_id, payload, protocol_version
151        )
152
153    @parameterized.expand(_ALL_LANGUAGES_V2)
154    def test_medium_client_write_offset(self, client_type, protocol_version):
155        """Runs a non-zero starting offset transfer."""
156        payload = random.Random(67336391945).randbytes(512)
157        config = self.default_config()
158        resource_id = 6
159        self.do_single_write(
160            client_type,
161            config,
162            resource_id,
163            payload,
164            protocol_version,
165            initial_offset=100,
166            offsettable_resources=True,
167        )
168
169    @parameterized.expand(_ALL_LANGUAGES_V2)
170    def test_medium_client_write_offset_with_drops(
171        self, client_type, protocol_version
172    ):
173        """Tests a non-zero starting offset transfer with periodic drops."""
174        payload = random.Random(67336391945).randbytes(1024)
175        config = TransferConfig(
176            self.default_server_config(),
177            self.default_client_config(),
178            text_format.Parse(
179                """
180                client_filter_stack: [
181                    { hdlc_packetizer: {} },
182                    { keep_drop_queue: {keep_drop_queue: [5, 1]} }
183                ]
184
185                server_filter_stack: [
186                    { hdlc_packetizer: {} },
187                    { keep_drop_queue: {keep_drop_queue: [5, 1]} }
188            ]""",
189                config_pb2.ProxyConfig(),
190            ),
191        )
192        resource_id = 6
193        self.do_single_write(
194            client_type,
195            config,
196            resource_id,
197            payload,
198            protocol_version,
199            initial_offset=100,
200            offsettable_resources=True,
201        )
202
203
204if __name__ == '__main__':
205    test_fixture.run_tests_for(MediumTransferWriteIntegrationTest)
206