OSDN Git Service

L2CAP: Add test script for pts test
authorTed Wang <tedwang@google.com>
Wed, 4 Dec 2019 11:46:09 +0000 (19:46 +0800)
committerTed Wang <tedwang@google.com>
Fri, 6 Dec 2019 06:56:48 +0000 (14:56 +0800)
*Add following PTS test script:
  L2CAP/COS/CED/BV-01-C
  L2CAP/COS/CED/BV-03-C
  L2CAP/COS/CED/BV-04-C
  L2CAP/COS/CED/BV-05-C
  L2CAP/COS/CED/BV-07-C
  L2CAP/COS/CED/BV-08-C
  L2CAP/COS/CED/BV-09-C
  L2CAP/COS/CED/BV-11-C
  L2CAP/COS/CED/BI-01-C
  L2CAP/COS/CFD/BV-03-C
  L2CAP/COS/CFD/BV-08-C
  L2CAP/ERM/BI-01-C

*Add method FetchConnectionClose in facade to monitor connection closed

Test: run_pts_l2cap.sh

Change-Id: Ie1d6a84ada8344321307d215f304a8cb34ea2ad6

gd/cert/gd_device.py
gd/cert/pts_base_test.py [new file with mode: 0644]
gd/cert/run_pts_l2cap.sh [new file with mode: 0755]
gd/l2cap/classic/cert/pts_l2cap_test.py [new file with mode: 0644]
gd/l2cap/classic/facade.cc
gd/l2cap/classic/facade.proto
gd/l2cap/pts/pts.json [new file with mode: 0644]
gd/l2cap/pts/pts_l2cap_testcase [new file with mode: 0644]
gd/l2cap/pts/run_pts_l2cap.sh [new file with mode: 0755]

index 8f84dbc..fa9d24f 100644 (file)
@@ -85,3 +85,4 @@ class GdDevice(GdDeviceBase):
         self.hci_classic_security.command_complete_stream = EventStream(self.hci_classic_security.FetchCommandCompleteEvent)
         self.l2cap.packet_stream = EventStream(self.l2cap.FetchL2capData)
         self.l2cap.connection_complete_stream = EventStream(self.l2cap.FetchConnectionComplete)
+        self.l2cap.connection_close_stream = EventStream(self.l2cap.FetchConnectionClose)
diff --git a/gd/cert/pts_base_test.py b/gd/cert/pts_base_test.py
new file mode 100644 (file)
index 0000000..07154ea
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.base_test import BaseTestClass
+
+import importlib
+import logging
+import os
+import signal
+import sys
+import subprocess
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+
+sys.path.append(ANDROID_BUILD_TOP + '/out/soong/.intermediates/system/bt/gd/BluetoothFacadeAndCertGeneratedStub_py/gen')
+
+
+class PTSBaseTestClass(BaseTestClass):
+    def __init__(self, configs):
+        BaseTestClass.__init__(self, configs)
+
+        gd_devices = self.controller_configs.get("GdDevice")
+
+        self.register_controller(
+            importlib.import_module('cert.gd_device'),
+            builtin=True)
diff --git a/gd/cert/run_pts_l2cap.sh b/gd/cert/run_pts_l2cap.sh
new file mode 100755 (executable)
index 0000000..4539c65
--- /dev/null
@@ -0,0 +1,5 @@
+#! /bin/bash
+
+# For bluetooth_packets_python3
+export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
+python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/l2cap/classic/cert/pts_l2cap_test.py b/gd/l2cap/classic/cert/pts_l2cap_test.py
new file mode 100644 (file)
index 0000000..57ccb36
--- /dev/null
@@ -0,0 +1,190 @@
+from cert.pts_base_test import PTSBaseTestClass
+from facade import common_pb2
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from l2cap.classic import facade_pb2 as l2cap_facade_pb2
+from google.protobuf import empty_pb2
+
+from datetime import timedelta
+import time
+class PTSL2capTest(PTSBaseTestClass):
+    def setup_test(self):
+        self.device_under_test = self.gd_devices[0]
+
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value('L2CAP'),
+            )
+        )
+
+        self.device_under_test.wait_channel_ready()
+
+        dut_address = self.device_under_test.controller_read_only_property.ReadLocalAddress(empty_pb2.Empty()).address
+        pts_address = self.controller_configs.get('pts_address').lower()
+        self.device_under_test.address = dut_address
+
+        self.dut_address = common_pb2.BluetoothAddress(
+            address=self.device_under_test.address)
+        self.pts_address = common_pb2.BluetoothAddress(
+            address=str.encode(pts_address))
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest()
+        )
+
+    def _pending_connection_complete(self, timeout=30):
+        dut_connection_stream = self.device_under_test.l2cap.connection_complete_stream
+        dut_connection_stream.subscribe()
+        dut_connection_stream.assert_event_occurs(
+          lambda device : device.remote.address == self.pts_address.address,
+          timeout=timedelta(seconds=timeout)
+        )
+        dut_connection_stream.unsubscribe()
+
+    def _pending_connection_close(self, timeout=30):
+        dut_connection_close_stream = self.device_under_test.l2cap.connection_close_stream
+        dut_connection_close_stream.subscribe()
+        dut_connection_close_stream.assert_event_occurs(
+          lambda device : device.remote.address == self.pts_address.address,
+          timeout=timedelta(seconds=timeout)
+        )
+        dut_connection_close_stream.unsubscribe()
+
+    def test_L2CAP_COS_CED_BV_01_C(self):
+        """
+        L2CAP/COS/CED/BV-01-C [Request Connection]
+        Verify that the IUT is able to request the connection establishment for an L2CAP data channel and
+        initiate the configuration procedure.
+        """
+        psm = 1
+        self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
+        self._pending_connection_complete()
+        self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_03_C(self):
+        """
+        L2CAP/COS/CED/BV-03-C [Send Data]
+        Verify that the IUT is able to send DATA.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'*34))
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_04_C(self):
+        """
+        L2CAP/COS/CED/BV-04-C [Disconnect]
+        Verify that the IUT is able to disconnect the data channel.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        time.sleep(2)
+        self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_05_C(self):
+        """
+        L2CAP/COS/CED/BV-05-C [Accept Connection]
+        Verify that the IUT is able to disconnect the data channel.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_07_C(self):
+        """
+        L2CAP/COS/CED/BV-07-C [Accept Disconnect]
+        Verify that the IUT is able to respond to the request to disconnect the data channel.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_08_C(self):
+        """
+        L2CAP/COS/CED/BV-08-C [Disconnect on Timeout]
+        Verify that the IUT disconnects the data channel and shuts down this channel if no response occurs
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+
+        time.sleep(120)
+
+    def test_L2CAP_COS_CED_BV_09_C(self):
+        """
+        L2CAP/COS/CED/BV-09-C [Receive Multi-Command Packet]
+        Verify that the IUT is able to receive more than one signaling command in one L2CAP packet.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BV_11_C(self):
+        """
+        L2CAP/COS/CED/BV-11-C [Configure MTU Size]
+        Verify that the IUT is able to configure the supported MTU size
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CED_BI_01_C(self):
+        """
+        L2CAP/COS/CED/BI-01-C [Reject Unknown Command]
+        Verify that the IUT rejects an unknown signaling command.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_complete()
+        time.sleep(5)
+
+    def test_L2CAP_COS_CFD_BV_03_C(self):
+        """
+        L2CAP/COS/CFD/BV-03-C [Send Requested Options]
+        Verify that the IUT can receive a configuration request with no options and send the requested
+        options to the Lower Tester
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC))
+        self._pending_connection_close()
+
+    def test_L2CAP_COS_CFD_BV_08_C(self):
+        """
+        L2CAP/COS/CFD/BV-08-C [Non-blocking Config Response]
+        Verify that the IUT does not block transmitting L2CAP_ConfigRsp while waiting for L2CAP_ConfigRsp
+        from the Lower Tester.
+        """
+        psm = 1
+        self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.pts_address, psm=psm))
+        self._pending_connection_complete()
+        self.device_under_test.l2cap.CloseChannel(l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+        self._pending_connection_close()
+
+
+    def test_L2CAP_ERM_BI_01_C(self):
+        """
+        L2CAP/ERM/BI-01-C [S-Frame [REJ] Lost or Corrupted]
+        Verify the IUT can handle receipt of an S-=frame [RR] Poll = 1 if the S-frame [REJ] sent from the IUT
+        is lost.
+        """
+        psm = 1
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+            psm=psm, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+        self._pending_connection_complete()
+        self._pending_connection_close(timeout=60)
index 64f3810..83073f7 100644 (file)
@@ -64,6 +64,22 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
     return connection_complete_stream_.HandleRequest(context, request, writer);
   }
 
+  class ConnectionCloseCallback : public grpc::GrpcEventStreamCallback<ConnectionCloseEvent, ConnectionCloseEvent> {
+   public:
+    void OnWriteResponse(ConnectionCloseEvent* response, const ConnectionCloseEvent& event) override {
+      response->CopyFrom(event);
+    }
+
+  } connection_close_callback_;
+  ::bluetooth::grpc::GrpcEventStream<ConnectionCloseEvent, ConnectionCloseEvent> connection_close_stream_{
+      &connection_close_callback_};
+
+  ::grpc::Status FetchConnectionClose(::grpc::ServerContext* context,
+                                      const ::bluetooth::facade::EventStreamRequest* request,
+                                      ::grpc::ServerWriter<classic::ConnectionCloseEvent>* writer) override {
+    return connection_close_stream_.HandleRequest(context, request, writer);
+  }
+
   ::grpc::Status Connect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
                          ::google::protobuf::Empty* response) override {
     auto fixed_channel_manager = l2cap_layer_->GetFixedChannelManager();
@@ -108,6 +124,17 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
     return ::grpc::Status::OK;
   }
 
+  ::grpc::Status CloseChannel(::grpc::ServerContext* context,
+                              const ::bluetooth::l2cap::classic::CloseChannelRequest* request,
+                              ::google::protobuf::Empty* response) override {
+    auto psm = request->psm();
+    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    dynamic_channel_helper_map_[psm]->disconnect();
+    return ::grpc::Status::OK;
+  }
+
   ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
                                 ::grpc::ServerWriter<classic::L2capPacket>* writer) override {
     return l2cap_stream_.HandleRequest(context, request, writer);
@@ -218,6 +245,10 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
           common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
     }
 
+    void disconnect() {
+      channel_->Close();
+    }
+
     void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
                                                 std::unique_ptr<DynamicChannelService> service) {}
 
@@ -226,6 +257,15 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
       event.mutable_remote()->set_address(channel->GetDevice().ToString());
       facade_service_->connection_complete_stream_.OnIncomingEvent(event);
       channel_ = std::move(channel);
+      channel_->RegisterOnCloseCallback(
+          handler_, common::Bind(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this)));
+    }
+
+    void on_close_callback(hci::ErrorCode errorCode) {
+      ConnectionCloseEvent event;
+      event.mutable_remote()->set_address(channel_->GetDevice().ToString());
+      event.set_reason(static_cast<uint32_t>(errorCode));
+      facade_service_->connection_close_stream_.OnIncomingEvent(event);
     }
 
     void on_connect_fail(DynamicChannelManager::ConnectionResult result) {}
index ab2a911..535dca9 100644 (file)
@@ -12,12 +12,15 @@ service L2capClassicModuleFacade {
   rpc FetchConnectionComplete(facade.EventStreamRequest) returns (stream ConnectionCompleteEvent) {
     // Testing Android Bluetooth stack only. Optional for other stack.
   }
+  rpc FetchConnectionClose(facade.EventStreamRequest) returns (stream ConnectionCloseEvent) {
+    // Testing Android Bluetooth stack only. Optional for other stack.
+  }
   rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
   rpc OpenChannel(OpenChannelRequest) returns (google.protobuf.Empty) {}
+  rpc CloseChannel(CloseChannelRequest) returns (google.protobuf.Empty) {}
   rpc ConfigureChannel(ConfigureChannelRequest) returns (google.protobuf.Empty) {}
   rpc SendL2capPacket(L2capPacket) returns (SendL2capPacketResult) {}
   rpc FetchL2capData(facade.EventStreamRequest) returns (stream L2capPacket) {}
-  rpc RegisterDynamicChannel(RegisterDynamicChannelRequest) returns (google.protobuf.Empty) {}
   rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
   rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
 }
@@ -30,6 +33,11 @@ message ConnectionCompleteEvent {
   facade.BluetoothAddress remote = 1;
 }
 
+message ConnectionCloseEvent {
+  facade.BluetoothAddress remote = 1;
+  uint32 reason = 2;
+}
+
 enum RetransmissionFlowControlMode {
   BASIC = 0;
   ERTM = 3;
@@ -47,8 +55,7 @@ message ConfigureChannelRequest {
 }
 
 message CloseChannelRequest {
-  facade.BluetoothAddress remote = 1;
-  uint32 cid = 2;
+  uint32 psm = 1;
 }
 
 enum ChannelSignalEventType {
@@ -77,10 +84,6 @@ message L2capPacket {
   bytes payload = 3;
 }
 
-message RegisterDynamicChannelRequest {
-  uint32 psm = 1;
-}
-
 message SetEnableDynamicChannelRequest {
   uint32 psm = 1;
   bool enable = 2;
diff --git a/gd/l2cap/pts/pts.json b/gd/l2cap/pts/pts.json
new file mode 100644 (file)
index 0000000..76a27be
--- /dev/null
@@ -0,0 +1,34 @@
+
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "PTS l2cap layer test",
+            "name": "PTSL2CAP",
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8899",
+                    "grpc_root_server_port": "8897",
+                    "signal_port": "8895",
+                    "label": "stack_under_test",
+                    "serial_number": "DUT",
+                    "cmd":
+                    [
+                        "adb",
+                        "-s",
+                        "$(serial_number)",
+                        "shell",
+                        "/system/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
+                        "--signal-port=$(signal_port)"
+                    ]
+                }
+            ],
+            "pts_address": "PTS"
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/l2cap/pts/pts_l2cap_testcase b/gd/l2cap/pts/pts_l2cap_testcase
new file mode 100644 (file)
index 0000000..a0ae40e
--- /dev/null
@@ -0,0 +1 @@
+PTSL2capTest
diff --git a/gd/l2cap/pts/run_pts_l2cap.sh b/gd/l2cap/pts/run_pts_l2cap.sh
new file mode 100755 (executable)
index 0000000..9165f58
--- /dev/null
@@ -0,0 +1,3 @@
+#! /bin/bash
+
+act.py -c $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/l2cap/pts/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd