1# Copyright 2022 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""A variety of transfer tests that validate backwards compatibility. 15 16Usage: 17 18 bazel run pw_transfer/integration_test:legacy_binaries_test 19 20Command-line arguments must be provided after a double-dash: 21 22 bazel run pw_transfer/integration_test:legacy_binaries_test -- \ 23 --server-port 3304 24 25Which tests to run can be specified as command-line arguments: 26 27 bazel run pw_transfer/integration_test:legacy_binaries_test -- \ 28 LegacyClientTransferIntegrationTests.test_small_client_write_0_cpp 29 30""" 31 32from parameterized import parameterized 33import random 34 35from pw_transfer.integration_test import config_pb2 36from pw_transfer.integration_test import test_fixture 37from test_fixture import TransferIntegrationTestHarness 38from python.runfiles import runfiles 39 40# Each set of transfer tests uses a different client/server port pair to 41# allow tests to be run in parallel. 42_SERVER_PORT = 3314 43_CLIENT_PORT = 3315 44 45 46# NOTE: These backwards compatibility tests DO NOT include tests that verify 47# expected error cases (e.g. timeouts, unknown resource ID) because legacy 48# integration test clients did not support the ability to check those. 49# Additionally, there are deliberately NOT back-to-back read/write transfers 50# because of known issues with transfer cleanup in the legacy transfer protocol. 51class LegacyTransferIntegrationTest(test_fixture.TransferIntegrationTest): 52 """This base class defines the tests to run, but isn't run directly.""" 53 54 # Explicitly use UNKNOWN_VERSION (the default value of 55 # TransferAction.protocol_version), as that will cause protocol_version to 56 # be omitted from the generated text proto, which is critical for the legacy 57 # client that doesn't support transfer version specifications (and will 58 # cause a proto parse error). 59 PROTOCOL_VERSION = config_pb2.TransferAction.ProtocolVersion.UNKNOWN_VERSION 60 LEGACY_SERVER = False 61 LEGACY_CLIENT = False 62 63 def default_config(self) -> test_fixture.TransferConfig: 64 # The legacy binaries aren't aware of the max_lifetime_retries field, 65 # which was added more recently. Clear it so it isn't encoded into the 66 # serialized message. 67 config = super().default_config() 68 config.client.max_lifetime_retries = 0 69 return config 70 71 @parameterized.expand( 72 [ 73 ("cpp"), 74 ("java"), 75 ("python"), 76 ] 77 ) 78 def test_single_byte_client_write(self, client_type): 79 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 80 self.skipTest("No legacy binary in use, skipping") 81 82 if not self.LEGACY_SERVER and ( 83 client_type == "java" or client_type == "python" 84 ): 85 self.skipTest("Java and Python legacy clients not yet set up") 86 87 payload = b"?" 88 config = self.default_config() 89 resource_id = 5 90 self.do_single_write( 91 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 92 ) 93 94 @parameterized.expand( 95 [ 96 ("cpp"), 97 ("java"), 98 ("python"), 99 ] 100 ) 101 def test_small_client_write(self, client_type): 102 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 103 self.skipTest("No legacy binary in use, skipping") 104 105 if not self.LEGACY_SERVER and ( 106 client_type == "java" or client_type == "python" 107 ): 108 self.skipTest("Java and Python legacy clients not yet set up") 109 110 payload = b"some data" 111 config = self.default_config() 112 resource_id = 5 113 self.do_single_write( 114 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 115 ) 116 117 @parameterized.expand( 118 [ 119 ("cpp"), 120 ("java"), 121 ("python"), 122 ] 123 ) 124 def test_medium_hdlc_escape_client_write(self, client_type): 125 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 126 self.skipTest("No legacy binary in use, skipping") 127 128 if not self.LEGACY_SERVER and ( 129 client_type == "java" or client_type == "python" 130 ): 131 self.skipTest("Java and Python legacy clients not yet set up") 132 133 payload = b"~" * 8731 134 config = self.default_config() 135 resource_id = 12345678 136 self.do_single_write( 137 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 138 ) 139 140 @parameterized.expand( 141 [ 142 ("cpp"), 143 ("java"), 144 ("python"), 145 ] 146 ) 147 def test_medium_random_data_client_write(self, client_type): 148 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 149 self.skipTest("No legacy binary in use, skipping") 150 151 if not self.LEGACY_SERVER and ( 152 client_type == "java" or client_type == "python" 153 ): 154 self.skipTest("Java and Python legacy clients not yet set up") 155 156 rng = random.Random(1533659510898) 157 payload = rng.randbytes(13713) 158 config = self.default_config() 159 resource_id = 12345678 160 self.do_single_write( 161 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 162 ) 163 164 @parameterized.expand( 165 [ 166 ("cpp"), 167 ("java"), 168 ("python"), 169 ] 170 ) 171 def test_single_byte_client_read(self, client_type): 172 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 173 self.skipTest("No legacy binary in use, skipping") 174 175 if not self.LEGACY_SERVER and ( 176 client_type == "java" or client_type == "python" 177 ): 178 self.skipTest("Java and Python legacy clients not yet set up") 179 180 payload = b"?" 181 config = self.default_config() 182 resource_id = 5 183 self.do_single_read( 184 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 185 ) 186 187 @parameterized.expand( 188 [ 189 ("cpp"), 190 ("java"), 191 ("python"), 192 ] 193 ) 194 def test_small_client_read(self, client_type): 195 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 196 self.skipTest("No legacy binary in use, skipping") 197 198 if not self.LEGACY_SERVER and ( 199 client_type == "java" or client_type == "python" 200 ): 201 self.skipTest("Java and Python legacy clients not yet set up") 202 203 payload = b"some data" 204 config = self.default_config() 205 resource_id = 5 206 self.do_single_read( 207 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 208 ) 209 210 @parameterized.expand( 211 [ 212 ("cpp"), 213 ("java"), 214 ("python"), 215 ] 216 ) 217 def test_medium_hdlc_escape_client_read(self, client_type): 218 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 219 self.skipTest("No legacy binary in use, skipping") 220 221 if self.LEGACY_SERVER: 222 self.skipTest("Legacy server has HDLC buffer sizing issues") 223 224 payload = b"~" * 8731 225 config = self.default_config() 226 resource_id = 5 227 self.do_single_read( 228 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 229 ) 230 231 @parameterized.expand( 232 [ 233 ("cpp"), 234 ("java"), 235 ("python"), 236 ] 237 ) 238 def test_medium_random_data_client_read(self, client_type): 239 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 240 self.skipTest("No legacy binary in use, skipping") 241 242 if self.LEGACY_SERVER: 243 self.skipTest("Legacy server has HDLC buffer sizing issues") 244 245 rng = random.Random(1533659510898) 246 payload = rng.randbytes(13713) 247 config = self.default_config() 248 resource_id = 5 249 self.do_single_read( 250 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 251 ) 252 253 254class LegacyClientTransferIntegrationTests(LegacyTransferIntegrationTest): 255 r = runfiles.Create() 256 client_binary = r.Rlocation( 257 "+_repo_rules+pw_transfer_test_binaries/cpp_client_528098d5", 258 ) 259 HARNESS_CONFIG = TransferIntegrationTestHarness.Config( 260 cpp_client_binary=client_binary, 261 server_port=_SERVER_PORT, 262 client_port=_CLIENT_PORT, 263 ) 264 LEGACY_CLIENT = True 265 266 267class LegacyServerTransferIntegrationTests(LegacyTransferIntegrationTest): 268 r = runfiles.Create() 269 server_binary = r.Rlocation( 270 "+_repo_rules+pw_transfer_test_binaries/server_528098d5", 271 ) 272 HARNESS_CONFIG = TransferIntegrationTestHarness.Config( 273 server_binary=server_binary, 274 server_port=_SERVER_PORT, 275 client_port=_CLIENT_PORT, 276 ) 277 LEGACY_SERVER = True 278 279 280if __name__ == '__main__': 281 test_fixture.run_tests_for(LegacyClientTransferIntegrationTests) 282 test_fixture.run_tests_for(LegacyServerTransferIntegrationTests) 283