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