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 32import itertools 33from parameterized import parameterized 34import random 35 36from pigweed.pw_transfer.integration_test import config_pb2 37import test_fixture 38from test_fixture import TransferIntegrationTestHarness 39from rules_python.python.runfiles import runfiles 40 41# Each set of transfer tests uses a different client/server port pair to 42# allow tests to be run in parallel. 43_SERVER_PORT = 3314 44_CLIENT_PORT = 3315 45 46 47# NOTE: These backwards compatibility tests DO NOT include tests that verify 48# expected error cases (e.g. timeouts, unknown resource ID) because legacy 49# integration test clients did not support the ability to check those. 50# Additionally, there are deliberately NOT back-to-back read/write transfers 51# because of known issues with transfer cleanup in the legacy transfer protocol. 52class LegacyTransferIntegrationTest(test_fixture.TransferIntegrationTest): 53 """This base class defines the tests to run, but isn't run directly.""" 54 55 # Explicitly use UNKNOWN_VERSION (the default value of 56 # TransferAction.protocol_version), as that will cause protocol_version to 57 # be omitted from the generated text proto, which is critical for the legacy 58 # client that doesn't support transfer version specifications (and will 59 # cause a proto parse error). 60 PROTOCOL_VERSION = config_pb2.TransferAction.ProtocolVersion.UNKNOWN_VERSION 61 LEGACY_SERVER = False 62 LEGACY_CLIENT = False 63 64 def default_config(self) -> test_fixture.TransferConfig: 65 # The legacy binaries aren't aware of the max_lifetime_retries field, 66 # which was added more recently. Clear it so it isn't encoded into the 67 # serialized message. 68 config = super().default_config() 69 config.client.max_lifetime_retries = 0 70 return config 71 72 @parameterized.expand( 73 [ 74 ("cpp"), 75 ("java"), 76 ("python"), 77 ] 78 ) 79 def test_single_byte_client_write(self, client_type): 80 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 81 self.skipTest("No legacy binary in use, skipping") 82 83 if not self.LEGACY_SERVER and ( 84 client_type == "java" or client_type == "python" 85 ): 86 self.skipTest("Java and Python legacy clients not yet set up") 87 88 payload = b"?" 89 config = self.default_config() 90 resource_id = 5 91 self.do_single_write( 92 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 93 ) 94 95 @parameterized.expand( 96 [ 97 ("cpp"), 98 ("java"), 99 ("python"), 100 ] 101 ) 102 def test_small_client_write(self, client_type): 103 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 104 self.skipTest("No legacy binary in use, skipping") 105 106 if not self.LEGACY_SERVER and ( 107 client_type == "java" or client_type == "python" 108 ): 109 self.skipTest("Java and Python legacy clients not yet set up") 110 111 payload = b"some data" 112 config = self.default_config() 113 resource_id = 5 114 self.do_single_write( 115 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 116 ) 117 118 @parameterized.expand( 119 [ 120 ("cpp"), 121 ("java"), 122 ("python"), 123 ] 124 ) 125 def test_medium_hdlc_escape_client_write(self, client_type): 126 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 127 self.skipTest("No legacy binary in use, skipping") 128 129 if not self.LEGACY_SERVER and ( 130 client_type == "java" or client_type == "python" 131 ): 132 self.skipTest("Java and Python legacy clients not yet set up") 133 134 payload = b"~" * 8731 135 config = self.default_config() 136 resource_id = 12345678 137 self.do_single_write( 138 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 139 ) 140 141 @parameterized.expand( 142 [ 143 ("cpp"), 144 ("java"), 145 ("python"), 146 ] 147 ) 148 def test_medium_random_data_client_write(self, client_type): 149 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 150 self.skipTest("No legacy binary in use, skipping") 151 152 if not self.LEGACY_SERVER and ( 153 client_type == "java" or client_type == "python" 154 ): 155 self.skipTest("Java and Python legacy clients not yet set up") 156 157 rng = random.Random(1533659510898) 158 payload = rng.randbytes(13713) 159 config = self.default_config() 160 resource_id = 12345678 161 self.do_single_write( 162 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 163 ) 164 165 @parameterized.expand( 166 [ 167 ("cpp"), 168 ("java"), 169 ("python"), 170 ] 171 ) 172 def test_single_byte_client_read(self, client_type): 173 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 174 self.skipTest("No legacy binary in use, skipping") 175 176 if not self.LEGACY_SERVER and ( 177 client_type == "java" or client_type == "python" 178 ): 179 self.skipTest("Java and Python legacy clients not yet set up") 180 181 payload = b"?" 182 config = self.default_config() 183 resource_id = 5 184 self.do_single_read( 185 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 186 ) 187 188 @parameterized.expand( 189 [ 190 ("cpp"), 191 ("java"), 192 ("python"), 193 ] 194 ) 195 def test_small_client_read(self, client_type): 196 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 197 self.skipTest("No legacy binary in use, skipping") 198 199 if not self.LEGACY_SERVER and ( 200 client_type == "java" or client_type == "python" 201 ): 202 self.skipTest("Java and Python legacy clients not yet set up") 203 204 payload = b"some data" 205 config = self.default_config() 206 resource_id = 5 207 self.do_single_read( 208 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 209 ) 210 211 @parameterized.expand( 212 [ 213 ("cpp"), 214 ("java"), 215 ("python"), 216 ] 217 ) 218 def test_medium_hdlc_escape_client_read(self, client_type): 219 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 220 self.skipTest("No legacy binary in use, skipping") 221 222 if self.LEGACY_SERVER: 223 self.skipTest("Legacy server has HDLC buffer sizing issues") 224 225 payload = b"~" * 8731 226 config = self.default_config() 227 resource_id = 5 228 self.do_single_read( 229 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 230 ) 231 232 @parameterized.expand( 233 [ 234 ("cpp"), 235 ("java"), 236 ("python"), 237 ] 238 ) 239 def test_medium_random_data_client_read(self, client_type): 240 if not (self.LEGACY_SERVER or self.LEGACY_CLIENT): 241 self.skipTest("No legacy binary in use, skipping") 242 243 if self.LEGACY_SERVER: 244 self.skipTest("Legacy server has HDLC buffer sizing issues") 245 246 rng = random.Random(1533659510898) 247 payload = rng.randbytes(13713) 248 config = self.default_config() 249 resource_id = 5 250 self.do_single_read( 251 "cpp", config, resource_id, payload, self.PROTOCOL_VERSION 252 ) 253 254 255class LegacyClientTransferIntegrationTests(LegacyTransferIntegrationTest): 256 r = runfiles.Create() 257 client_binary = r.Rlocation("pw_transfer_test_binaries/cpp_client_528098d5") 258 HARNESS_CONFIG = TransferIntegrationTestHarness.Config( 259 cpp_client_binary=client_binary, 260 server_port=_SERVER_PORT, 261 client_port=_CLIENT_PORT, 262 ) 263 LEGACY_CLIENT = True 264 265 266class LegacyServerTransferIntegrationTests(LegacyTransferIntegrationTest): 267 r = runfiles.Create() 268 server_binary = r.Rlocation("pw_transfer_test_binaries/server_528098d5") 269 HARNESS_CONFIG = TransferIntegrationTestHarness.Config( 270 server_binary=server_binary, 271 server_port=_SERVER_PORT, 272 client_port=_CLIENT_PORT, 273 ) 274 LEGACY_SERVER = True 275 276 277if __name__ == '__main__': 278 test_fixture.run_tests_for(LegacyClientTransferIntegrationTests) 279 test_fixture.run_tests_for(LegacyServerTransferIntegrationTests) 280