| #!/usr/bin/python3.4 |
| # |
| # Copyright 2017 - 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. |
| |
| import string |
| import time |
| |
| from acts import asserts |
| from acts.test_decorators import test_tracker_info |
| from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest |
| from acts.test_utils.wifi.aware import aware_const as aconsts |
| from acts.test_utils.wifi.aware import aware_test_utils as autils |
| from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest |
| |
| |
| class MessageTest(AwareBaseTest): |
| """Set of tests for Wi-Fi Aware L2 (layer 2) message exchanges.""" |
| |
| # configuration parameters used by tests |
| PAYLOAD_SIZE_MIN = 0 |
| PAYLOAD_SIZE_TYPICAL = 1 |
| PAYLOAD_SIZE_MAX = 2 |
| |
| NUM_MSGS_NO_QUEUE = 10 |
| NUM_MSGS_QUEUE_DEPTH_MULT = 2 # number of messages = mult * queue depth |
| |
| def create_msg(self, caps, payload_size, id): |
| """Creates a message string of the specified size containing the input id. |
| |
| Args: |
| caps: Device capabilities. |
| payload_size: The size of the message to create - min (null or empty |
| message), typical, max (based on device capabilities). Use |
| the PAYLOAD_SIZE_xx constants. |
| id: Information to include in the generated message (or None). |
| |
| Returns: A string of the requested size, optionally containing the id. |
| """ |
| if payload_size == self.PAYLOAD_SIZE_MIN: |
| # arbitrarily return a None or an empty string (equivalent messages) |
| return None if id % 2 == 0 else "" |
| elif payload_size == self.PAYLOAD_SIZE_TYPICAL: |
| return "*** ID=%d ***" % id + string.ascii_uppercase |
| else: # PAYLOAD_SIZE_MAX |
| return "*** ID=%4d ***" % id + "M" * ( |
| caps[aconsts.CAP_MAX_SERVICE_SPECIFIC_INFO_LEN] - 15) |
| |
| def create_config(self, is_publish, extra_diff=None): |
| """Create a base configuration based on input parameters. |
| |
| Args: |
| is_publish: True for publish, False for subscribe sessions. |
| extra_diff: String to add to service name: allows differentiating |
| discovery sessions. |
| |
| Returns: |
| publish discovery configuration object. |
| """ |
| config = {} |
| if is_publish: |
| config[ |
| aconsts. |
| DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.PUBLISH_TYPE_UNSOLICITED |
| else: |
| config[ |
| aconsts. |
| DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.SUBSCRIBE_TYPE_PASSIVE |
| config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceX" + ( |
| extra_diff if extra_diff is not None else "") |
| return config |
| |
| def prep_message_exchange(self, extra_diff=None): |
| """Creates a discovery session (publish and subscribe), and waits for |
| service discovery - at that point the sessions are ready for message |
| exchange. |
| |
| Args: |
| extra_diff: String to add to service name: allows differentiating |
| discovery sessions. |
| """ |
| p_dut = self.android_devices[0] |
| p_dut.pretty_name = "Publisher" |
| s_dut = self.android_devices[1] |
| s_dut.pretty_name = "Subscriber" |
| |
| # if differentiating (multiple) sessions then should decorate events with id |
| use_id = extra_diff is not None |
| |
| # Publisher+Subscriber: attach and wait for confirmation |
| p_id = p_dut.droid.wifiAwareAttach(False, None, use_id) |
| autils.wait_for_event( |
| p_dut, aconsts.EVENT_CB_ON_ATTACHED |
| if not use_id else autils.decorate_event( |
| aconsts.EVENT_CB_ON_ATTACHED, p_id)) |
| time.sleep(self.device_startup_offset) |
| s_id = s_dut.droid.wifiAwareAttach(False, None, use_id) |
| autils.wait_for_event( |
| s_dut, aconsts.EVENT_CB_ON_ATTACHED |
| if not use_id else autils.decorate_event( |
| aconsts.EVENT_CB_ON_ATTACHED, s_id)) |
| |
| # Publisher: start publish and wait for confirmation |
| p_disc_id = p_dut.droid.wifiAwarePublish( |
| p_id, self.create_config(True, extra_diff=extra_diff), use_id) |
| autils.wait_for_event( |
| p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED |
| if not use_id else autils.decorate_event( |
| aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id)) |
| |
| # Subscriber: start subscribe and wait for confirmation |
| s_disc_id = s_dut.droid.wifiAwareSubscribe( |
| s_id, self.create_config(False, extra_diff=extra_diff), use_id) |
| autils.wait_for_event( |
| s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED |
| if not use_id else autils.decorate_event( |
| aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id)) |
| |
| # Subscriber: wait for service discovery |
| discovery_event = autils.wait_for_event( |
| s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED |
| if not use_id else autils.decorate_event( |
| aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id)) |
| peer_id_on_sub = discovery_event["data"][ |
| aconsts.SESSION_CB_KEY_PEER_ID] |
| |
| return { |
| "p_dut": p_dut, |
| "s_dut": s_dut, |
| "p_id": p_id, |
| "s_id": s_id, |
| "p_disc_id": p_disc_id, |
| "s_disc_id": s_disc_id, |
| "peer_id_on_sub": peer_id_on_sub |
| } |
| |
| def run_message_no_queue(self, payload_size): |
| """Validate L2 message exchange between publisher & subscriber with no |
| queueing - i.e. wait for an ACK on each message before sending the next |
| message. |
| |
| Args: |
| payload_size: min, typical, or max (PAYLOAD_SIZE_xx). |
| """ |
| discovery_info = self.prep_message_exchange() |
| p_dut = discovery_info["p_dut"] |
| s_dut = discovery_info["s_dut"] |
| p_disc_id = discovery_info["p_disc_id"] |
| s_disc_id = discovery_info["s_disc_id"] |
| peer_id_on_sub = discovery_info["peer_id_on_sub"] |
| |
| for i in range(self.NUM_MSGS_NO_QUEUE): |
| msg = self.create_msg(s_dut.aware_capabilities, payload_size, i) |
| msg_id = self.get_next_msg_id() |
| s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id, |
| msg, 0) |
| tx_event = autils.wait_for_event( |
| s_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT) |
| rx_event = autils.wait_for_event( |
| p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED) |
| asserts.assert_equal( |
| msg_id, tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID], |
| "Subscriber -> Publisher message ID corrupted") |
| autils.assert_equal_strings( |
| msg, |
| rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], |
| "Subscriber -> Publisher message %d corrupted" % i) |
| |
| peer_id_on_pub = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID] |
| for i in range(self.NUM_MSGS_NO_QUEUE): |
| msg = self.create_msg(s_dut.aware_capabilities, payload_size, |
| 1000 + i) |
| msg_id = self.get_next_msg_id() |
| p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id, |
| msg, 0) |
| tx_event = autils.wait_for_event( |
| p_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT) |
| rx_event = autils.wait_for_event( |
| s_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED) |
| asserts.assert_equal( |
| msg_id, tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID], |
| "Publisher -> Subscriber message ID corrupted") |
| autils.assert_equal_strings( |
| msg, |
| rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], |
| "Publisher -> Subscriber message %d corrupted" % i) |
| |
| # verify there are no more events |
| time.sleep(autils.EVENT_TIMEOUT) |
| autils.verify_no_more_events(p_dut, timeout=0) |
| autils.verify_no_more_events(s_dut, timeout=0) |
| |
| def wait_for_messages(self, |
| tx_msgs, |
| tx_msg_ids, |
| tx_disc_id, |
| rx_disc_id, |
| tx_dut, |
| rx_dut, |
| are_msgs_empty=False): |
| """Validate that all expected messages are transmitted correctly and |
| received as expected. Method is called after the messages are sent into |
| the transmission queue. |
| |
| Note: that message can be transmitted and received out-of-order (which is |
| acceptable and the method handles that correctly). |
| |
| Args: |
| tx_msgs: dictionary of transmitted messages |
| tx_msg_ids: dictionary of transmitted message ids |
| tx_disc_id: transmitter discovery session id (None for no decoration) |
| rx_disc_id: receiver discovery session id (None for no decoration) |
| tx_dut: transmitter device |
| rx_dut: receiver device |
| are_msgs_empty: True if the messages are None or empty (changes dup detection) |
| |
| Returns: the peer ID from any of the received messages |
| """ |
| # peer id on receiver |
| peer_id_on_rx = None |
| |
| # wait for all messages to be transmitted |
| still_to_be_tx = len(tx_msg_ids) |
| while still_to_be_tx != 0: |
| tx_event = autils.wait_for_event( |
| tx_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT |
| if tx_disc_id is None else autils.decorate_event( |
| aconsts.SESSION_CB_ON_MESSAGE_SENT, tx_disc_id)) |
| tx_msg_id = tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID] |
| tx_msg_ids[tx_msg_id] = tx_msg_ids[tx_msg_id] + 1 |
| if tx_msg_ids[tx_msg_id] == 1: |
| still_to_be_tx = still_to_be_tx - 1 |
| |
| # check for any duplicate transmit notifications |
| asserts.assert_equal( |
| len(tx_msg_ids), sum(tx_msg_ids.values()), |
| "Duplicate transmit message IDs: %s" % tx_msg_ids) |
| |
| # wait for all messages to be received |
| still_to_be_rx = len(tx_msg_ids) |
| while still_to_be_rx != 0: |
| rx_event = autils.wait_for_event( |
| rx_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED |
| if rx_disc_id is None else autils.decorate_event( |
| aconsts.SESSION_CB_ON_MESSAGE_RECEIVED, rx_disc_id)) |
| peer_id_on_rx = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID] |
| if are_msgs_empty: |
| still_to_be_rx = still_to_be_rx - 1 |
| else: |
| rx_msg = rx_event["data"][ |
| aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] |
| asserts.assert_true( |
| rx_msg in tx_msgs, |
| "Received a message we did not send!? -- '%s'" % rx_msg) |
| tx_msgs[rx_msg] = tx_msgs[rx_msg] + 1 |
| if tx_msgs[rx_msg] == 1: |
| still_to_be_rx = still_to_be_rx - 1 |
| |
| # check for any duplicate received messages |
| if not are_msgs_empty: |
| asserts.assert_equal( |
| len(tx_msgs), sum(tx_msgs.values()), |
| "Duplicate transmit messages: %s" % tx_msgs) |
| |
| return peer_id_on_rx |
| |
| def run_message_with_queue(self, payload_size): |
| """Validate L2 message exchange between publisher & subscriber with |
| queueing - i.e. transmit all messages and then wait for ACKs. |
| |
| Args: |
| payload_size: min, typical, or max (PAYLOAD_SIZE_xx). |
| """ |
| discovery_info = self.prep_message_exchange() |
| p_dut = discovery_info["p_dut"] |
| s_dut = discovery_info["s_dut"] |
| p_disc_id = discovery_info["p_disc_id"] |
| s_disc_id = discovery_info["s_disc_id"] |
| peer_id_on_sub = discovery_info["peer_id_on_sub"] |
| |
| msgs = {} |
| msg_ids = {} |
| for i in range( |
| self.NUM_MSGS_QUEUE_DEPTH_MULT * s_dut. |
| aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): |
| msg = self.create_msg(s_dut.aware_capabilities, payload_size, i) |
| msg_id = self.get_next_msg_id() |
| msgs[msg] = 0 |
| msg_ids[msg_id] = 0 |
| s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id, |
| msg, 0) |
| peer_id_on_pub = self.wait_for_messages( |
| msgs, msg_ids, None, None, s_dut, p_dut, |
| payload_size == self.PAYLOAD_SIZE_MIN) |
| |
| msgs = {} |
| msg_ids = {} |
| for i in range( |
| self.NUM_MSGS_QUEUE_DEPTH_MULT * p_dut. |
| aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): |
| msg = self.create_msg(p_dut.aware_capabilities, payload_size, |
| 1000 + i) |
| msg_id = self.get_next_msg_id() |
| msgs[msg] = 0 |
| msg_ids[msg_id] = 0 |
| p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id, |
| msg, 0) |
| self.wait_for_messages(msgs, msg_ids, None, None, p_dut, s_dut, |
| payload_size == self.PAYLOAD_SIZE_MIN) |
| |
| # verify there are no more events |
| time.sleep(autils.EVENT_TIMEOUT) |
| autils.verify_no_more_events(p_dut, timeout=0) |
| autils.verify_no_more_events(s_dut, timeout=0) |
| |
| def run_message_multi_session_with_queue(self, payload_size): |
| """Validate L2 message exchange between publishers & subscribers with |
| queueing - i.e. transmit all messages and then wait for ACKs. Uses 2 |
| discovery sessions running concurrently and validates that messages |
| arrive at the correct destination. |
| |
| Args: |
| payload_size: min, typical, or max (PAYLOAD_SIZE_xx) |
| """ |
| discovery_info1 = self.prep_message_exchange(extra_diff="-111") |
| p_dut = discovery_info1["p_dut"] # same for both sessions |
| s_dut = discovery_info1["s_dut"] # same for both sessions |
| p_disc_id1 = discovery_info1["p_disc_id"] |
| s_disc_id1 = discovery_info1["s_disc_id"] |
| peer_id_on_sub1 = discovery_info1["peer_id_on_sub"] |
| |
| discovery_info2 = self.prep_message_exchange(extra_diff="-222") |
| p_disc_id2 = discovery_info2["p_disc_id"] |
| s_disc_id2 = discovery_info2["s_disc_id"] |
| peer_id_on_sub2 = discovery_info2["peer_id_on_sub"] |
| |
| msgs1 = {} |
| msg_ids1 = {} |
| msgs2 = {} |
| msg_ids2 = {} |
| for i in range( |
| self.NUM_MSGS_QUEUE_DEPTH_MULT * s_dut. |
| aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): |
| msg1 = self.create_msg(s_dut.aware_capabilities, payload_size, i) |
| msg_id1 = self.get_next_msg_id() |
| msgs1[msg1] = 0 |
| msg_ids1[msg_id1] = 0 |
| s_dut.droid.wifiAwareSendMessage(s_disc_id1, peer_id_on_sub1, |
| msg_id1, msg1, 0) |
| msg2 = self.create_msg(s_dut.aware_capabilities, payload_size, |
| 100 + i) |
| msg_id2 = self.get_next_msg_id() |
| msgs2[msg2] = 0 |
| msg_ids2[msg_id2] = 0 |
| s_dut.droid.wifiAwareSendMessage(s_disc_id2, peer_id_on_sub2, |
| msg_id2, msg2, 0) |
| |
| peer_id_on_pub1 = self.wait_for_messages( |
| msgs1, msg_ids1, s_disc_id1, p_disc_id1, s_dut, p_dut, |
| payload_size == self.PAYLOAD_SIZE_MIN) |
| peer_id_on_pub2 = self.wait_for_messages( |
| msgs2, msg_ids2, s_disc_id2, p_disc_id2, s_dut, p_dut, |
| payload_size == self.PAYLOAD_SIZE_MIN) |
| |
| msgs1 = {} |
| msg_ids1 = {} |
| msgs2 = {} |
| msg_ids2 = {} |
| for i in range( |
| self.NUM_MSGS_QUEUE_DEPTH_MULT * p_dut. |
| aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]): |
| msg1 = self.create_msg(p_dut.aware_capabilities, payload_size, |
| 1000 + i) |
| msg_id1 = self.get_next_msg_id() |
| msgs1[msg1] = 0 |
| msg_ids1[msg_id1] = 0 |
| p_dut.droid.wifiAwareSendMessage(p_disc_id1, peer_id_on_pub1, |
| msg_id1, msg1, 0) |
| msg2 = self.create_msg(p_dut.aware_capabilities, payload_size, |
| 1100 + i) |
| msg_id2 = self.get_next_msg_id() |
| msgs2[msg2] = 0 |
| msg_ids2[msg_id2] = 0 |
| p_dut.droid.wifiAwareSendMessage(p_disc_id2, peer_id_on_pub2, |
| msg_id2, msg2, 0) |
| |
| self.wait_for_messages(msgs1, msg_ids1, p_disc_id1, s_disc_id1, p_dut, |
| s_dut, payload_size == self.PAYLOAD_SIZE_MIN) |
| self.wait_for_messages(msgs2, msg_ids2, p_disc_id2, s_disc_id2, p_dut, |
| s_dut, payload_size == self.PAYLOAD_SIZE_MIN) |
| |
| # verify there are no more events |
| time.sleep(autils.EVENT_TIMEOUT) |
| autils.verify_no_more_events(p_dut, timeout=0) |
| autils.verify_no_more_events(s_dut, timeout=0) |
| |
| ############################################################################ |
| |
| @test_tracker_info(uuid="a8cd0512-b279-425f-93cf-949ddba22c7a") |
| @WifiBaseTest.wifi_test_wrap |
| def test_message_no_queue_min(self): |
| """Functional / Message / No queue |
| - Minimal payload size (None or "") |
| """ |
| self.run_message_no_queue(self.PAYLOAD_SIZE_MIN) |
| |
| @test_tracker_info(uuid="2c26170a-5d0a-4cf4-b0b9-56ef03f5dcf4") |
| def test_message_no_queue_typical(self): |
| """Functional / Message / No queue |
| - Typical payload size |
| """ |
| self.run_message_no_queue(self.PAYLOAD_SIZE_TYPICAL) |
| |
| @test_tracker_info(uuid="c984860c-b62d-4d9b-8bce-4d894ea3bfbe") |
| @WifiBaseTest.wifi_test_wrap |
| def test_message_no_queue_max(self): |
| """Functional / Message / No queue |
| - Max payload size (based on device capabilities) |
| """ |
| self.run_message_no_queue(self.PAYLOAD_SIZE_MAX) |
| |
| @test_tracker_info(uuid="3f06de73-31ab-4e0c-bc6f-59abdaf87f4f") |
| def test_message_with_queue_min(self): |
| """Functional / Message / With queue |
| - Minimal payload size (none or "") |
| """ |
| self.run_message_with_queue(self.PAYLOAD_SIZE_MIN) |
| |
| @test_tracker_info(uuid="9b7f5bd8-b0b1-479e-8e4b-9db0bb56767b") |
| def test_message_with_queue_typical(self): |
| """Functional / Message / With queue |
| - Typical payload size |
| """ |
| self.run_message_with_queue(self.PAYLOAD_SIZE_TYPICAL) |
| |
| @test_tracker_info(uuid="4f9a6dce-3050-4e6a-a143-53592c6c7c28") |
| def test_message_with_queue_max(self): |
| """Functional / Message / With queue |
| - Max payload size (based on device capabilities) |
| """ |
| self.run_message_with_queue(self.PAYLOAD_SIZE_MAX) |
| |
| @test_tracker_info(uuid="4cece232-0983-4d6b-90a9-1bb9314b64f0") |
| def test_message_with_multiple_discovery_sessions_typical(self): |
| """Functional / Message / Multiple sessions |
| |
| Sets up 2 discovery sessions on 2 devices. Sends a message in each |
| direction on each discovery session and verifies that reaches expected |
| destination. |
| """ |
| self.run_message_multi_session_with_queue(self.PAYLOAD_SIZE_TYPICAL) |