def test_disabled_routine_is_not_written_to_script(self): # Make experiment and two test routines exp = Experiment() rt1 = UnknownRoutine(exp, name="testRoutine1") rt2 = UnknownRoutine(exp, name="testRoutine2") # Disable one routine rt1.params['disabled'].val = True rt2.params['disabled'].val = False # Add routines to expriment exp.addStandaloneRoutine("testRoutine1", rt1) exp.flow.addRoutine(rt1, 0) exp.addStandaloneRoutine("testRoutine2", rt2) exp.flow.addRoutine(rt2, 0) # Write python script pyScript = exp.writeScript(target="PsychoPy") # Check that one routine is present and the other is not assert "testRoutine1" not in pyScript and "testRoutine2" in pyScript
class TestMouseComponent: """ Test that Mouse coponents have the correct params and write as expected. """ def setup(self): # Make blank experiment self.exp = Experiment() # Make blank routine self.routine = Routine(name="testRoutine", exp=self.exp) self.exp.addRoutine("testRoutine", self.routine) self.exp.flow.addRoutine(self.routine, 0) # Add loop around routine self.loop = TrialHandler(exp=self.exp, name="testLoop") self.exp.flow.addLoop(self.loop, 0, -1) # Make Mouse component self.comp = MouseComponent(exp=self.exp, parentName="testRoutine", name="testMouse") self.routine.addComponent(self.comp) # Make a rect for when we need something to click on self.target = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon") self.routine.addComponent(self.target) def test_click_save_end_clickable_cases(self): """ Test all combinations of options for what to save, what can be clicked on & what kind of clicks to end the routine on. """ saveMouseStateCases = [ { 'val': "final", 'want': ["thisExp.addData('testMouse.x', x)" ], # should contain code for adding final value of x 'avoid': ["testMouse.x.append(x)"] }, # should not contain code to update testMouse.x in frame loop { 'val': "on click", 'want': [ "thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end "testMouse.x.append(x)" ], # should contain code to update testMouse.x in frame loop 'avoid': ["thisExp.addData('testMouse.x', x)"] }, # should not add final value of x { 'val': "on valid click", 'want': [ "thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end "testMouse.x.append(x)", # should contain code to update testMouse.x in frame loop "if gotValidClick:" ], # should check for valid clicks 'avoid': ["thisExp.addData('testMouse.x', x)"] }, # should not add final value of x { 'val': "every frame", 'want': [ "thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end "testMouse.x.append(x)" ], # should contain code to update testMouse.x in frame loop 'avoid': ["thisExp.addData('testMouse.x', x)"] }, # should not add final value of x { 'val': "never", 'want': [], 'avoid': [ "thisExp.addData('testMouse.x', testMouse.x)", # should not add testMouse.x at the end "testMouse.x.append(x)", # should not contain code to update testMouse.x in frame loop "thisExp.addData('testMouse.x', x)" ] }, # should not add final value of x]}, ] forceEndRoutineOnPressCases = [ { 'val': "never", 'want': [], 'avoid': [ "# abort routine on response", # should not include code to abort routine "# abort routine on response" ] }, { 'val': "any click", 'want': ["# abort routine on response" ], # should include code to abort routine on response 'avoid': [] }, { 'val': "valid click", 'want': [ "# abort routine on response", # should include code to abort routine on response "if gotValidClick:" ], # should check for valid clicks 'avoid': [] }, ] clickableCases = [ { 'val': "[]", 'want': [], 'avoid': ["clickableList = [testPolygon]"] }, # should not define a populated clickables list { 'val': "[testPolygon]", 'want': [], 'avoid': ["clickableList = []"] }, # should not define a blank clickables list ] # Iterate through saveMouseState cases for SMScase in saveMouseStateCases: # Set saveMouseState self.comp.params['saveMouseState'].val = SMScase['val'] for FEROPcase in forceEndRoutineOnPressCases: # Set forceEndRoutineOnPress self.comp.params['forceEndRoutineOnPress'].val = FEROPcase[ 'val'] for Ccase in clickableCases: # Set clickable self.comp.params['clickable'].val = Ccase['val'] # Compile script script = self.exp.writeScript(target="PsychoPy") try: # Look for wanted phrases for phrase in SMScase['want']: assert phrase in script, ( f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") for phrase in FEROPcase['want']: assert phrase in script, ( f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") for phrase in Ccase['want']: assert phrase in script, ( f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") # Check there's no avoid phrases for phrase in SMScase['avoid']: assert phrase not in script, ( f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") for phrase in FEROPcase['avoid']: assert phrase not in script, ( f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") for phrase in Ccase['avoid']: assert phrase not in script, ( f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, " f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and " f"clickable={self.comp.params['clickable']}") except AssertionError as err: # If any assertion fails, save script to view filename = Path( TESTS_DATA_PATH ) / f"{__class__.__name__}_clicks_cases_local.py" with open(filename, "w") as f: f.write(script) # Append ref to saved script in error message print(f"Script saved at: {filename}") # Raise original error raise err
def test_get_resources_js(): cases = [ # Resource not handled, no loop present {'exp': "unhandled_noloop", 'seek': ['blue.png'], 'avoid': ['white.png', 'yellow.png', 'groups.csv', 'groupA.csv', 'groupB.csv']}, # Resource not handled, loop defined by string {'exp': "unhandled_strloop", 'seek': ['blue.png', 'white.png', 'groupA.csv'], 'avoid': ['yellow.png', 'groupB.csv', 'groups.csv']}, # Resource not handled, loop defined by constructed string {'exp': "unhandled_constrloop", 'seek': ['blue.png', 'white.png', 'yellow.png', 'groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': []}, # Resource not handled, loop defined by constructed string from loop {'exp': "unhandled_recurloop", 'seek': ['blue.png', 'white.png', 'yellow.png', 'groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': []}, # Resource handled by static component, no loop present {'exp': "handledbystatic_noloop", 'seek': [], 'avoid': ['blue.png', 'white.png', 'yellow.png', 'groups.csv', 'groupA.csv', 'groupB.csv']}, # Resource handled by static component, loop defined by string {'exp': "handledbystatic_strloop", 'seek': ['groupA.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png', 'groupB.csv', 'groups.csv']}, # Resource handled by static component, loop defined by constructed string {'exp': "handledbystatic_constrloop", 'seek': ['groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png']}, # Resource handled by static component, loop defined by constructed string from loop {'exp': "handledbystatic_recurloop", 'seek': ['groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png']}, # Resource handled by resource manager component, no loop present {'exp': "handledbyrm_noloop", 'seek': [], 'avoid': ['blue.png', 'white.png', 'yellow.png', 'groups.csv', 'groupA.csv', 'groupB.csv']}, # Resource handled by resource manager component, loop defined by constructed string {'exp': "handledbyrm_strloop", 'seek': ['groupA.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png', 'groupB.csv', 'groups.csv']}, # Resource handled by resource manager component, loop defined by constructed string {'exp': "handledbyrm_constrloop", 'seek': ['groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png']}, # Resource handled by resource manager component, loop defined by constructed string from loop {'exp': "handledbyrm_recurloop", 'seek': ['groupA.csv', 'groupB.csv', 'groups.csv'], 'avoid': ['blue.png', 'white.png', 'yellow.png']}, ] exp = Experiment() for case in cases: # Load experiment exp.loadFromXML(Path(TESTS_DATA_PATH) / "test_get_resources" / (case['exp'] + ".psyexp")) # Write to JS script = exp.writeScript(target="PsychoJS") # Extract resources def at start of experiment resources = re.search("(?<=resources: \[)[^\]]*", script).group(0) # Check that all "seek" phrases are included for phrase in case['seek']: assert phrase in resources, f"'{phrase}' was not found in resources for {case['exp']}.psyexp" # Check that all "avoid" phrases are excluded for phrase in case['avoid']: assert phrase not in resources, f"'{phrase}' was found in resources for {case['exp']}.psyexp"
def test_muting(self): """ Test that component and standalone routines are muted under the correct conditions (i.e. if target is unimplemented or if disabled) """ # Make experiment to hold everything exp = Experiment() comp_rt = Routine("comp_rt", exp) comp_rt = exp.addRoutine("comp_rt", comp_rt) exp.flow.append(comp_rt) # Define some routines/components which should or should not compile exemplars = [] # standalone routine + disabled + no target obj = UnknownRoutine(exp, name="test_rt_disabled_notarget") obj.disabled = True obj.targets = [] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": []}) # standalone routine + disabled + py target obj = UnknownRoutine(exp, name="test_rt_disabled_pytarget") obj.disabled = True obj.targets = ['PsychoPy'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": []}) # standalone routine + disabled + js target obj = UnknownRoutine(exp, name="test_rt_disabled_jstarget") obj.disabled = True obj.targets = ['PsychoJS'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": []}) # standalone routine + disabled + both targets obj = UnknownRoutine(exp, name="test_rt_disabled_bothtarget") obj.disabled = True obj.targets = ['PsychoPy', 'PsychoJS'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": []}) # standalone routine + enabled + no target obj = UnknownRoutine(exp, name="test_rt_enabled_notarget") obj.disabled = False obj.targets = [] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # standalone routine + enabled + py target obj = UnknownRoutine(exp, name="test_rt_enabled_pytarget") obj.disabled = False obj.targets = ['PsychoPy'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # standalone routine + enabled + js target obj = UnknownRoutine(exp, name="test_rt_enabled_jstarget") obj.disabled = False obj.targets = ['PsychoJS'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # standalone routine + enabled + both target obj = UnknownRoutine(exp, name="test_rt_enabled_bothtarget") obj.disabled = False obj.targets = ['PsychoPy', 'PsychoJS'] exp.addStandaloneRoutine(obj.name, obj) exp.flow.append(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # component + disabled + no target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_disabled_notarget") obj.disabled = True obj.targets = [] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": []}) # component + disabled + py target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_disabled_pytarget") obj.disabled = True obj.targets = ['PsychoPy'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": []}) # component + disabled + js target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_disabled_jstarget") obj.disabled = True obj.targets = ['PsychoJS'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": []}) # component + disabled + both target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_disabled_bothtarget") obj.disabled = True obj.targets = ['PsychoPy', 'PsychoJS'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": []}) # component + enabled + no target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_enabled_notarget") obj.disabled = False obj.targets = [] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # component + enabled + py target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_enabled_pytarget") obj.disabled = False obj.targets = ['PsychoPy'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # component + enabled + js target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_enabled_jstarget") obj.disabled = False obj.targets = ['PsychoJS'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": obj.targets}) # component + enabled + both target obj = UnknownComponent(exp, parentName="comp_rt", name="test_cmp_enabled_bothtarget") obj.disabled = False obj.targets = ['PsychoPy', 'PsychoJS'] comp_rt.addComponent(obj) exemplars.append({"obj": obj, "ans": obj.targets}) tykes = [] # Compile experiment pyScript = exp.writeScript(target="PsychoPy") # jsScript = exp.writeScript(target="PsychoJS") ## disabled until js can compile without saving # Test all cases for case in exemplars + tykes: # Check Python script if "PsychoPy" in case['ans']: assert case['obj'].name in pyScript, ( f"{case['obj']} not found in Python script when it should be." ) else: assert case['obj'].name not in pyScript, ( f"{case['obj']} found in Python script when it should not be." )
class TestPolygonComponent(_TestBaseComponentsMixin, _TestDisabledMixin): """ Test that Polygon coponents have the correct params and write as expected. """ def setup(self): # Make blank experiment self.exp = Experiment() # Make blank routine self.routine = Routine(name="testRoutine", exp=self.exp) self.exp.addRoutine("testRoutine", self.routine) self.exp.flow.addRoutine(self.routine, 0) # Add loop around routine self.loop = TrialHandler(exp=self.exp, name="testLoop") self.exp.flow.addLoop(self.loop, 0, -1) # Make a rect for when we need something to click on self.comp = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon") self.routine.addComponent(self.comp) def test_vertices_usage(self): """ Test that vertices values are used only under the correct conditions """ # Define values to look for and avoid in code according to value of shape cases = [ # Shape is a line { 'val': "line", 'seek': ["visual.Line("], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "triangle", 'seek': ["visual.ShapeStim("], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "rectangle", 'seek': ["visual.Rect("], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "circle", 'seek': ["visual.ShapeStim(", "vertices='circle'"], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "cross", 'seek': ["visual.ShapeStim(", "vertices='cross'"], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "star", 'seek': ["visual.ShapeStim(", "vertices='star7'"], 'avoid': ["___nVertices___", "___vertices___"] }, { 'val': "regular polygon...", 'seek': ["___nVertices___", "visual.Polygon("], 'avoid': ["___vertices___"] }, { 'val': "custom polygon...", 'seek': ["___vertices___", "visual.ShapeStim("], 'avoid': ["___nVertices___"] }, ] # Setup component with markers for nVertices and vertices self.comp.params['nVertices'].val = "___nVertices___" self.comp.params['vertices'].val = "___vertices___" # Test each case for case in cases: # Set shape self.comp.params['shape'].val = case['val'] # Write experiment pyScript = self.exp.writeScript(target="PsychoPy") # Look for sought values in experiment script for seekVal in case['seek']: assert seekVal in pyScript, ( f"Could not find wanted value `{seekVal}` in experiment when polygon shape was {case['val']}." ) # Look for avoid values in experiment script for avoidVal in case['avoid']: assert avoidVal not in pyScript, ( f"Found unwanted value `{avoidVal}` in experiment when polygon shape was {case['val']}." )