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_read_test 20 21Command-line arguments must be provided after a double-dash: 22 23 bazel run pw_transfer/integration_test:cross_language_medium_read_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_read_test -- \ 29 MediumTransferReadIntegrationTest.test_medium_client_read_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 52_ALL_LANGUAGES_V2 = tuple( 53 itertools.product( 54 _ALL_LANGUAGES, [config_pb2.TransferAction.ProtocolVersion.V2] 55 ) 56) 57 58 59class MediumTransferReadIntegrationTest(test_fixture.TransferIntegrationTest): 60 # Each set of transfer tests uses a different client/server port pair to 61 # allow tests to be run in parallel. 62 HARNESS_CONFIG = TransferIntegrationTestHarness.Config( 63 server_port=3304, client_port=3305 64 ) 65 66 @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS) 67 def test_medium_client_read(self, client_type, protocol_version): 68 payload = random.Random(67336391945).randbytes(512) 69 config = self.default_config() 70 resource_id = 5 71 self.do_single_read( 72 client_type, config, resource_id, payload, protocol_version 73 ) 74 75 @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS) 76 def test_large_hdlc_escape_client_read(self, client_type, protocol_version): 77 # Use bytes that will be escaped by HDLC to ensure transfer over a 78 # HDLC channel doesn't cause frame corruption due to insufficient 79 # buffer space. ~10KB is relatively arbitrary, but is to ensure that 80 # more than a small handful of packets are sent between the server 81 # and client. 82 payload = b"~" * 98731 83 config = self.default_config() 84 resource_id = 5 85 self.do_single_read( 86 client_type, config, resource_id, payload, protocol_version 87 ) 88 89 @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS) 90 def test_pattern_drop_client_read(self, client_type, protocol_version): 91 """Drops packets with an alternating pattern.""" 92 payload = random.Random(67336391945).randbytes(1234) 93 config = TransferConfig( 94 self.default_server_config(), 95 self.default_client_config(), 96 text_format.Parse( 97 """ 98 client_filter_stack: [ 99 { hdlc_packetizer: {} }, 100 { 101 keep_drop_queue: { 102 keep_drop_queue: [5, 1], 103 only_consider_transfer_chunks: true, 104 } 105 } 106 ] 107 108 server_filter_stack: [ 109 { hdlc_packetizer: {} }, 110 { 111 keep_drop_queue: { 112 keep_drop_queue: [5, 1], 113 only_consider_transfer_chunks: true, 114 } 115 } 116 ]""", 117 config_pb2.ProxyConfig(), 118 ), 119 ) 120 # Resource ID is arbitrary, but deliberately set to be >1 byte. 121 resource_id = 1337 122 123 # This test causes flakes during the opening handshake of a transfer, so 124 # allow the resource_id of this transfer to be reused multiple times. 125 self.do_single_read( 126 client_type, 127 config, 128 resource_id, 129 payload, 130 protocol_version, 131 permanent_resource_id=True, 132 ) 133 134 @parameterized.expand(_ALL_LANGUAGES_AND_VERSIONS) 135 def test_parameter_drop_client_read(self, client_type, protocol_version): 136 """Drops the first few transfer initialization packets.""" 137 payload = random.Random(67336391945).randbytes(1234) 138 config = TransferConfig( 139 self.default_server_config(), 140 self.default_client_config(), 141 text_format.Parse( 142 """ 143 client_filter_stack: [ 144 { hdlc_packetizer: {} }, 145 { 146 keep_drop_queue: { 147 keep_drop_queue: [2, 1, -1], 148 only_consider_transfer_chunks: true, 149 } 150 } 151 ] 152 153 server_filter_stack: [ 154 { hdlc_packetizer: {} }, 155 { 156 keep_drop_queue: { 157 keep_drop_queue: [1, 2, -1], 158 only_consider_transfer_chunks: true, 159 } 160 } 161 ]""", 162 config_pb2.ProxyConfig(), 163 ), 164 ) 165 # Resource ID is arbitrary, but deliberately set to be >2 bytes. 166 resource_id = 597419 167 168 # This test deliberately causes flakes during the opening handshake of 169 # a transfer, so allow the resource_id of this transfer to be reused 170 # multiple times. 171 self.do_single_read( 172 client_type, 173 config, 174 resource_id, 175 payload, 176 protocol_version, 177 permanent_resource_id=True, 178 ) 179 180 @parameterized.expand(_ALL_LANGUAGES_V2) 181 def test_medium_client_read_offset(self, client_type, protocol_version): 182 payload = random.Random(67336391945).randbytes(512) 183 config = self.default_config() 184 185 resource_id = 6 186 self.do_single_read( 187 client_type, 188 config, 189 resource_id, 190 payload, 191 protocol_version, 192 initial_offset=100, 193 offsettable_resources=True, 194 ) 195 196 @parameterized.expand(_ALL_LANGUAGES_V2) 197 def test_medium_client_read_offset_with_drops( 198 self, client_type, protocol_version 199 ): 200 payload = random.Random(67336391945).randbytes(1024) 201 config = TransferConfig( 202 self.default_server_config(), 203 self.default_client_config(), 204 text_format.Parse( 205 """ 206 client_filter_stack: [ 207 { hdlc_packetizer: {} }, 208 { 209 keep_drop_queue: { 210 keep_drop_queue: [5, 1], 211 only_consider_transfer_chunks: true, 212 } 213 } 214 ] 215 216 server_filter_stack: [ 217 { hdlc_packetizer: {} }, 218 { 219 keep_drop_queue: { 220 keep_drop_queue: [5, 1], 221 only_consider_transfer_chunks: true, 222 } 223 } 224 ]""", 225 config_pb2.ProxyConfig(), 226 ), 227 ) 228 229 resource_id = 7 230 self.do_single_read( 231 client_type, 232 config, 233 resource_id, 234 payload, 235 protocol_version, 236 initial_offset=100, 237 offsettable_resources=True, 238 ) 239 240 241if __name__ == '__main__': 242 test_fixture.run_tests_for(MediumTransferReadIntegrationTest) 243