pw_transfer: Extend write tests
Extends transfer write integration test cases to cover write test cases
covered by the legacy test infrastructure.
Bug: b/234875234
Cq-Include-Trybots: luci.pigweed.try:pigweed-integration-transfer
Change-Id: I2da66290cd05a88528a3f4cf0b8b32ac196564a3
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/97580
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/pw_transfer/integration_test.cc b/pw_transfer/integration_test.cc
index 98b4255..f9a9200 100644
--- a/pw_transfer/integration_test.cc
+++ b/pw_transfer/integration_test.cc
@@ -338,12 +338,6 @@
static_assert(true, "Semicolons are required")
PW_TRANSFER_TEST_WRITE(Empty, "");
-PW_TRANSFER_TEST_WRITE(SingleByte_1, "\0");
-PW_TRANSFER_TEST_WRITE(SingleByte_2, "?");
-PW_TRANSFER_TEST_WRITE(SmallData, "hunter2");
-PW_TRANSFER_TEST_WRITE(LargeData, kData512);
-PW_TRANSFER_TEST_WRITE(HdlcEscape, kDataHdlcEscape);
-PW_TRANSFER_TEST_WRITE(VeryLargeData, kData8192);
} // namespace
} // namespace pw::transfer
diff --git a/pw_transfer/integration_test/cross_language_integration_test.py b/pw_transfer/integration_test/cross_language_integration_test.py
index 694758e..b1f16cf 100644
--- a/pw_transfer/integration_test/cross_language_integration_test.py
+++ b/pw_transfer/integration_test/cross_language_integration_test.py
@@ -39,7 +39,7 @@
import sys
import tempfile
import time
-from typing import List
+from typing import List, NamedTuple
import unittest
from google.protobuf import text_format
@@ -179,6 +179,13 @@
await self.wait_for_termination(timeout)
+class _TransferConfig(NamedTuple):
+ """A simple tuple to collect configs for test binaries."""
+ server: config_pb2.ServerConfig
+ client: config_pb2.ClientConfig
+ proxy: config_pb2.ProxyConfig
+
+
# TODO(b/232805936): Extend tests to use different resource IDs and do multiple
# reads/writes.
class PwTransferIntegrationTest(unittest.TestCase):
@@ -298,6 +305,77 @@
if self._proxy:
await self._proxy.terminate_and_wait(TIMEOUT)
+ @staticmethod
+ def _default_config() -> _TransferConfig:
+ """Returns a new transfer config with default options."""
+ return _TransferConfig(
+ config_pb2.ServerConfig(
+ chunk_size_bytes=216,
+ pending_bytes=32 * 1024,
+ chunk_timeout_seconds=5,
+ transfer_service_retries=4,
+ extend_window_divisor=32,
+ ),
+ config_pb2.ClientConfig(
+ max_retries=5,
+ initial_chunk_timeout_ms=4000,
+ chunk_timeout_ms=4000,
+ ),
+ text_format.Parse(
+ """
+ client_filter_stack: [
+ { hdlc_packetizer: {} },
+ { data_dropper: {rate: 0.01, seed: 1649963713563718435} }
+ ]
+
+ server_filter_stack: [
+ { hdlc_packetizer: {} },
+ { data_dropper: {rate: 0.01, seed: 1649963713563718436} }
+ ]""", config_pb2.ProxyConfig()))
+
+ def _do_single_write(self, client_type: str, config: _TransferConfig,
+ resource_id: int, data: bytes) -> None:
+ """Performs a single client-to-server write of the provided data."""
+ with tempfile.NamedTemporaryFile(
+ ) as f_payload, tempfile.NamedTemporaryFile() as f_server_output:
+ config.server.resources[resource_id].destination_paths.append(
+ f_server_output.name)
+ config.client.transfer_actions.append(
+ config_pb2.TransferAction(
+ resource_id=resource_id,
+ file_path=f_payload.name,
+ transfer_type=config_pb2.TransferAction.TransferType.
+ WRITE_TO_SERVER))
+
+ f_payload.write(data)
+ f_payload.flush() # Ensure contents are there to read!
+ asyncio.run(
+ self._perform_write(config.server, client_type, config.client,
+ config.proxy))
+ self.assertEqual(f_server_output.read(), data)
+
+ @parameterized.expand([
+ ("cpp"),
+ ("java"),
+ ("python"),
+ ])
+ def test_null_byte_client_write(self, client_type):
+ payload = b"\0"
+ config = self._default_config()
+ resource_id = 5
+ self._do_single_write(client_type, config, resource_id, payload)
+
+ @parameterized.expand([
+ ("cpp"),
+ ("java"),
+ ("python"),
+ ])
+ def test_single_byte_client_write(self, client_type):
+ payload = b"?"
+ config = self._default_config()
+ resource_id = 5
+ self._do_single_write(client_type, config, resource_id, payload)
+
@parameterized.expand([
("cpp"),
("java"),
@@ -305,48 +383,31 @@
])
def test_small_client_write(self, client_type):
payload = b"some data"
- server_config = config_pb2.ServerConfig(
- chunk_size_bytes=216,
- pending_bytes=32 * 1024,
- chunk_timeout_seconds=5,
- transfer_service_retries=4,
- extend_window_divisor=32,
- )
- client_config = config_pb2.ClientConfig(
- max_retries=5,
- initial_chunk_timeout_ms=10000,
- chunk_timeout_ms=4000,
- )
- proxy_config = text_format.Parse(
- """
- client_filter_stack: [
- { hdlc_packetizer: {} },
- { data_dropper: {rate: 0.01, seed: 1649963713563718435} }
- ]
+ config = self._default_config()
+ resource_id = 5
+ self._do_single_write(client_type, config, resource_id, payload)
- server_filter_stack: [
- { hdlc_packetizer: {} },
- { data_dropper: {rate: 0.01, seed: 1649963713563718436} }
- ]""", config_pb2.ProxyConfig())
+ @parameterized.expand([
+ ("cpp"),
+ ("java"),
+ ("python"),
+ ])
+ def test_medium_client_write(self, client_type):
+ payload = random.Random(67336391945).randbytes(512)
+ config = self._default_config()
+ resource_id = 5
+ self._do_single_write(client_type, config, resource_id, payload)
- resource_id = 12
- with tempfile.NamedTemporaryFile(
- ) as f_payload, tempfile.NamedTemporaryFile() as f_server_output:
- server_config.resources[resource_id].destination_paths.append(
- f_server_output.name)
- client_config.transfer_actions.append(
- config_pb2.TransferAction(
- resource_id=resource_id,
- file_path=f_payload.name,
- transfer_type=config_pb2.TransferAction.TransferType.
- WRITE_TO_SERVER))
-
- f_payload.write(payload)
- f_payload.flush() # Ensure contents are there to read!
- asyncio.run(
- self._perform_write(server_config, client_type, client_config,
- proxy_config))
- self.assertEqual(f_server_output.read(), payload)
+ @parameterized.expand([
+ ("cpp"),
+ ("java"),
+ ("python"),
+ ])
+ def test_large_hdlc_escape_client_write(self, client_type):
+ payload = b"~" * 98731
+ config = self._default_config()
+ resource_id = 5
+ self._do_single_write(client_type, config, resource_id, payload)
@parameterized.expand([
("cpp"),
@@ -383,23 +444,9 @@
payload = random.Random(1649963713563718437).randbytes(3 * 1024 * 1024)
resource_id = 12
- with tempfile.NamedTemporaryFile(
- ) as f_payload, tempfile.NamedTemporaryFile() as f_server_output:
- server_config.resources[resource_id].destination_paths.append(
- f_server_output.name)
- client_config.transfer_actions.append(
- config_pb2.TransferAction(
- resource_id=resource_id,
- file_path=f_payload.name,
- transfer_type=config_pb2.TransferAction.TransferType.
- WRITE_TO_SERVER))
- f_payload.write(payload)
- f_payload.flush() # Ensure contents are there to read!
- asyncio.run(
- self._perform_write(server_config, client_type, client_config,
- proxy_config))
- self.assertEqual(f_server_output.read(), payload)
+ config = _TransferConfig(server_config, client_config, proxy_config)
+ self._do_single_write(client_type, config, resource_id, payload)
@parameterized.expand([
("cpp"),
@@ -436,23 +483,9 @@
payload = random.Random(1649963713563718437).randbytes(3 * 1024 * 1024)
resource_id = 12
- with tempfile.NamedTemporaryFile(
- ) as f_payload, tempfile.NamedTemporaryFile() as f_server_output:
- server_config.resources[resource_id].destination_paths.append(
- f_server_output.name)
- client_config.transfer_actions.append(
- config_pb2.TransferAction(
- resource_id=resource_id,
- file_path=f_payload.name,
- transfer_type=config_pb2.TransferAction.TransferType.
- WRITE_TO_SERVER))
- f_payload.write(payload)
- f_payload.flush() # Ensure contents are there to read!
- asyncio.run(
- self._perform_write(server_config, client_type, client_config,
- proxy_config))
- self.assertEqual(f_server_output.read(), payload)
+ config = _TransferConfig(server_config, client_config, proxy_config)
+ self._do_single_write(client_type, config, resource_id, payload)
if __name__ == '__main__':
diff --git a/pw_transfer/py/tests/python_cpp_transfer_test.py b/pw_transfer/py/tests/python_cpp_transfer_test.py
index 97bd0d3..de2ffed 100755
--- a/pw_transfer/py/tests/python_cpp_transfer_test.py
+++ b/pw_transfer/py/tests/python_cpp_transfer_test.py
@@ -125,32 +125,6 @@
self.manager.write(28, b'')
self.assertEqual(self.get_content(28), b'')
- def test_write_single_byte(self) -> None:
- for _ in range(ITERATIONS):
- self.set_content(29, 'junk')
- self.manager.write(29, b'$')
- self.assertEqual(self.get_content(29), b'$')
-
- def test_write_small_amount_of_data(self) -> None:
- for _ in range(ITERATIONS):
- self.set_content(30, 'junk')
- self.manager.write(30, b'file transfer')
- self.assertEqual(self.get_content(30), b'file transfer')
-
- def test_write_large_amount_of_data(self) -> None:
- for _ in range(ITERATIONS):
- self.set_content(31, 'junk')
- self.manager.write(31, b'*' * 512)
- self.assertEqual(self.get_content(31), b'*' * 512)
-
- def test_write_very_large_amount_of_data(self) -> None:
- for _ in range(ITERATIONS):
- self.set_content(32, 'junk')
-
- # Larger than the transfer service's configured pending_bytes.
- self.manager.write(32, _DATA_4096B)
- self.assertEqual(self.get_content(32), _DATA_4096B)
-
def test_write_string(self) -> None:
for _ in range(ITERATIONS):
# Write a string instead of bytes.
@@ -274,11 +248,6 @@
self._outgoing_filter.dropped_packet_probability = 0.1
self.read_large_amount_of_data()
- def test_packet_loss_during_write(self) -> None:
- self._incoming_filter.dropped_packet_probability = 0.1
- self._outgoing_filter.dropped_packet_probability = 0.1
- self.write_very_large_amount_of_data()
-
def test_packet_delay_during_read(self) -> None:
self._incoming_filter.delayed_packet_probability = 0.1
self._outgoing_filter.delayed_packet_probability = 0.1
@@ -306,11 +275,6 @@
self._outgoing_filter.out_of_order_probability = 0.01
self.read_large_amount_of_data()
- def test_packet_reordering_during_write(self) -> None:
- self._incoming_filter.out_of_order_probability = 0.05
- self._outgoing_filter.out_of_order_probability = 0.05
- self.write_very_large_amount_of_data()
-
def _main(test_server_command: List[str], port: int,
unittest_args: List[str]) -> None: