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