1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 # 5 # This module contains unit tests for the classes in the mtb module 6 7 import glob 8 import os 9 import sys 10 import unittest 11 12 import common_unittest_utils 13 import fuzzy 14 import mtb 15 import test_conf as conf 16 17 from common_unittest_utils import create_mocked_devices 18 from firmware_constants import AXIS, GV, MTB, PLATFORM, UNIT, VAL 19 from mtb import FingerPath, TidPacket 20 from geometry.elements import Point, about_eq 21 22 23 unittest_path_lumpy = os.path.join(os.getcwd(), 'tests/logs/lumpy') 24 mocked_device = create_mocked_devices() 25 26 27 def get_mtb_packets(gesture_filename): 28 """Get mtb_packets object by reading the gesture file.""" 29 parser = mtb.MtbParser() 30 packets = parser.parse_file(gesture_filename) 31 mtb_packets = mtb.Mtb(packets=packets) 32 return mtb_packets 33 34 35 class FakeMtb(mtb.Mtb): 36 """A fake MTB class to set up x and y positions directly.""" 37 def __init__(self, list_x, list_y): 38 self.list_x = list_x 39 self.list_y = list_y 40 41 def get_x_y(self, target_slot): 42 """Return list_x, list_y directly.""" 43 return (self.list_x, self.list_y) 44 45 46 class MtbTest(unittest.TestCase): 47 """Unit tests for mtb.Mtb class.""" 48 49 def setUp(self): 50 self.test_dir = os.path.join(os.getcwd(), 'tests') 51 self.data_dir = os.path.join(self.test_dir, 'data') 52 53 def _get_filepath(self, filename, gesture_dir=''): 54 return os.path.join(self.data_dir, gesture_dir, filename) 55 56 def _get_range_middle(self, criteria): 57 """Get the middle range of the criteria.""" 58 fc = fuzzy.FuzzyCriteria(criteria) 59 range_min , range_max = fc.get_criteria_value_range() 60 range_middle = (range_min + range_max) / 2.0 61 return range_middle 62 63 def _call_get_reversed_motions(self, list_x, list_y, expected_x, 64 expected_y, direction): 65 mtb = FakeMtb(list_x, list_y) 66 displacement = mtb.get_reversed_motions(0, direction, ratio=0.1) 67 self.assertEqual(displacement[AXIS.X], expected_x) 68 self.assertEqual(displacement[AXIS.Y], expected_y) 69 70 def test_get_reversed_motions_no_reversed(self): 71 list_x = (10, 22 ,36, 54, 100) 72 list_y = (1, 2 ,6, 10, 22) 73 self._call_get_reversed_motions(list_x, list_y, 0, 0, GV.TLBR) 74 75 def test_get_reversed_motions_reversed_x_y(self): 76 list_x = (10, 22 ,36, 154, 100) 77 list_y = (1, 2 ,6, 30, 22) 78 self._call_get_reversed_motions(list_x, list_y, -54, -8, GV.TLBR) 79 80 def _test_get_x_y(self, filename, slot, expected_value): 81 gesture_filename = self._get_filepath(filename) 82 mtb_packets = get_mtb_packets(gesture_filename) 83 list_x, list_y = mtb_packets.get_x_y(slot) 84 points = zip(list_x, list_y) 85 self.assertEqual(len(points), expected_value) 86 87 def test_get_x_y(self): 88 self._test_get_x_y('one_finger_with_slot_0.dat', 0, 12) 89 self._test_get_x_y('one_finger_without_slot_0.dat', 0, 9) 90 self._test_get_x_y('two_finger_with_slot_0.dat', 0, 121) 91 self._test_get_x_y('two_finger_with_slot_0.dat', 1, 59) 92 self._test_get_x_y('two_finger_without_slot_0.dat', 0, 104) 93 self._test_get_x_y('two_finger_without_slot_0.dat', 1, 10) 94 95 def test_get_pressure(self): 96 """Test get pressure""" 97 filename = 'one_finger_with_slot_0.dat' 98 gesture_filename = self._get_filepath(filename) 99 mtb_packets = get_mtb_packets(gesture_filename) 100 finger_paths = mtb_packets.get_ordered_finger_paths() 101 102 # There is only one tracking ID in the file. 103 self.assertEqual(len(finger_paths), 1) 104 105 # Verify some of the pressure values 106 finger_path = finger_paths.values()[0] 107 list_z = finger_path.get('pressure') 108 self.assertEqual(list_z[0:5], [59, 57, 56, 58, 60]) 109 110 def test_get_x_y_multiple_slots(self): 111 filename = 'x_y_multiple_slots.dat' 112 filepath = self._get_filepath(filename) 113 mtb_packets = get_mtb_packets(filepath) 114 slots = (0, 1) 115 list_x, list_y = mtb_packets.get_x_y_multiple_slots(slots) 116 expected_list_x = {} 117 expected_list_y = {} 118 expected_list_x[0] = [1066, 1068, 1082, 1183, 1214, 1285, 1322, 1351, 119 1377, 1391] 120 expected_list_y[0] = [561, 559, 542, 426, 405, 358, 328, 313, 304, 297] 121 expected_list_x[1] = [770, 769, 768, 758, 697, 620, 585, 565, 538, 538] 122 expected_list_y[1] = [894, 894, 895, 898, 927, 968, 996, 1003, 1013, 123 1013] 124 for slot in slots: 125 self.assertEqual(list_x[slot], expected_list_x[slot]) 126 self.assertEqual(list_y[slot], expected_list_y[slot]) 127 128 def test_get_x_y_multiple_slots2(self): 129 """Test slot state machine. 130 131 When the last slot in the previous packet is slot 0, and the first 132 slot in the current packet is also slot 0, the slot 0 will not be 133 displayed explicitly. This test ensures that the slot stat machine 134 is tracked properly. 135 """ 136 filename = 'pinch_to_zoom.zoom_in.dat' 137 filepath = self._get_filepath(filename) 138 mtb_packets = get_mtb_packets(filepath) 139 slots = (0, 1) 140 list_x, list_y = mtb_packets.get_x_y_multiple_slots(slots) 141 expected_final_x = {} 142 expected_final_y = {} 143 expected_final_x[0] = 1318 144 expected_final_y[0] = 255 145 expected_final_x[1] = 522 146 expected_final_y[1] = 1232 147 for slot in slots: 148 self.assertEqual(list_x[slot][-1], expected_final_x[slot]) 149 self.assertEqual(list_y[slot][-1], expected_final_y[slot]) 150 151 def _test_get_all_finger_paths_about_numbers_of_packets( 152 self, filename, expected_numbers): 153 mtb_packets = get_mtb_packets(self._get_filepath(filename)) 154 finger_paths = mtb_packets.get_ordered_finger_paths() 155 for tid, expected_len in expected_numbers.items(): 156 self.assertEqual(len(finger_paths[tid].tid_packets), expected_len) 157 158 def test_get_ordered_finger_paths_about_number_of_packets(self): 159 self._test_get_all_finger_paths_about_numbers_of_packets( 160 'two_finger_with_slot_0.dat', {2101: 122, 2102: 60}) 161 self._test_get_all_finger_paths_about_numbers_of_packets( 162 'two_finger_without_slot_0.dat', {2097: 105, 2098: 11}) 163 164 def test_data_ready(self): 165 """Test data_ready flag when point.x could be 0.""" 166 filename = ('20130506_030025-fw_11.27-robot_sim/' 167 'one_finger_to_edge.center_to_left.slow-lumpy-fw_11.27-' 168 'robot_sim-20130506_031554.dat') 169 filepath = os.path.join(unittest_path_lumpy, filename) 170 mtb_packets = get_mtb_packets(filepath) 171 points = mtb_packets.get_ordered_finger_path(0, 'point') 172 # Note: 173 # 1. In the first packet, there exists the event ABS_PRESSURE 174 # but no ABS_MT_PRESSURE. 175 # 2. The last packet with ABS_MT_TRACKING_ID = -1 is also counted. 176 self.assertEqual(len(points), 78) 177 178 def _test_drumroll(self, filename, expected_max_distance): 179 """expected_max_distance: unit in pixel""" 180 gesture_filename = self._get_filepath(filename) 181 mtb_packets = get_mtb_packets(gesture_filename) 182 actual_max_distance = mtb_packets.get_max_distance_of_all_tracking_ids() 183 self.assertTrue(about_eq(actual_max_distance, expected_max_distance)) 184 185 def test_drumroll(self): 186 expected_max_distance = 52.0216301167 187 self._test_drumroll('drumroll_lumpy.dat', expected_max_distance) 188 189 def test_drumroll1(self): 190 expected_max_distance = 43.5660418216 191 self._test_drumroll('drumroll_lumpy_1.dat', expected_max_distance) 192 193 def test_drumroll_link(self): 194 expected_max_distance = 25.6124969497 195 self._test_drumroll('drumroll_link.dat', expected_max_distance) 196 197 def test_no_drumroll_link(self): 198 expected_max_distance = 2.91547594742 199 self._test_drumroll('no_drumroll_link.dat', expected_max_distance) 200 201 def test_no_drumroll_link(self): 202 expected_max_distance = 24.8243831746 203 self._test_drumroll('drumroll_link_2.dat', expected_max_distance) 204 205 def _test_finger_path(self, filename, tid, expected_slot, expected_data, 206 request_data_ready=True): 207 """Test the data in a finger path""" 208 # Instantiate the expected finger_path 209 expected_finger_path = FingerPath(expected_slot, 210 [TidPacket(time, Point(*xy), z) 211 for time, xy, z in expected_data]) 212 213 # Derive the actual finger_path for the specified tid 214 mtb_packets = get_mtb_packets(self._get_filepath(filename)) 215 finger_paths = mtb_packets.get_ordered_finger_paths(request_data_ready) 216 actual_finger_path = finger_paths[tid] 217 218 # Assert that the packet lengths are the same. 219 self.assertEqual(len(expected_finger_path.tid_packets), 220 len(actual_finger_path.tid_packets)) 221 222 # Assert that all tid data (including syn_time, point, pressure, etc.) 223 # in the tid packets are the same. 224 for i in range(len(actual_finger_path.tid_packets)): 225 expected_packet = expected_finger_path.tid_packets[i] 226 actual_packet = actual_finger_path.tid_packets[i] 227 self.assertEqual(expected_packet.syn_time, actual_packet.syn_time) 228 self.assertTrue(expected_packet.point == actual_packet.point) 229 self.assertEqual(expected_packet.pressure, actual_packet.pressure) 230 231 def test_get_ordered_finger_paths(self): 232 """Test get_ordered_finger_paths 233 234 Tracking ID 95: slot 0 (no explicit slot 0 assigned). 235 This is the only slot in the packet. 236 """ 237 filename = 'drumroll_link_2.dat' 238 tid = 95 239 expected_slot = 0 240 expected_data = [# (syn_time, (x, y), z) 241 (238154.686034, (789, 358), 59), 242 (238154.691606, (789, 358), 60), 243 (238154.697058, (789, 358), 57), 244 (238154.702576, (789, 358), 59), 245 (238154.713731, (789, 358), 57), 246 (238154.719160, (789, 359), 57), 247 (238154.724791, (789, 359), 56), 248 (238154.730111, (789, 359), 58), 249 (238154.735588, (788, 359), 53), 250 (238154.741068, (788, 360), 53), 251 (238154.746569, (788, 360), 49), 252 (238154.752108, (787, 360), 40), 253 (238154.757705, (787, 361), 27), 254 (238154.763075, (490, 903), 46), 255 (238154.768532, (486, 892), 61), 256 (238154.774695, (484, 895), 57), 257 (238154.780192, (493, 890), 56), 258 (238154.785651, (488, 893), 55), 259 (238154.791140, (488, 893), 56), 260 (238154.802080, (489, 893), 55), 261 (238154.807578, (490, 893), 50), 262 (238154.818573, (490, 893), 46), 263 (238154.824066, (491, 893), 36), 264 (238154.829525, (492, 893), 22), 265 (238154.849958, (492, 893), 22), 266 ] 267 self._test_finger_path(filename, tid, expected_slot, expected_data) 268 269 def test_get_ordered_finger_paths2(self): 270 """Test get_ordered_finger_paths 271 272 Tracking ID 104: slot 0 (explicit slot 0 assigned). 273 This is the 2nd slot in the packet. 274 A slot 1 has already existed. 275 """ 276 277 filename = 'drumroll_link_2.dat' 278 tid = 104 279 expected_slot = 0 280 expected_data = [# (syn_time, (x, y), z) 281 (238157.994296, (780, 373), 75), 282 (238158.001110, (780, 372), 75), 283 (238158.007128, (780, 372), 76), 284 (238158.012617, (780, 372), 73), 285 (238158.018112, (780, 373), 69), 286 (238158.023600, (780, 373), 68), 287 (238158.029542, (781, 373), 51), 288 (238158.049605, (781, 373), 51), 289 ] 290 self._test_finger_path(filename, tid, expected_slot, expected_data) 291 292 def test_get_ordered_finger_paths2b(self): 293 """Test get_ordered_finger_paths 294 295 Tracking ID 103: slot 1 (explicit slot 1 assigned). 296 This tracking ID overlaps with two distinct 297 tracking IDs of which the slot is the same slot 0. 298 This is a good test as a multiple-finger case. 299 300 tid 102, slot 0 arrived 301 tid 103, slot 1 arrived 302 tid 102, slot 0 left 303 tid 104, slot 0 arrived 304 tid 103, slot 1 left 305 tid 104, slot 0 left 306 """ 307 filename = 'drumroll_link_2.dat' 308 tid = 103 309 expected_slot = 1 310 expected_data = [# (syn_time, (x, y), z) 311 (238157.906405, (527, 901), 71), 312 (238157.911749, (527, 901), 74), 313 (238157.917247, (527, 901), 73), 314 (238157.923152, (527, 902), 71), 315 (238157.928317, (527, 902), 72), 316 (238157.934492, (527, 902), 71), 317 (238157.939984, (527, 902), 69), 318 (238157.945485, (527, 902), 65), 319 (238157.950984, (527, 902), 66), 320 (238157.956482, (527, 902), 70), 321 (238157.961976, (527, 902), 65), 322 (238157.973768, (527, 902), 64), 323 (238157.980491, (528, 901), 61), 324 (238157.987140, (529, 899), 60), 325 (238157.994296, (531, 896), 52), 326 (238158.001110, (534, 892), 34), 327 (238158.007128, (534, 892), 34), 328 (238158.012617, (534, 892), 34), 329 (238158.018112, (534, 892), 34), 330 (238158.023600, (534, 892), 34), 331 (238158.029542, (534, 892), 34), 332 ] 333 self._test_finger_path(filename, tid, expected_slot, expected_data) 334 335 def test_get_ordered_finger_paths3(self): 336 """Test get_ordered_finger_paths 337 338 This is a good test sample. 339 - An unusual slot 9 340 - This is the 2nd slot in the packet. A slot 8 has already existed. 341 - Its ABS_MT_PRESSURE is missing in the first packet. 342 - Slot 8 terminates a few packets earlier than this slot. 343 - Some of the ABS_MT_POSITION_X/Y and ABS_MT_PRESSURE are not shown. 344 """ 345 filename = 'drumroll_3.dat' 346 tid = 582 347 expected_slot = 9 348 expected_data = [# (syn_time, (x, y), z) 349 (6411.371613, (682, 173), None), 350 (6411.382541, (667, 186), 35), 351 (6411.393355, (664, 189), 37), 352 (6411.404310, (664, 190), 38), 353 (6411.413015, (664, 189), 38), 354 (6411.422118, (665, 189), 38), 355 (6411.430792, (665, 189), 37), 356 (6411.439764, (667, 188), 36), 357 (6411.448484, (675, 185), 29), 358 (6411.457212, (683, 181), 17), 359 (6411.465843, (693, 172), 5), 360 (6411.474749, (469, 381), 6), 361 (6411.483702, (471, 395), 26), 362 (6411.492369, (471, 396), 13), 363 (6411.499916, (471, 396), 13), 364 ] 365 self._test_finger_path(filename, tid, expected_slot, expected_data, 366 request_data_ready=False) 367 368 def test_get_ordered_finger_paths4(self): 369 """Test get_ordered_finger_paths 370 371 This test is to verify if it could handle the case when a finger-off 372 event is followed immediately by a finger-on event in the same packet. 373 This situation may occur occasionally in two_close_fingers_tracking 374 gestures. Basically, this could be considered as a firmware bug. 375 However, our test should be able to handle the situation gracefully. 376 377 A problematic packet may look like: 378 379 Event: time .., type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value -1 380 Event: time .., type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 202 381 Event: time .., type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 1577 382 Event: time .., type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 1018 383 Event: time .., type 3 (EV_ABS), code 58 (ABS_MT_PRESSURE), value 99 384 Event: time .., type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 19 385 Event: time .., type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 19 386 Event: time .., type 3 (EV_ABS), code 0 (ABS_X), value 1577 387 Event: time .., type 3 (EV_ABS), code 1 (ABS_Y), value 1018 388 Event: time .., type 3 (EV_ABS), code 24 (ABS_PRESSURE), value 99 389 Event: time .., -------------- SYN_REPORT ------------ 390 """ 391 # Get the actual finger_paths from the gesture data file. 392 filename = 'two_close_fingers_tracking.dat' 393 mtb_packets = get_mtb_packets(self._get_filepath(filename)) 394 finger_paths = mtb_packets.get_ordered_finger_paths( 395 request_data_ready=False) 396 397 data_list = [ 398 # (tid, packet_idx, syn_time, (x, y), z, number_packets) 399 (197, -1, 1395784288.323233, (1619, 1019), 98, 435), 400 (202, 0, 1395784288.323233, (1577, 1018), 99, 261), 401 ] 402 403 for tid, packet_idx, syn_time, xy, z, number_packets in data_list: 404 expected_packet = TidPacket(syn_time, Point(*xy), z) 405 406 # Derive the actual finger path and the actual packet. 407 actual_finger_path = finger_paths[tid] 408 actual_packet = actual_finger_path.tid_packets[packet_idx] 409 410 # Assert that the number of packets in the actual finger path 411 # is equal to the specified number. 412 self.assertEqual(number_packets, 413 len(actual_finger_path.tid_packets)) 414 415 # Assert that the expected packet is equal to the actual packet. 416 self.assertEqual(expected_packet.syn_time, actual_packet.syn_time) 417 self.assertTrue(expected_packet.point == actual_packet.point) 418 self.assertEqual(expected_packet.pressure, actual_packet.pressure) 419 420 421 def test_get_slot_data(self): 422 """Test if it can get the data from the correct slot. 423 424 slot 0 and slot 1 start at the same packet. This test verifies if the 425 method uses the correct corresponding slot numbers. 426 """ 427 filename = 'two_finger_tracking.diagonal.slow.dat' 428 gesture_filename = self._get_filepath(filename) 429 mtb_packets = get_mtb_packets(gesture_filename) 430 431 # There are more packets. Use just a few of them to verify. 432 xy_pairs = { 433 # Slot 0 434 0: [(1142, 191), (1144, 201), (1144, 200)], 435 # Slot 1 436 1: [(957, 105), (966, 106), (960, 104)], 437 } 438 439 number_packets = { 440 # Slot 0 441 0: 190, 442 # Slot 1 443 1: 189, 444 } 445 446 slots = [0, 1] 447 for slot in slots: 448 points = mtb_packets.get_slot_data(slot, 'point') 449 # Verify the number of packets in each slot 450 self.assertEqual(len(points), number_packets[slot]) 451 # Verify a few packets in each slot 452 for i, xy_pair in enumerate(xy_pairs[slot]): 453 self.assertTrue(Point(*xy_pair) == points[i]) 454 455 def test_convert_to_evemu_format(self): 456 evemu_filename = self._get_filepath('one_finger_swipe.evemu.dat') 457 mtplot_filename = self._get_filepath('one_finger_swipe.dat') 458 packets = mtb.MtbParser().parse_file(mtplot_filename) 459 evemu_converted_iter = iter(mtb.convert_to_evemu_format(packets)) 460 with open(evemu_filename) as evemuf: 461 for line_evemu_original in evemuf: 462 evemu_original = line_evemu_original.split() 463 evemu_converted_str = next(evemu_converted_iter, None) 464 self.assertNotEqual(evemu_converted_str, None) 465 if evemu_converted_str: 466 evemu_converted = evemu_converted_str.split() 467 self.assertEqual(len(evemu_original), 5) 468 self.assertEqual(len(evemu_converted), 5) 469 # Skip the timestamps for they are different in both formats. 470 # Prefix, type, code, and value should be the same. 471 for i in [0, 2, 3, 4]: 472 self.assertEqual(evemu_original[i], evemu_converted[i]) 473 474 def test_get_largest_gap_ratio(self): 475 """Test get_largest_gap_ratio for one-finger and two-finger gestures.""" 476 # The following files come with noticeable large gaps. 477 list_large_ratio = [ 478 'one_finger_tracking.left_to_right.slow_1.dat', 479 'two_finger_gaps.vertical.dat', 480 'two_finger_gaps.horizontal.dat', 481 'resting_finger_2nd_finger_moving_segment_gaps.dat', 482 'gap_new_finger_arriving_or_departing.dat', 483 'one_stationary_finger_2nd_finger_moving_gaps.dat', 484 'resting_finger_2nd_finger_moving_gaps.dat', 485 ] 486 gesture_slots = { 487 'one_finger': [0,], 488 'two_finger': [0, 1], 489 'resting_finger': [1,], 490 'gap_new_finger': [0,], 491 'one_stationary_finger': [1,], 492 } 493 494 range_middle = self._get_range_middle(conf.no_gap_criteria) 495 gap_data_dir = self._get_filepath('gaps') 496 gap_data_filenames = glob.glob(os.path.join(gap_data_dir, '*.dat')) 497 for filename in gap_data_filenames: 498 mtb_packets = get_mtb_packets(filename) 499 base_filename = os.path.basename(filename) 500 501 # What slots to check are based on the gesture name. 502 slots = [] 503 for gesture in gesture_slots: 504 if base_filename.startswith(gesture): 505 slots = gesture_slots[gesture] 506 break 507 508 for slot in slots: 509 largest_gap_ratio = mtb_packets.get_largest_gap_ratio(slot) 510 if base_filename in list_large_ratio: 511 self.assertTrue(largest_gap_ratio >= range_middle) 512 else: 513 self.assertTrue(largest_gap_ratio < range_middle) 514 515 def test_get_largest_accumulated_level_jumps(self): 516 """Test get_largest_accumulated_level_jumps.""" 517 dir_level_jumps = 'drag_edge_thumb' 518 519 filenames = [ 520 # filenames with level jumps 521 # ---------------------------------- 522 'drag_edge_thumb.horizontal.dat', 523 'drag_edge_thumb.horizontal_2.dat', 524 # test no points in some tracking ID 525 'drag_edge_thumb.horizontal_3.no_points.dat', 526 'drag_edge_thumb.vertical.dat', 527 'drag_edge_thumb.vertical_2.dat', 528 'drag_edge_thumb.diagonal.dat', 529 # Change tracking IDs quickly. 530 'drag_edge_thumb.horizontal_4.change_ids_quickly.dat', 531 532 # filenames without level jumps 533 # ---------------------------------- 534 'drag_edge_thumb.horizontal.curvy.dat', 535 'drag_edge_thumb.horizontal_2.curvy.dat', 536 'drag_edge_thumb.vertical.curvy.dat', 537 'drag_edge_thumb.vertical_2.curvy.dat', 538 # Rather small level jumps 539 'drag_edge_thumb.horizontal_5.small_level_jumps.curvy.dat', 540 ] 541 542 largest_level_jumps = { 543 # Large jumps 544 'drag_edge_thumb.horizontal.dat': {AXIS.X: 0, AXIS.Y: 97}, 545 # Smaller jumps 546 'drag_edge_thumb.horizontal_2.dat': {AXIS.X: 0, AXIS.Y: 24}, 547 # test no points in some tracking ID 548 'drag_edge_thumb.horizontal_3.no_points.dat': 549 {AXIS.X: 97, AXIS.Y: 88}, 550 # Change tracking IDs quickly. 551 'drag_edge_thumb.horizontal_4.change_ids_quickly.dat': 552 {AXIS.X: 0, AXIS.Y: 14}, 553 # Large jumps 554 'drag_edge_thumb.vertical.dat': {AXIS.X: 54, AXIS.Y: 0}, 555 # The first slot 0 comes with smaller jumps only. 556 'drag_edge_thumb.vertical_2.dat': {AXIS.X: 20, AXIS.Y: 0}, 557 # Large jumps 558 'drag_edge_thumb.diagonal.dat': {AXIS.X: 84, AXIS.Y: 58}, 559 } 560 561 target_slot = 0 562 for filename in filenames: 563 filepath = self._get_filepath(filename, gesture_dir=dir_level_jumps) 564 packets = get_mtb_packets(filepath) 565 displacements = packets.get_displacements_for_slots(target_slot) 566 567 # There are no level jumps in a curvy line. 568 file_with_level_jump = 'curvy' not in filename 569 570 # Check the first slot only 571 tids = displacements.keys() 572 tids.sort() 573 tid = tids[0] 574 # Check both axis X and axis Y 575 for axis in AXIS.LIST: 576 disp = displacements[tid][axis] 577 jump = packets.get_largest_accumulated_level_jumps(disp) 578 # Verify that there are no jumps in curvy files, and 579 # that there are jumps in the other files. 580 expected_jump = (0 if not file_with_level_jump 581 else largest_level_jumps[filename][axis]) 582 self.assertTrue(jump == expected_jump) 583 584 def test_get_max_distance_from_points(self): 585 """Test get_max_distance_from_points""" 586 # Two farthest points: (15, 16) and (46, 70) 587 list_coordinates_pairs = [ 588 (20, 25), (21, 35), (15, 16), (25, 22), (30, 32), (46, 70), 589 (35, 68), (42, 53), (50, 30), (43, 69), (16, 17), (14, 30), 590 ] 591 points = [Point(*pairs) for pairs in list_coordinates_pairs] 592 mtb_packets = mtb.Mtb(device=mocked_device[PLATFORM.LUMPY]) 593 594 # Verify the max distance in pixels 595 max_distance_px = mtb_packets.get_max_distance_from_points(points, 596 UNIT.PIXEL) 597 expected_max_distance_px = ((46 - 15) ** 2 + (70 - 16) ** 2) ** 0.5 598 self.assertAlmostEqual(max_distance_px, expected_max_distance_px) 599 600 # Verify the max distance in mms 601 max_distance_mm = mtb_packets.get_max_distance_from_points(points, 602 UNIT.MM) 603 expected_max_distance_mm = (((46 - 15) / 12.0) ** 2 + 604 ((70 - 16) / 10.0) ** 2) ** 0.5 605 self.assertAlmostEqual(max_distance_mm, expected_max_distance_mm) 606 607 def _test_get_segments(self, list_t, list_coord, expected_segments, ratio): 608 """Test get_segments 609 610 @param expected_segments: a dictionary of 611 {segment_flag: expected_segment_indexes} 612 """ 613 mtb_packets = mtb.Mtb(device=mocked_device[PLATFORM.LUMPY]) 614 for segment_flag, (expected_segment_t, expected_segment_coord) in \ 615 expected_segments.items(): 616 segment_t, segment_coord = mtb_packets.get_segments( 617 list_t, list_coord, segment_flag, ratio) 618 self.assertEqual(segment_t, expected_segment_t) 619 self.assertEqual(segment_coord, expected_segment_coord) 620 621 def test_get_segments_by_distance(self): 622 """Test get_segments_by_distance 623 624 In the test case below, 625 min_coord = 100 626 max_coord = 220 627 max_distance = max_coord - min_coord = 220 - 100 = 120 628 ratio = 0.1 629 120 * 0.1 = 12 630 begin segment: 100 ~ 112 631 end segment: 208 ~ 220 632 """ 633 list_coord = [102, 101, 101, 100, 100, 103, 104, 110, 118, 120, 634 122, 124, 131, 140, 150, 160, 190, 210, 217, 220] 635 list_t = [1000 + 0.012 * i for i in range(len(list_coord))] 636 ratio = 0.1 637 expected_segments= { 638 VAL.WHOLE: (list_t, list_coord), 639 VAL.MIDDLE: (list_t[8:17], list_coord[8:17]), 640 VAL.BEGIN: (list_t[:8], list_coord[:8]), 641 VAL.END: (list_t[17:], list_coord[17:]), 642 } 643 self._test_get_segments(list_t, list_coord, expected_segments, ratio) 644 645 def test_get_segments_by_length(self): 646 """Test get_segments_by_length""" 647 list_coords = [ 648 [105, 105, 105, 105, 105, 105, 105, 105, 105, 105], 649 [104, 105, 105, 105, 105, 105, 105, 105, 105, 105], 650 [105, 105, 105, 105, 105, 105, 105, 105, 105, 106], 651 ] 652 ratio = 0.1 653 for list_c in list_coords: 654 list_t = [1000 + 0.012 * i for i in range(len(list_c))] 655 expected_segments= { 656 VAL.WHOLE: (list_t, list_c), 657 VAL.MIDDLE: (list_t[1:9], list_c[1:9]), 658 VAL.BEGIN: (list_t[:1], list_c[:1]), 659 VAL.END: (list_t[9:], list_c[9:]), 660 } 661 self._test_get_segments(list_t, list_c, expected_segments, ratio) 662 663 664 if __name__ == '__main__': 665 unittest.main() 666