""" """ """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ __author__ = "Théo de la Hogue" __credits__ = [] __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" __license__ = "GPLv3" import unittest from dataclasses import dataclass from argaze import GazeFeatures import numpy import pandas def random_gaze_positions(size, frame_dimension: tuple[float, float] = (1, 1)): """ Generate random TimeStampedGazePsoitions for testing purpose. Timestamps are current time. GazePositions are random values. """ import random import time ts_gaze_positions = GazeFeatures.TimeStampedGazePositions() for i in range(0, size): # Edit gaze position random_gaze_position = GazeFeatures.GazePosition((random.random() * frame_dimension[0], random.random() * frame_dimension[1]), precision=5) # Timestamp gaze position random_gaze_position.timestamp = time.time() # Store timestamped gaze position ts_gaze_positions.append(random_gaze_position) return ts_gaze_positions class TestFixation(GazeFeatures.Fixation): """Define basic fixation class for test.""" def __init__(self, positions: GazeFeatures.TimeStampedGazePositions = (), finished: bool = False, message: str = None, **kwargs): super().__init__(positions, finished, message, **kwargs) if positions: positions_array = numpy.asarray(self.values()) centroid = numpy.mean(positions_array, axis=0) # Update focus attribute using centroid self.focus = (centroid[0], centroid[1]) class TestSaccade(GazeFeatures.Saccade): """Define basic saccade for test.""" def __init__(self, positions: GazeFeatures.TimeStampedGazePositions = (), finished: bool = False, message: str = None, **kwargs): super().__init__(positions, finished, message, **kwargs) class TestGazePositionClass(unittest.TestCase): """Test GazePosition class.""" def test_new(self): """Test GazePosition creation.""" # Check empty GazePosition empty_gaze_position = GazeFeatures.GazePosition(message="empty for test") self.assertEqual(empty_gaze_position, ()) self.assertEqual(empty_gaze_position, ()) self.assertEqual(empty_gaze_position.precision, None) self.assertEqual(empty_gaze_position.message, "empty for test") self.assertEqual(len(empty_gaze_position), 0) self.assertEqual(bool(empty_gaze_position), False) self.assertEqual(numpy.array(empty_gaze_position).shape, (0,)) # Check integer GazePosition int_gaze_position = GazeFeatures.GazePosition((123, 456), precision=55) self.assertEqual(int_gaze_position, (123, 456)) self.assertEqual(int_gaze_position[0], 123) self.assertEqual(int_gaze_position[1], 456) self.assertEqual(int_gaze_position, (123, 456)) self.assertEqual(int_gaze_position.precision, 55) self.assertEqual(len(int_gaze_position), 2) self.assertEqual(bool(int_gaze_position), True) self.assertEqual(numpy.array(int_gaze_position).shape, (2,)) # Check float GazePosition float_gaze_position = GazeFeatures.GazePosition((1.23, 4.56), precision=5.5) self.assertEqual(float_gaze_position, (1.23, 4.56)) self.assertEqual(float_gaze_position[0], 1.23) self.assertEqual(float_gaze_position[1], 4.56) self.assertEqual(float_gaze_position, (1.23, 4.56)) self.assertEqual(float_gaze_position.precision, 5.5) self.assertEqual(len(float_gaze_position), 2) self.assertEqual(bool(float_gaze_position), True) self.assertEqual(numpy.array(float_gaze_position).shape, (2,)) def test_properties(self): """Test GazePosition properties cannot be modified after creation.""" gaze_position = GazeFeatures.GazePosition() # Check that gaze position value setting fails with self.assertRaises(AttributeError): gaze_position.value = (12, 34) # WARNING: gaze_position = (12, 34) is possible !!!! # How to prevent this? self.assertNotEqual(gaze_position, (12, 34)) self.assertEqual(gaze_position, ()) # Check that gaze position precision setting fails with self.assertRaises(AttributeError): gaze_position.precision = 5 self.assertNotEqual(gaze_position.precision, 5) self.assertEqual(gaze_position.precision, None) # Check that gaze position message setting fails with self.assertRaises(AttributeError): gaze_position.message = "later setting" self.assertNotEqual(gaze_position.message, "later setting") self.assertEqual(gaze_position.message, None) def test_overlapping(self): """Test GazePosition overlap method.""" gaze_position_A = GazeFeatures.GazePosition((0, 0), precision=10) gaze_position_B = GazeFeatures.GazePosition((10, 0), precision=10) gaze_position_C = GazeFeatures.GazePosition((5, 0), precision=5) gaze_position_D = GazeFeatures.GazePosition((4, 0), precision=5) self.assertFalse(gaze_position_A.is_overlapping()(gaze_position_B)) self.assertTrue(gaze_position_A.is_overlapping()(gaze_position_C)) self.assertFalse(gaze_position_A.is_overlapping()(gaze_position_C, both=True)) self.assertFalse(gaze_position_B.is_overlapping()(gaze_position_A)) self.assertTrue(gaze_position_B.is_overlapping()(gaze_position_C)) self.assertFalse(gaze_position_B.is_overlapping()(gaze_position_C, both=True)) self.assertFalse(gaze_position_C.is_overlapping()(gaze_position_A)) self.assertFalse(gaze_position_C.is_overlapping()(gaze_position_B)) def test___repr__(self): """Test GazePosition string representation.""" # Check empty GazePosition representation self.assertEqual(repr(GazeFeatures.GazePosition()), "{\"value\": [], \"timestamp\": NaN}") # Check GazePosition representation self.assertEqual(repr(GazeFeatures.GazePosition((12, 345), precision=50, message="ok")), \ "{\"value\": [12, 345], \"precision\": 50, \"message\": \"ok\", \"timestamp\": NaN}") class TestTimeStampedGazePositionsClass(unittest.TestCase): """Test TimeStampedGazePositions class.""" def test_new(self): """Test TimeStampedGazePositions creation.""" empty_gaze_position = GazeFeatures.GazePosition() empty_gaze_position.timestamp = 100 gaze_position_with_message = GazeFeatures.GazePosition((12, 345), message="second position") gaze_position_with_message.timestamp = 200 dict_gaze_position = {"timestamp": 300, "value": (0, 0), "precision": 0.} ts_gaze_positions = GazeFeatures.TimeStampedGazePositions([empty_gaze_position, gaze_position_with_message, dict_gaze_position]) # Check first GazePosition is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition) self.assertEqual(bool(ts_gaze_positions[0]), False) self.assertEqual(ts_gaze_positions[0].timestamp, 100) # Check second GazePosition is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[1], GazeFeatures.GazePosition) self.assertEqual(bool(ts_gaze_positions[1]), True) self.assertEqual(ts_gaze_positions[1].timestamp, 200) # Check third GazePosition from dict is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.GazePosition) self.assertEqual(bool(ts_gaze_positions[2]), True) self.assertEqual(ts_gaze_positions[2].timestamp, 300) # Check that bad data type insertion fails with self.assertRaises(TypeError): ts_gaze_positions.append("This string is not a gaze position value.") # Check that dict with bad keys insertion fails with self.assertRaises(TypeError): ts_gaze_positions.append({"bad_key": (0, 0), "precision": 0.}) # Check final lenght self.assertEqual(len(ts_gaze_positions), 3) def test___repr__(self): """Test inherited string representation.""" ts_gaze_positions = GazeFeatures.TimeStampedGazePositions() self.assertEqual(repr(GazeFeatures.TimeStampedGazePositions()), "[]") empty_gaze_position = GazeFeatures.GazePosition() empty_gaze_position.timestamp = 100 ts_gaze_positions.append(empty_gaze_position) self.assertEqual(repr(ts_gaze_positions), "[{\"value\": [], \"timestamp\": 100}]") full_gaze_position = GazeFeatures.GazePosition((12, 345), precision=50, message="ok") full_gaze_position.timestamp = 200 ts_gaze_positions[0] = full_gaze_position self.assertEqual(repr(ts_gaze_positions), "[{\"value\": [12, 345], \"precision\": 50, \"message\": \"ok\", \"timestamp\": 200}]") def test_from_dataframe(self): """Test from_dataframe classmethod.""" data = {'Specific timestamp label': [0, 1, 2, 3, 4], 'Specific gaze position x label': [0, 10, numpy.nan, 30, 40], 'Specific gaze position y label': [0, 100, numpy.nan, 300, 400] } dataframe = pandas.DataFrame().from_dict(data) ts_gaze_positions = GazeFeatures.TimeStampedGazePositions.from_dataframe(dataframe, timestamp='Specific timestamp label', x='Specific gaze position x label', y='Specific gaze position y label') # Check buffer length self.assertEqual(len(ts_gaze_positions), 5) # Check first gaze position is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition) self.assertEqual(ts_gaze_positions[0].precision, None) self.assertEqual(bool(ts_gaze_positions[0]), True) # Check third gaze position is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.GazePosition) self.assertEqual(ts_gaze_positions[2].precision, None) self.assertEqual(bool(ts_gaze_positions[2]), False) data = {'Specific timestamp label': [0, 1, 2, 3, 4], 'Specific gaze position x label': [0, 10, numpy.nan, 30, 40], 'Specific gaze position y label': [0, 100, numpy.nan, 300, 400], 'Specific precisison label': [15, 15, numpy.nan, 15, 15] } dataframe = pandas.DataFrame().from_dict(data) ts_gaze_positions = GazeFeatures.TimeStampedGazePositions.from_dataframe(dataframe, timestamp='Specific timestamp label', x='Specific gaze position x label', y='Specific gaze position y label', precision='Specific precisison label') # Check buffer length self.assertEqual(len(ts_gaze_positions), 5) # Check first gaze position is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition) self.assertEqual(ts_gaze_positions[0].precision, 15) self.assertEqual(bool(ts_gaze_positions[0]), True) # Check third gaze position is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.GazePosition) self.assertEqual(numpy.isnan(ts_gaze_positions[2].precision), True) self.assertEqual(bool(ts_gaze_positions[2]), False) def test_as_dataframe(self): """Test inherited as_dataframe method.""" ts_gaze_positions_dataframe = random_gaze_positions(10).as_dataframe() # Check dataframe conversion self.assertEqual(ts_gaze_positions_dataframe.index.name, "timestamp") self.assertEqual(ts_gaze_positions_dataframe.index.size, 10) self.assertEqual(ts_gaze_positions_dataframe.columns.size, 3) self.assertEqual(ts_gaze_positions_dataframe.columns[0], "value") self.assertEqual(ts_gaze_positions_dataframe.columns[1], "precision") self.assertEqual(ts_gaze_positions_dataframe.columns[2], "message") self.assertEqual(ts_gaze_positions_dataframe["value"].dtype, 'object') self.assertEqual(ts_gaze_positions_dataframe["precision"].dtype, 'int64') self.assertEqual(ts_gaze_positions_dataframe["message"].dtype, 'O') # Python object type # Check empty position conversion empty_gaze_position = GazeFeatures.GazePosition() empty_gaze_position.timestamp = 100 ts_gaze_positions = GazeFeatures.TimeStampedGazePositions([empty_gaze_position]) ts_gaze_positions_dataframe = ts_gaze_positions.as_dataframe() self.assertEqual(ts_gaze_positions_dataframe.index.name, "timestamp") self.assertEqual(ts_gaze_positions_dataframe.index.size, 1) self.assertEqual(ts_gaze_positions_dataframe.columns.size, 3) self.assertEqual(ts_gaze_positions_dataframe.columns[0], "value") self.assertEqual(ts_gaze_positions_dataframe.columns[1], "precision") self.assertEqual(ts_gaze_positions_dataframe.columns[2], "message") self.assertEqual(ts_gaze_positions_dataframe["value"].dtype, 'object') self.assertEqual(ts_gaze_positions_dataframe["precision"].dtype, 'O') # Python object type self.assertEqual(ts_gaze_positions_dataframe["message"].dtype, 'O') # Python object type class TestGazeMovementClass(unittest.TestCase): """Test GazeMovement class.""" def test_new(self): """Test GazeMovement creation.""" abstract_gaze_movement = GazeFeatures.GazeMovement(random_gaze_positions(0)) # Check abstract GazeMovement self.assertEqual(len(abstract_gaze_movement), 0) self.assertEqual(abstract_gaze_movement.duration, 0) self.assertEqual(abstract_gaze_movement.amplitude, 0) self.assertEqual(bool(abstract_gaze_movement), False) self.assertEqual(abstract_gaze_movement.is_finished(), False) def test_finish(self): """Test GazeMovement finishing.""" abstract_gaze_movement = GazeFeatures.GazeMovement(random_gaze_positions(2)) abstract_gaze_movement_ref = abstract_gaze_movement # Check abstract GazeMovement and its reference self.assertEqual(abstract_gaze_movement.is_finished(), False) self.assertEqual(abstract_gaze_movement_ref.is_finished(), False) # Set gaze movement as finished abstract_gaze_movement.finish() # Check abstract GazeMovement and its reference self.assertEqual(abstract_gaze_movement.is_finished(), True) self.assertEqual(abstract_gaze_movement_ref.is_finished(), True) def test_message(self): """Test GazeMovement creation with message only.""" gaze_movement = GazeFeatures.GazeMovement(message='test') # Check GazeMovement self.assertEqual(len(gaze_movement), 0) self.assertEqual(gaze_movement.duration, 0) self.assertEqual(gaze_movement.amplitude, 0) self.assertEqual(bool(gaze_movement), False) self.assertEqual(gaze_movement.is_finished(), False) self.assertEqual(gaze_movement.message, 'test') class TestScanStepClass(unittest.TestCase): """Test ScanStep class.""" def test_new(self): """Test ScanStep creation.""" fixation = TestFixation(random_gaze_positions(10)) saccade = TestSaccade(random_gaze_positions(2)) scan_step = GazeFeatures.ScanStep(fixation, saccade) # Check aoi scan step creation self.assertEqual(scan_step.first_fixation, fixation) self.assertEqual(scan_step.last_saccade, saccade) self.assertGreater(scan_step.duration, 0) def build_scan_path(size, frame_dimension: tuple[float, float] = (1, 1)): """Build scan path""" scan_path = GazeFeatures.ScanPath() for i in range(size): fixation = TestFixation(random_gaze_positions(10, frame_dimension), timestamp=i) scan_path.append_fixation(fixation) saccade = TestSaccade(random_gaze_positions(2, frame_dimension), timestamp=i+1) scan_path.append_saccade(saccade) return scan_path class TestScanPathClass(unittest.TestCase): """Test ScanPath class.""" def test_new(self): """Test ScanPath creation.""" # Check aoi scan step creation scan_path = GazeFeatures.ScanPath() self.assertEqual(len(scan_path), 0) self.assertEqual(scan_path.duration, 0) def test_append(self): """Test ScanPath append methods.""" scan_path = GazeFeatures.ScanPath() # Append a saccade that should be ignored saccade = TestSaccade(random_gaze_positions(2)) new_step = scan_path.append_saccade(saccade) # Check that no scan step have been created yet self.assertEqual(len(scan_path), 0) self.assertEqual(scan_path.duration, 0) self.assertEqual(new_step, None) # Append first fixation fixation_A = TestFixation(random_gaze_positions(10)) new_step = scan_path.append_fixation(fixation_A) # Check that no scan step have been created yet self.assertEqual(len(scan_path), 0) self.assertEqual(scan_path.duration, 0) self.assertEqual(new_step, None) # Append consecutive saccade saccade_A = TestSaccade(random_gaze_positions(2)) new_step_A = scan_path.append_saccade(saccade_A) # Check that new scan step have been created self.assertEqual(len(scan_path), 1) self.assertEqual(scan_path.duration, new_step_A.duration) self.assertEqual(new_step_A.first_fixation, fixation_A) self.assertEqual(new_step_A.last_saccade, saccade_A) self.assertEqual(new_step_A.duration, fixation_A.duration + saccade_A.duration) # Append 2 consecutive fixations then a saccade fixation_B1 = TestFixation(random_gaze_positions(10)) new_step = scan_path.append_fixation(fixation_B1) # Check that no scan step have been created yet self.assertEqual(len(scan_path), 1) self.assertEqual(scan_path.duration, new_step_A.duration) self.assertEqual(new_step, None) fixation_B2 = TestFixation(random_gaze_positions(10)) new_step = scan_path.append_fixation(fixation_B2) # Check that no scan step have been created yet self.assertEqual(len(scan_path), 1) self.assertEqual(scan_path.duration, new_step_A.duration) self.assertEqual(new_step, None) saccade_B = TestSaccade(random_gaze_positions(2)) new_step_B = scan_path.append_saccade(saccade_B) # Check that new scan step have been created self.assertEqual(len(scan_path), 2) self.assertEqual(scan_path.duration, new_step_A.duration + new_step_B.duration) self.assertEqual(new_step_B.first_fixation, fixation_B2) self.assertEqual(new_step_B.last_saccade, saccade_B) self.assertEqual(new_step_B.duration, fixation_B2.duration + saccade_B.duration) class TestAOIScanStepClass(unittest.TestCase): """Test AOIScanStep class.""" def test_new(self): """Test AOIScanStep creation.""" movements = GazeFeatures.TimeStampedGazeMovements() fixation = TestFixation(random_gaze_positions(10)) ts = fixation[0].timestamp movements.append(fixation) saccade = TestSaccade(random_gaze_positions(2)) ts = saccade[0].timestamp movements.append(saccade) aoi_scan_step = GazeFeatures.AOIScanStep(movements, 'Test') # Check aoi scan step creation self.assertEqual(len(aoi_scan_step.movements), 2) self.assertEqual(aoi_scan_step.aoi, 'Test') self.assertEqual(aoi_scan_step.first_fixation, fixation) self.assertEqual(aoi_scan_step.last_saccade, saccade) self.assertGreater(aoi_scan_step.duration, 0) def test_error(self): """Test AOIScanStep creation error.""" movements = GazeFeatures.TimeStampedGazeMovements() saccade = TestSaccade(random_gaze_positions(2)) ts = saccade[0].timestamp movements.append(saccade) fixation = TestFixation(random_gaze_positions(10)) ts = fixation[0].timestamp movements.append(fixation) # Check that aoi scan step creation fail with self.assertRaises(GazeFeatures.AOIScanStepError): aoi_scan_step = GazeFeatures.AOIScanStep(movements, 'Test') def build_aoi_scan_path(expected_aoi, aoi_path): """Build AOI scan path""" aoi_scan_path = GazeFeatures.AOIScanPath(expected_aoi) # Append a hidden last step to allow last given step creation aoi_path.append(aoi_path[-2]) for i, aoi in enumerate(aoi_path): fixation = TestFixation(random_gaze_positions(10), timestamp=i) aoi_scan_path.append_fixation(fixation, aoi) saccade = TestSaccade(random_gaze_positions(2), timestamp=i+1) aoi_scan_path.append_saccade(saccade) return aoi_scan_path class TestAOIScanPathClass(unittest.TestCase): """Test AOIScanPath class.""" def test_new(self): """Test AOIScanPath creation.""" # Check aoi scan step creation aoi_scan_path = GazeFeatures.AOIScanPath() self.assertEqual(len(aoi_scan_path), 0) def test_append(self): """Test AOIScanPath append methods.""" aoi_scan_path = GazeFeatures.AOIScanPath(['Foo', 'Bar']) # Append fixation on A aoi fixation_A = TestFixation(random_gaze_positions(10)) new_step = aoi_scan_path.append_fixation(fixation_A, 'Foo') # Check that no aoi scan step have been created yet self.assertEqual(len(aoi_scan_path), 0) self.assertEqual(aoi_scan_path.duration, 0) self.assertEqual(new_step, None) # Append saccade saccade_A = TestSaccade(random_gaze_positions(2)) new_step = aoi_scan_path.append_saccade(saccade_A) # Check that no aoi scan step have been created yet self.assertEqual(len(aoi_scan_path), 0) self.assertEqual(aoi_scan_path.duration, 0) self.assertEqual(new_step, None) # Append fixation on B aoi fixation_B = TestFixation(random_gaze_positions(10)) new_step_A = aoi_scan_path.append_fixation(fixation_B, 'Bar') # Check a first aoi scan step have been created once a new fixation is appened self.assertEqual(len(aoi_scan_path), 1) self.assertEqual(aoi_scan_path.duration, new_step_A.duration) self.assertEqual(len(new_step_A.movements), 2) self.assertEqual(new_step_A.aoi, 'Foo') self.assertEqual(new_step_A.letter, 'A') self.assertEqual(new_step_A.duration, saccade_A[-1].timestamp - fixation_A[0].timestamp) # Check letter affectation self.assertEqual(aoi_scan_path.get_letter_aoi('A'), 'Foo') def test_append_error(self): """Test AOIScanPath append error.""" aoi_scan_path = GazeFeatures.AOIScanPath(['Foo', 'Bar']) # Append fixation on A aoi fixation = TestFixation(random_gaze_positions(10)) new_step = aoi_scan_path.append_fixation(fixation, 'Foo') # Check that no aoi scan step have been created yet self.assertEqual(len(aoi_scan_path), 0) self.assertEqual(new_step, None) # Append fixation on B aoi fixation = TestFixation(random_gaze_positions(10)) # Check that aoi scan step creation fail when fixation is appened after another fixation with self.assertRaises(GazeFeatures.AOIScanStepError): new_step = aoi_scan_path.append_fixation(fixation, 'Bar') # Check that unexpected aoi scan step creation fail with self.assertRaises(GazeFeatures.AOIScanStepError): new_step = aoi_scan_path.append_fixation(fixation, 'Shu') def test_letter_index_and_string_reprentation(self): """Test AOIScanPath letter index and string representation feature.""" aoi_scan_path = build_aoi_scan_path(['Foo', 'Bar', 'Shu'], ['Bar', 'Shu', 'Foo', 'Bar']) # Check first step self.assertEqual(aoi_scan_path[0].aoi, 'Bar') self.assertEqual(aoi_scan_path[0].letter, 'A') # Check second step self.assertEqual(aoi_scan_path[1].aoi, 'Shu') self.assertEqual(aoi_scan_path[1].letter, 'B') # Check thrird step self.assertEqual(aoi_scan_path[2].aoi, 'Foo') self.assertEqual(aoi_scan_path[2].letter, 'C') # Check fourth step self.assertEqual(aoi_scan_path[3].aoi, 'Bar') self.assertEqual(aoi_scan_path[3].letter, 'A') # Check letter affectation self.assertEqual(aoi_scan_path.get_letter_aoi('A'), 'Bar') self.assertEqual(aoi_scan_path.get_letter_aoi('B'), 'Shu') self.assertEqual(aoi_scan_path.get_letter_aoi('C'), 'Foo') # Check letter sequence representation self.assertEqual(aoi_scan_path.letter_sequence, 'ABCA') def test_transition_matrix(self): """Test AOIScanPath transition matrix feature.""" aoi_scan_path = build_aoi_scan_path(['Foo', 'Bar', 'Shu'], ['Bar', 'Shu', 'Foo']) # Check transition matrix ([destination][departure]) self.assertEqual(aoi_scan_path.transition_matrix['Foo']['Foo'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Bar']['Bar'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Shu']['Shu'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Foo']['Bar'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Foo']['Shu'], 1) self.assertEqual(aoi_scan_path.transition_matrix['Bar']['Foo'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Bar']['Shu'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Shu']['Foo'], 0) self.assertEqual(aoi_scan_path.transition_matrix['Shu']['Bar'], 1) def test_transition_matrix(self): """Test AOIScanPath fixations count feature.""" aoi_scan_path = build_aoi_scan_path(['Foo', 'Bar', 'Shu'], ['Bar', 'Shu', 'Foo']) # Check fixations count scan_fixations_count, aoi_fixations_count = aoi_scan_path.fixations_count() self.assertEqual(scan_fixations_count, 3) self.assertEqual(aoi_fixations_count['Foo'], 1) self.assertEqual(aoi_fixations_count['Bar'], 1) self.assertEqual(aoi_fixations_count['Shu'], 1) if __name__ == '__main__': unittest.main()