Performance testing: Add assertions

Add assertions on a timedelta object.

Also add a benchmark to make sure we can send at least a certain number
of packets

Test: cert/run --host L2capPerformanceTest
Tag: #gd-refactor
Bug: 157613312
Change-Id: I222c9d11de27ae473c40fa89401b11f37c9e2eed
diff --git a/system/gd/cert/performance_test_logger.py b/system/gd/cert/performance_test_logger.py
index 20bb638..006bbd6 100644
--- a/system/gd/cert/performance_test_logger.py
+++ b/system/gd/cert/performance_test_logger.py
@@ -58,7 +58,7 @@
         intervals = []
         for i in range(len(self.start_interval_points[label])):
             interval = self.end_interval_points[label][i] - self.start_interval_points[label][i]
-            intervals.append(interval.seconds * 1000 + interval.microseconds / 1000)
+            intervals.append(interval)
         return intervals
 
     def dump_intervals(self):
diff --git a/system/gd/cert/truth.py b/system/gd/cert/truth.py
index 54f41ec..eba6be3 100644
--- a/system/gd/cert/truth.py
+++ b/system/gd/cert/truth.py
@@ -130,10 +130,21 @@
         assert_false(self._value, "")
 
 
+class TimeDeltaSubject(ObjectSubject):
+
+    def __init__(self, value):
+        super().__init__(value)
+
+    def isWithin(self, time_bound):
+        assert_true(self._value < time_bound, "")
+
+
 def assertThat(subject):
     if type(subject) is bool:
         return BooleanSubject(subject)
     elif isinstance(subject, IEventStream):
         return EventStreamSubject(subject)
+    elif isinstance(subject, timedelta):
+        return TimeDeltaSubject(subject)
     else:
         return ObjectSubject(subject)
diff --git a/system/gd/l2cap/classic/cert/l2cap_performance_test.py b/system/gd/l2cap/classic/cert/l2cap_performance_test.py
index f263e76..9c387ee 100644
--- a/system/gd/l2cap/classic/cert/l2cap_performance_test.py
+++ b/system/gd/l2cap/classic/cert/l2cap_performance_test.py
@@ -36,6 +36,9 @@
         super().teardown_test()
 
     def _basic_mode_tx(self, mtu, packets):
+        """
+        Send the specified number of packets and return the time interval in ms.
+        """
         self._setup_link_from_cert()
 
         (dut_channel, cert_channel) = self._open_channel_from_cert()
@@ -47,7 +50,28 @@
         self.performance_test_logger.end_interval("TX")
 
         duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
-        self.log.info("Duration: %d" % duration)
+        self.log.info("Duration: %s" % str(duration))
+
+        return duration
+
+    def _basic_mode_tx_fixed_interval(self, mtu, interval=timedelta(seconds=10), batch_size=20):
+        """
+        Send packets as much as possible over a certain interval, and return the
+        number of packets sent
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        start_time = datetime.now()
+        end_time = start_time + interval
+        packets_sent = 0
+        while datetime.now() < end_time:
+            for _ in range(batch_size):
+                dut_channel.send(b'a' * mtu)
+            packets_sent += batch_size
+            assertThat(cert_channel).emits(L2capMatchers.Data(b'a' * mtu), at_least_times=batch_size)
+
+        return packets_sent
 
     def _basic_mode_rx(self, mtu, packets):
         self._setup_link_from_cert()
@@ -63,9 +87,12 @@
         self.performance_test_logger.end_interval("RX")
 
         duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
-        self.log.info("Duration: %d" % duration)
+        self.log.info("Duration: %s" % str(duration))
 
     def _ertm_mode_tx(self, mtu, packets, tx_window_size=10):
+        """
+        Send the specified number of packets and return the time interval in ms.
+        """
         # Make sure that number of packets is a multiple of tx_window_size
         packets = packets // tx_window_size * tx_window_size
         # For ERTM TX test, we have to do it sequentially because cert needs to ack
@@ -89,7 +116,9 @@
         self.performance_test_logger.end_interval("TX")
 
         duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
-        self.log.info("Duration: %d" % duration)
+        self.log.info("Duration: %s" % str(duration))
+
+        return duration
 
     def _ertm_mode_rx(self, mtu, packets, tx_window_size=10):
         # Make sure that number of packets is a multiple of tx_window_size
@@ -115,16 +144,19 @@
         self.performance_test_logger.end_interval("RX")
 
         duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
-        self.log.info("Duration: %d" % duration)
+        self.log.info("Duration: %s" % str(duration))
 
     def test_basic_mode_tx_672_100(self):
-        self._basic_mode_tx(672, 100)
+        duration = self._basic_mode_tx(672, 100)
+        assertThat(duration).isWithin(timedelta(seconds=2))
 
     def test_basic_mode_tx_100_100(self):
-        self._basic_mode_tx(100, 100)
+        duration = self._basic_mode_tx(100, 100)
+        assertThat(duration).isWithin(timedelta(seconds=2))
 
     def test_ertm_mode_tx_672_100(self):
-        self._ertm_mode_tx(672, 100)
+        duration = self._ertm_mode_tx(672, 100)
+        assertThat(duration).isWithin(timedelta(seconds=5))
 
     def test_basic_mode_rx_672_100(self):
         self._basic_mode_rx(672, 100)
@@ -145,5 +177,11 @@
             assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(data))
             self.performance_test_logger.end_interval("RX")
         duration = self.performance_test_logger.get_duration_of_intervals("RX")
-        mean = sum(duration) / len(duration)
-        self.log.info("Mean: %d" % mean)
+        mean = sum(duration, timedelta()) / len(duration)
+        self.log.info("Mean: %s" % str(mean))
+
+    def test_basic_mode_number_of_packets_10_seconds_672(self):
+        number_packets = self._basic_mode_tx_fixed_interval(672)
+        # Requiring that 500 packets (20ms period on average) are sent
+        self.log.info("Packets sent: %d" % number_packets)
+        assertThat(number_packets > 500).isTrue()