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: