1#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import random
20import time
21
22from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
23from vts.runners.host import asserts
24from vts.runners.host import base_test
25from vts.runners.host import test_runner
26from vts.runners.host import const
27from vts.utils.python.mirror import py2pb
28
29
30class VtsCodelabFmqTest(base_test.BaseTestClass):
31    """Testcases for API calls on FMQ from host side.
32
33    Attributes:
34        _audio: HalMirror, audio HAL server instance.
35        _queue1_writer: ResourceFmqMirror, writer of queue 1.
36        _queue1_reader: ResourceFmqMirror, reader of queue 1.
37        _queue2_writer: ResourceFmqMirror, writer of queue 2.
38        _queue2_reader: ResourceFmqMirror, reader of queue 2.
39        _queue3_writer: ResourceFmqMirror, writer of queue 3.
40        _queue3_reader1: ResourceFmqMirror, reader 1 of queue 3.
41        _queue3_reader2: ResourceFmqMirror, reader 2 of queue 3.
42        _queue4_writer: ResourceFmqMirror, writer of queue 4.
43        _queue4_reader: ResourceFmqMirror, reader of queue 4.
44        _stream_in_types: HalMirror, contains interface in IStreamIn.hal.
45    """
46
47    def setUpClass(self):
48        """This class loads audio HAL in our target-side driver.
49
50        It also initializes four queues for testing:
51        Queue 1: synchronized queue (without blocking) between
52                 one writer and one reader, sending uint16_t type in the queue.
53        Queue 2: synchronized queue (with blocking feature) between
54                 one writer and one reader, sending uint32_t type in
55                 the queue.
56        Queue 3: unsynchronized queue (without blocking) between
57                 one writer and two readers, sending double_t type in
58                 the queue.
59        Queue 4: synchronized queue (without blocking) between
60                 one writer and one reader, sending predefined
61                 ReadParameters type in IStreamIn.hal interface.
62        """
63        self.dut = self.android_devices[0]
64        # Initialize an audio hal driver to start all managers
65        # on the target side.
66        # We use it to check sending FMQ with primitive types and
67        # other predefined types (e.g. ReadParameters) in audio HAL.
68        self.dut.hal.InitHidlHal(
69            target_type="audio",
70            target_basepaths=self.dut.libPaths,
71            target_version_major=4,
72            target_version_minor=0,
73            target_package="android.hardware.audio",
74            target_component_name="IDevicesFactory",
75            bits=int(self.abi_bitness))
76        # Create a shortcut for audio HAL server.
77        self._audio = self.dut.hal.audio
78
79        # Initialize a non-blocking, synchronized writer.
80        self._queue1_writer = self.dut.resource.InitFmq(
81            data_type="uint16_t",
82            sync=True,
83            queue_size=2048,
84            blocking=False,
85            client=self.dut.hal.GetTcpClient("audio"))
86        queue1_writer_id = self._queue1_writer.queueId
87        asserts.assertNotEqual(queue1_writer_id, -1)
88
89        # Initialize a non-blocking, synchronized reader.
90        # This reader shares the same queue as self._queue1_writer.
91        self._queue1_reader = self.dut.resource.InitFmq(
92            existing_queue=self._queue1_writer,
93            client=self.dut.hal.GetTcpClient("audio"))
94        queue1_reader_id = self._queue1_reader.queueId
95        asserts.assertNotEqual(queue1_reader_id, -1)
96
97        # Initialize a blocking, synchronized writer.
98        self._queue2_writer = self.dut.resource.InitFmq(
99            data_type="uint32_t",
100            sync=True,
101            queue_size=2048,
102            blocking=True,
103            client=self.dut.hal.GetTcpClient("audio"))
104        queue2_writer_id = self._queue2_writer.queueId
105        asserts.assertNotEqual(queue2_writer_id, -1)
106
107        # Initialize a blocking, synchronized reader.
108        # This reader shares the same queue as self._queue2_writer.
109        self._queue2_reader = self.dut.resource.InitFmq(
110            existing_queue=self._queue2_writer,
111            client=self.dut.hal.GetTcpClient("audio"))
112        queue2_reader_id = self._queue2_reader.queueId
113        asserts.assertNotEqual(queue2_reader_id, -1)
114
115        # Initialize a non-blocking, unsynchronized writer.
116        self._queue3_writer = self.dut.resource.InitFmq(
117            data_type="double_t",
118            sync=False,
119            queue_size=2048,
120            blocking=False,
121            client=self.dut.hal.GetTcpClient("audio"))
122        queue3_writer_id = self._queue3_writer.queueId
123        asserts.assertNotEqual(queue3_writer_id, -1)
124
125        # Initialize a non-blocking, unsynchronized reader 1.
126        # This reader shares the same queue as self._queue3_writer.
127        self._queue3_reader1 = self.dut.resource.InitFmq(
128            existing_queue=self._queue3_writer,
129            client=self.dut.hal.GetTcpClient("audio"))
130        queue3_reader1_id = self._queue3_reader1.queueId
131        asserts.assertNotEqual(queue3_reader1_id, -1)
132
133        # Initialize a non-blocking, unsynchronized reader 2.
134        # This reader shares the same queue as self._queue3_writer and self._queue3_reader1.
135        self._queue3_reader2 = self.dut.resource.InitFmq(
136            existing_queue=self._queue3_writer,
137            client=self.dut.hal.GetTcpClient("audio"))
138        queue3_reader2_id = self._queue3_reader2.queueId
139        asserts.assertNotEqual(queue3_reader2_id, -1)
140
141        # Find the user-defined type in IStreamIn.hal service.
142        self._stream_in_types = self._audio.GetHidlTypeInterface("IStreamIn")
143        read_param_type = self._stream_in_types.GetAttribute("ReadParameters")
144        # Initialize a non-blocking, synchronized writer.
145        self._queue4_writer = self.dut.resource.InitFmq(
146            # ::android::hardware::audio::V4_0::IStreamIn::ReadParameters
147            data_type=read_param_type.name,
148            sync=True,
149            queue_size=2048,
150            blocking=False,
151            client=self.dut.hal.GetTcpClient("audio"))
152        queue4_writer_id = self._queue4_writer.queueId
153        asserts.assertNotEqual(queue4_writer_id, -1)
154
155        # Initialize a non-blocking, synchronized reader.
156        # This reader shares the same queue as self._queue4_writer.
157        self._queue4_reader = self.dut.resource.InitFmq(
158            existing_queue=self._queue4_writer,
159            client=self.dut.hal.GetTcpClient("audio"))
160        queue4_reader_id = self._queue4_reader.queueId
161        asserts.assertNotEqual(queue4_reader_id, -1)
162
163    def testBasic(self):
164        """Tests correctness of basic util methods. """
165        # Check the correctness on queue 1, which uses primitive type uint32_t.
166        asserts.assertEqual(self._queue1_writer.getQuantumSize(), 2)
167        asserts.assertEqual(self._queue1_writer.getQuantumCount(), 2048)
168        asserts.assertEqual(self._queue1_writer.availableToWrite(), 2048)
169        asserts.assertEqual(self._queue1_reader.availableToRead(), 0)
170        asserts.assertTrue(self._queue1_writer.isValid(),
171                           "Queue 1 writer should be valid.")
172        asserts.assertTrue(self._queue1_reader.isValid(),
173                           "Queue 1 reader should be valid.")
174
175        # Also check the correctness on queue 4, which uses predefined type
176        # in audio HAL service.
177        asserts.assertEqual(self._queue4_writer.getQuantumCount(), 2048)
178        asserts.assertEqual(self._queue4_writer.availableToWrite(), 2048)
179        asserts.assertEqual(self._queue4_reader.availableToRead(), 0)
180        asserts.assertTrue(self._queue4_writer.isValid(),
181                           "Queue 4 writer should be valid.")
182        asserts.assertTrue(self._queue4_reader.isValid(),
183                           "Queue 4 reader should be valid.")
184
185    def testSimpleReadWrite(self):
186        """Test a simple interaction between a writer and a reader.
187
188        This test operates on queue 1, and tests basic read/write.
189        """
190        write_data = self.GetRandomIntegers(2048)
191        read_data = []
192        # Writer writes some data.
193        asserts.assertTrue(
194            self._queue1_writer.write(write_data, 2048), "Write queue failed.")
195        # Check reader reads them back correctly.
196        read_success = self._queue1_reader.read(read_data, 2048)
197        asserts.assertTrue(read_success, "Read queue failed.")
198        asserts.assertEqual(read_data, write_data)
199
200    def testReadEmpty(self):
201        """Test reading from an empty queue. """
202        read_data = []
203        read_success = self._queue1_reader.read(read_data, 5)
204        asserts.assertFalse(read_success,
205                            "Read should fail because queue is empty.")
206
207    def testWriteFull(self):
208        """Test writes fail when queue is full. """
209        write_data = self.GetRandomIntegers(2048)
210
211        # This write should succeed.
212        asserts.assertTrue(
213            self._queue1_writer.write(write_data, 2048),
214            "Writer should write successfully.")
215        # This write should fail because queue is full.
216        asserts.assertFalse(
217            self._queue1_writer.write(write_data, 2048),
218            "Writer should fail because queue is full.")
219
220    def testWriteTooLarge(self):
221        """Test writing more than queue capacity. """
222        write_data = self.GetRandomIntegers(2049)
223        # Write overflows the capacity of the queue.
224        asserts.assertFalse(
225            self._queue1_writer.write(write_data, 2049),
226            "Writer should fail since there are too many items to write.")
227
228    def testConsecutiveReadWrite(self):
229        """Test consecutive interactions between reader and writer.
230
231        This test operates on queue 1, and tests consecutive read/write.
232        """
233        for i in range(64):
234            write_data = self.GetRandomIntegers(2048)
235            asserts.assertTrue(
236                self._queue1_writer.write(write_data, 2048),
237                "Writer should write successfully.")
238            read_data = []
239            read_success = self._queue1_reader.read(read_data, 2048)
240            asserts.assertTrue(read_success,
241                               "Reader should read successfully.")
242            asserts.assertEqual(write_data, read_data)
243
244        # Reader should have no more available to read.
245        asserts.assertEqual(0, self._queue1_reader.availableToRead())
246
247    def testBlockingReadWrite(self):
248        """Test blocking read/write.
249
250        This test operates on queue 2, and tests blocking read/write.
251        Writer waits 0.05s and writes.
252        Reader blocks for at most 0.1s, and should read successfully.
253        TODO: support this when reader and writer operates in parallel.
254        """
255        write_data = self.GetRandomIntegers(2048)
256        read_data = []
257
258        # Writer waits for 0.05s and writes.
259        time.sleep(0.05)
260        asserts.assertTrue(
261            self._queue2_writer.writeBlocking(write_data, 2048, 1000000),
262            "Writer should write successfully.")
263
264        # Reader reads.
265        read_success = self._queue2_reader.readBlocking(
266            read_data, 2048, 1000 * 1000000)
267        asserts.assertTrue(read_success,
268                           "Reader should read successfully after blocking.")
269        asserts.assertEqual(write_data, read_data)
270
271    def testBlockingTimeout(self):
272        """Test blocking timeout.
273
274        This test operates on queue2, and tests that reader should time out
275        because there is not data available.
276        """
277        read_data = []
278        read_success = self._queue2_reader.readBlocking(
279            read_data, 5, 100 * 1000000)
280        asserts.assertFalse(
281            read_success,
282            "Reader blocking should time out because there is no data to read."
283        )
284
285    def testUnsynchronizedReadWrite(self):
286        """Test separate read from two readers.
287
288        This test operates on queue3, and tests that two readers can read back
289        what writer writes.
290        """
291        # Prepare write data.
292        write_data = self.GetRandomFloats(2048)
293        read_data1 = []
294        read_data2 = []
295        asserts.assertTrue(
296            self._queue3_writer.write(write_data, 2048),
297            "Writer should write successfully.")
298        read_success1 = self._queue3_reader1.read(read_data1, 2048)
299        read_success2 = self._queue3_reader2.read(read_data2, 2048)
300        asserts.assertTrue(read_success1, "Reader 1 should read successfully.")
301        asserts.assertTrue(read_success2, "Reader 2 should read successfully.")
302        asserts.assertEqual(write_data, read_data1)
303        asserts.assertEqual(write_data, read_data2)
304
305    def testIllegalBlocking(self):
306        """Test blocking is not allowed in unsynchronized queue.
307
308        This test operates on queue 3, and tests that blocking is not allowed
309        in unsynchronized queue.
310        """
311        write_data = self.GetRandomFloats(2048)
312        asserts.assertFalse(
313            self._queue3_writer.writeBlocking(write_data, 2048, 100 * 1000000),
314            "Blocking operation should fail in unsynchronized queue.")
315
316    def testSimpleReadWriteStructType(self):
317        """Test read/write on queue with predefined type in HAL service.
318
319        This test operates on queue 4, and tests reader and writer can interact
320        in a queue with predefined type ReadParameters defined in IStreamIn.hal.
321        """
322        write_data = [{
323            "command": self._stream_in_types.ReadCommand.READ,
324            "params": {
325                "read": 100
326            }
327        }, {
328            "command": self._stream_in_types.ReadCommand.READ,
329            "params": {
330                "read": 1000
331            }
332        }, {
333            "command":
334            self._stream_in_types.ReadCommand.GET_CAPTURE_POSITION,
335            "params": {}
336        }]
337
338        # Convert each item into a VariableSpecificationMessage using Py2Pb library.
339        read_param_type = self._stream_in_types.GetAttribute("ReadParameters")
340        converted_write_data = map(
341            lambda item: py2pb.Convert(read_param_type, item), write_data)
342        asserts.assertTrue(
343            self._queue4_writer.write(converted_write_data, 3),
344            "Writer should write successfully.")
345
346        # Reader reads the data back, result is a list of dict.
347        read_data = []
348        asserts.assertTrue(
349            self._queue4_reader.read(read_data, 3),
350            "Reader should read successfully.")
351        for i in range(len(write_data)):
352            asserts.assertTrue(
353                self.VerifyDict(write_data[i], read_data[i]),
354                "Dictionary item %d mismatch.", i)
355
356    @staticmethod
357    def GetRandomIntegers(data_size):
358        """Helper method to generate a list of random integers between 0 and 100.
359
360        Args:
361            data_size: int, length of result list.
362
363        Returns:
364            int list, list of integers.
365        """
366        return [random.randint(0, 100) for i in range(data_size)]
367
368    @staticmethod
369    def GetRandomFloats(data_size):
370        """Helper method to generate a list of random floats between 0.0 and 100.0.
371
372        Args:
373            data_size: int, length of result list.
374
375        Returns:
376            float list, list of floats.
377        """
378        return [random.random() * 100 for i in range(data_size)]
379
380    @staticmethod
381    def VerifyDict(correct_dict, return_dict):
382        """Check if two dictionary values are equal.
383
384        This method loops through keys in the dictionary. Two dictionaries can
385        differ in such cases:
386        1. A name exists in the correct dictionary, but not in the returned
387           dictionary.
388        2. The values differ for the same name. If the value is a primitive
389           type, such as integer, string, directly compare them.
390           If the value is a nested dict, recursively call this function to
391           verify the nested dict.
392
393        Args:
394            correct_dict: dict, correct dictionary.
395            return_dict: dict, dictionary that is actually returned to reader.
396
397        Returns:
398            bool, true if two dictionaries match, false otherwise.
399        """
400        for name in correct_dict:
401            if name not in return_dict:
402                return False
403            correct_val = correct_dict[name]
404            if type(correct_val) == dict:
405                if not VtsCodelabFmqTest.VerifyDict(correct_val,
406                                                    return_dict[name]):
407                    return False
408            else:
409                if correct_val != return_dict[name]:
410                    return False
411        return True
412
413
414if __name__ == "__main__":
415    test_runner.main()
416