#!/usr/bin/env python import unittest from argaze import GazeFeatures import numpy def random_gaze_positions(size): """ 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(), random.random())) # Store gaze position ts_gaze_positions[time.time()] = random_gaze_position return ts_gaze_positions class TestGazePositionClass(unittest.TestCase): """Test GazePosition class.""" def test_new(self): """Test GazePosition creation.""" # Check empty GazePosition empty_gaze_position = GazeFeatures.GazePosition() self.assertEqual(empty_gaze_position.value, (0, 0)) self.assertEqual(empty_gaze_position[0], 0) self.assertEqual(empty_gaze_position[1], 0) for v in empty_gaze_position: self.assertEqual(v, 0) self.assertEqual(empty_gaze_position.precision, 0.) self.assertEqual(len(empty_gaze_position), 2) self.assertEqual(empty_gaze_position.valid, True) self.assertEqual(numpy.array(empty_gaze_position).shape, (2,)) # Check integer GazePosition int_gaze_position = GazeFeatures.GazePosition((123, 456), precision=55) self.assertEqual(int_gaze_position.value, (123, 456)) self.assertEqual(int_gaze_position[0], 123) self.assertEqual(int_gaze_position[1], 456) self.assertEqual(int_gaze_position.precision, 55) self.assertEqual(len(int_gaze_position), 2) self.assertEqual(int_gaze_position.valid, True) self.assertEqual(numpy.array(empty_gaze_position).shape, (2,)) # Check float GazePosition float_gaze_position = GazeFeatures.GazePosition((1.23, 4.56), precision=5.5) self.assertEqual(float_gaze_position.value, (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.precision, 5.5) self.assertEqual(len(float_gaze_position), 2) self.assertEqual(float_gaze_position.valid, True) self.assertEqual(numpy.array(empty_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 = (123, 456) self.assertNotEqual(gaze_position.value, (123, 456)) self.assertEqual(gaze_position.value, (0, 0)) 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.overlap(gaze_position_B)) self.assertTrue(gaze_position_A.overlap(gaze_position_C)) self.assertFalse(gaze_position_A.overlap(gaze_position_C, both=True)) self.assertFalse(gaze_position_B.overlap(gaze_position_A)) self.assertTrue(gaze_position_B.overlap(gaze_position_C)) self.assertFalse(gaze_position_B.overlap(gaze_position_C, both=True)) self.assertFalse(gaze_position_C.overlap(gaze_position_A)) self.assertFalse(gaze_position_C.overlap(gaze_position_B)) def test___repr__(self): """Test GazePosition string representation.""" # Check empty GazePosition representation self.assertEqual(repr(GazeFeatures.GazePosition()), "{\"value\": [0, 0], \"precision\": 0.0}") class TestUnvalidGazePositionClass(unittest.TestCase): """Test UnvalidGazePosition class.""" def test_new(self): """Test UnvalidGazePosition creation.""" import math unvalid_gaze_position = GazeFeatures.UnvalidGazePosition() self.assertEqual(unvalid_gaze_position.value, (None, None)) self.assertEqual(unvalid_gaze_position.precision, None) self.assertEqual(unvalid_gaze_position.valid, False) def test___repr__(self): """Test UnvalidGazePosition string representation.""" self.assertEqual(repr(GazeFeatures.UnvalidGazePosition()), "{\"message\": null, \"value\": [null, null], \"precision\": null}") class TestTimeStampedGazePositionsClass(unittest.TestCase): """Test TimeStampedGazePositions class.""" def test___setitem__(self): """Test __setitem__ method.""" ts_gaze_positions = GazeFeatures.TimeStampedGazePositions() ts_gaze_positions[0] = GazeFeatures.GazePosition() ts_gaze_positions[1] = GazeFeatures.UnvalidGazePosition() ts_gaze_positions[2] = {"value": (0, 0), "precision": 0.} # Check GazePosition is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[0], GazeFeatures.GazePosition) self.assertEqual(ts_gaze_positions[0].valid, True) # Check UnvalidGazePosition is correctly stored and accessible as a UnvalidGazePosition self.assertIsInstance(ts_gaze_positions[1], GazeFeatures.UnvalidGazePosition) self.assertEqual(ts_gaze_positions[1].valid, False) # Check dict with "value" and "precision" keys is correctly stored and accessible as a GazePosition self.assertIsInstance(ts_gaze_positions[2], GazeFeatures.GazePosition) self.assertEqual(ts_gaze_positions[2].valid, True) # Check that bad data type insertion fails with self.assertRaises(AssertionError): ts_gaze_positions[3] = "This string is not a gaze position value." # Check that dict with bad keys insertion fails with self.assertRaises(AssertionError): ts_gaze_positions[4] = {"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()), "{}") ts_gaze_positions[0] = GazeFeatures.GazePosition() self.assertEqual(repr(ts_gaze_positions), "{\"0\": {\"value\": [0, 0], \"precision\": 0.0}}") ts_gaze_positions[0] = GazeFeatures.UnvalidGazePosition() self.assertEqual(repr(ts_gaze_positions), "{\"0\": {\"message\": null, \"value\": [null, null], \"precision\": null}}") 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, 2) self.assertEqual(ts_gaze_positions_dataframe.columns[0], "value") self.assertEqual(ts_gaze_positions_dataframe.columns[1], "precision") self.assertEqual(ts_gaze_positions_dataframe["value"].dtype, 'object') self.assertEqual(ts_gaze_positions_dataframe["precision"].dtype, 'float64') # Check unvalid position conversion ts_gaze_positions = GazeFeatures.TimeStampedGazePositions() ts_gaze_positions[0] = GazeFeatures.UnvalidGazePosition() 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, 2) self.assertEqual(ts_gaze_positions_dataframe.columns[0], "value") self.assertEqual(ts_gaze_positions_dataframe.columns[1], "precision") self.assertEqual(ts_gaze_positions_dataframe["value"].dtype, 'object') self.assertEqual(ts_gaze_positions_dataframe["precision"].dtype, 'O') # Python object type class TestVisualScanStepClass(unittest.TestCase): """Test VisualScanStep class.""" def test_new(self): """Test VisualScanStep creation.""" movements = GazeFeatures.TimeStampedGazeMovements() fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first movements[ts] = fixation saccade = GazeFeatures.Saccade(random_gaze_positions(2)) ts, _ = saccade.positions.first movements[ts] = saccade visual_scan_step = GazeFeatures.VisualScanStep(movements, 'Test') # Check visual scan step creation self.assertEqual(len(visual_scan_step.movements), 2) self.assertEqual(visual_scan_step.aoi, 'Test') self.assertEqual(visual_scan_step.first_fixation, fixation) self.assertEqual(visual_scan_step.last_saccade, saccade) self.assertGreater(visual_scan_step.duration, 0) def test_error(self): """Test VisualScanStep creation error.""" movements = GazeFeatures.TimeStampedGazeMovements() saccade = GazeFeatures.Saccade(random_gaze_positions(2)) ts, _ = saccade.positions.first movements[ts] = saccade fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first movements[ts] = fixation # Check that visual scan step creation fail with self.assertRaises(GazeFeatures.VisualScanStepError): visual_scan_step = GazeFeatures.VisualScanStep(movements, 'Test') class TestVisualScanPathClass(unittest.TestCase): """Test VisualScanPath class.""" def test_new(self): """Test VisualScanPath creation.""" # Check visual scan step creation visual_scan_path = GazeFeatures.VisualScanPath() self.assertEqual(len(visual_scan_path), 0) def test_append(self): """Test VisualScanPath append methods.""" visual_scan_path = GazeFeatures.VisualScanPath() # Append fixation on A aoi fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first new_step = visual_scan_path.append_fixation(ts, fixation, 'A') # Check that no visual scan step have been created yet self.assertEqual(len(visual_scan_path), 0) self.assertEqual(new_step, None) # Append saccade saccade = GazeFeatures.Saccade(random_gaze_positions(2)) ts, _ = saccade.positions.first new_step = visual_scan_path.append_saccade(ts, saccade) # Check that no visual scan step have been created yet self.assertEqual(len(visual_scan_path), 0) self.assertEqual(new_step, None) # Append fixation on B aoi fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first new_step = visual_scan_path.append_fixation(ts, fixation, 'B') # Check a new visual scan step have been created self.assertEqual(len(visual_scan_path), 1) self.assertEqual(len(new_step.movements), 2) self.assertEqual(new_step.aoi, 'A') def test_append_error(self): """Test VisualScanPath append error.""" visual_scan_path = GazeFeatures.VisualScanPath() # Append fixation on A aoi fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first new_step = visual_scan_path.append_fixation(ts, fixation, 'A') # Check that no visual scan step have been created yet self.assertEqual(len(visual_scan_path), 0) self.assertEqual(new_step, None) # Append fixation on B aoi fixation = GazeFeatures.Fixation(random_gaze_positions(10)) ts, _ = fixation.positions.first # Check that visual scan step creation fail with self.assertRaises(GazeFeatures.VisualScanStepError): new_step = visual_scan_path.append_fixation(ts, fixation, 'B') if __name__ == '__main__': unittest.main()