def test_dynamic_registration_named(state, dummy_checks): diagnose_calls = 0 # this makes diagnose a LazyChain wrapper @state_dec_gen(dummy_checks) def diagnose(end_state): assert end_state.state_history[0] is state nonlocal diagnose_calls diagnose_calls += 1 TestEx = ExGen(dummy_checks, state) TestF = LazyChainStart(dummy_checks) TestEx.register_chainable_function(diagnose, "test123") TestEx().test123() TestEx() >> TestF().test123() TestEx() >> diagnose() assert diagnose_calls == 3 with pytest.raises(AttributeError): # The function is registered with a given name (test123) TestEx().diagnose() with pytest.raises(NameError): TestEx() >> test123()
def test_multiple_dynamic_registrations(state, dummy_checks): diagnose_calls = 0 state_dec = state_dec_gen(dummy_checks) @state_dec def diagnose1(end_state): assert end_state.state_history[0] is state nonlocal diagnose_calls diagnose_calls += 1 @state_dec def diagnose2(end_state): assert end_state.state_history[0] is state nonlocal diagnose_calls diagnose_calls += 1 TestEx = ExGen(dummy_checks, state) TestF = LazyChainStart(dummy_checks) TestEx.register_chainable_function(diagnose1) TestEx.register_chainable_function(diagnose2) TestEx().diagnose1().diagnose2() TestEx() >> TestF().diagnose1().diagnose2() TestEx() >> diagnose1().diagnose2() TestEx() >> diagnose1() >> diagnose2() assert diagnose_calls == 8 TestEx().diagnose1().noop().diagnose2().noop() TestEx() >> TestF().diagnose1().noop().diagnose2().noop() TestEx() >> diagnose1().noop() >> diagnose2().noop() assert diagnose_calls == 14 TestEx().child_state().diagnose1().diagnose2()
def test_dynamic_registration(state, dummy_checks): diagnose_calls = 0 @state_dec_gen(dummy_checks) def diagnose(end_state): assert end_state.state_history[0] is state nonlocal diagnose_calls diagnose_calls += 1 TestEx = ExGen(dummy_checks, state) TestF = LazyChainStart(dummy_checks) TestEx.register_chainable_function(diagnose) TestEx().diagnose() TestEx() >> TestF().diagnose() TestEx() >> diagnose() assert diagnose_calls == 3 TestEx().diagnose().noop() TestEx() >> TestF().diagnose().noop() TestEx() >> diagnose().noop() assert diagnose_calls == 6 TestEx().child_state().diagnose()
def test_create_embed_context(state, dummy_checks): # Given Ex = ExGen(dummy_checks, state) assert Ex()._state == state def derive_custom_state_args(parent_state): assert parent_state == state return {"student_code": "override"} # When embed_context = create_embed_context( "proto", Ex(), derive_custom_state_args=derive_custom_state_args, ) # Then assert isinstance(embed_context["get_bash_history"](), LazyChain) assert isinstance(embed_context["F"]().get_bash_history(), LazyChain) embed_state = embed_context["Ex"].root_state assert isinstance(embed_state, State) assert embed_state.student_code == "override" assert embed_state.reporter.runner == state.reporter assert embed_state.creator == {"type": "embed", "args": {"state": state}}
def test_sct_reflection(dummy_checks): def diagnose(_): raise RuntimeError("This is not run") sct_dict = {"diagnose": diagnose, **dummy_checks} Ex = ExGen(sct_dict, None, strict=False) F = LazyChainStart(sct_dict) chain_part_1 = Ex().noop().child_state() assert str(chain_part_1) == "child_state()" chain_part_2 = F().diagnose().fail() assert str(chain_part_2) == "diagnose().fail()" chain_part_3 = F().noop() assert str(chain_part_3) == "noop()" chain_part_2_and_3 = chain_part_2 >> chain_part_3 assert str(chain_part_2_and_3) == "diagnose().fail().noop()" chain = chain_part_1 >> chain_part_2_and_3 assert str(chain) == str(chain_part_2_and_3) assert str(chain_part_1) == "child_state().diagnose().fail().noop()" assert str(Ex) == "Ex().noop().child_state().diagnose().fail().noop()"
def test_dynamic_registration_raising(state, dummy_checks): @state_dec_gen(dummy_checks) def diagnose(_): raise InstructorError.from_message("problem") TestEx = ExGen(dummy_checks, state) TestEx.register_chainable_function(diagnose) with pytest.raises(InstructorError) as e: TestEx().diagnose() # If calls with a state argument in state_dec would be decorated with link_to_state, # there would be a double link_to_state call when an sct decorated with state_dec # is registered for chaining assert len(re.findall("Debug", str(e.value))) == 1 assert len(e.value.state_history) == 2
def test_state_linking_root_creator_noop(state, dummy_checks): def diagnose(end_state): assert end_state.creator is None sct_dict = {"diagnose": diagnose, **dummy_checks} TestEx = ExGen(sct_dict, state) TestF = LazyChainStart(sct_dict) TestEx().noop() >> TestF().diagnose()
def test_delayed_debug(state, dummy_checks): Ex = ExGen(state, {"_debug": _debug, **dummy_checks}) try: Ex()._debug("breakpoint name", on_error=True).noop().child_state().fail() assert False except TF as e: assert "history" in str(e) assert "child_state" in str(e) assert "test" in str(e)
def test_state_linking_root_creator_child_state(state, dummy_checks): def diagnose(end_state): assert end_state != state assert end_state.parent_state is state assert len(end_state.state_history) == 2 assert state == end_state.state_history[0] assert end_state == end_state.state_history[1] TestEx = ExGen(state, dummy_checks) TestEx().child_state() >> F(attr_scts={"diagnose": diagnose}).diagnose()
def test_get_embed_chain_constructors(state, dummy_checks): # Given Ex = ExGen(dummy_checks, state) assert Ex()._state == state # When EmbedEx, EmbedF = get_embed_chain_constructors("proto", Ex()) # Then assert isinstance(EmbedEx(), EagerChain) assert isinstance(EmbedF(), LazyChain)
def test_state_linking_root_creator_child_state(state, dummy_checks): def diagnose(end_state): assert end_state != state assert end_state.parent_state is state assert len(end_state.state_history) == 2 assert state == end_state.state_history[0] assert end_state == end_state.state_history[1] sct_dict = {"diagnose": diagnose, **dummy_checks} TestEx = ExGen(sct_dict, state) TestF = LazyChainStart(sct_dict) TestEx().child_state() >> TestF().diagnose()
def test_debug(state, dummy_checks): state.do_test(Success("msg")) sct_dict = {"_debug": _debug, **dummy_checks} Ex = ExGen(sct_dict, state) try: Ex().noop().child_state() >> LazyChain( chainable_functions=sct_dict)._debug("breakpoint name") assert False except InstructorError as e: assert "breakpoint name" in str(e) assert "history" in str(e) assert "child_state" in str(e) assert "test" in str(e)
def test_debug(state, dummy_checks): state.do_test(Success("msg")) Ex = ExGen(state, dummy_checks) try: Ex().noop().child_state() >> F(attr_scts={"_debug": _debug})._debug( "breakpoint name" ) assert False except TF as e: assert "breakpoint name" in str(e) assert "history" in str(e) assert "child_state" in str(e) assert "test" in str(e)
def create_sct_context(sct_dict, root_state: State = None) -> Dict[str, Callable]: """ Create the globals that will be available when running the SCT. Args: sct_dict: a dictionary of the functions to make available root_state: a State instance with the exercise information available to the SCT Returns: dict: the globals available to the SCT code """ state_dec = state_dec_gen(sct_dict) sct_ctx = {k: state_dec(v) for k, v in sct_dict.items()} ctx = { **sct_ctx, "state_dec": state_dec, # needed by ext packages "Ex": ExGen(sct_dict, root_state), "F": LazyChainStart(sct_dict), } return ctx
def test_final_debug(state, dummy_checks): Ex = ExGen({"_debug": _debug, **dummy_checks}, state) Ex()._debug("breakpoint name", on_error=True).noop().child_state() assert state.reporter.fail
def get_chains(): return { "Ex": ExGen(sct_dict, State.root_state), "F": LazyChainStart(sct_dict), }
from protowhat.failure import TestFail as TF from protowhat.selectors import Dispatcher from protowhat.State import State from protowhat.Reporter import Reporter from protowhat.sct_syntax import LazyChain, ExGen from protowhat.checks import check_files as cf from protowhat.checks.check_funcs import check_node, has_code # TODO: selectors require a _priority attribute # this is a holdover from the sql ast modules ast.Expr._priority = 0 DUMMY_NODES = {"Expr": ast.Expr} Ex = ExGen({}, None) class ParseHey: ParseError = SyntaxError def parse(self, code, *args, **kwargs): return ast.parse(code) def assert_equal_ast(a, b): assert ast.dump(a) == ast.dump(b) @pytest.fixture def temp_file_sum():
from protowhat.State import State from protowhat.sct_syntax import ExGen, F, state_dec_gen import pytest state_dec = state_dec_gen(State, {}) Ex = ExGen(State, {}) @pytest.fixture def addx(): return lambda state, x: state + x @pytest.fixture def f(): return F._from_func(lambda state, b: state + b, b='b') @pytest.fixture def f2(): return F._from_func(lambda state, c: state + c, c='c') def test_f_from_func(f): assert f('a') == 'ab' def test_f_sct_copy_kw(addx): assert F()._sct_copy(addx)(x='x')('state') == 'statex'
def test_state_linking_root_creator_noop(state, dummy_checks): def diagnose(end_state): assert end_state.creator is None TestEx = ExGen(state, dummy_checks) TestEx().noop() >> F(attr_scts={"diagnose": diagnose}).diagnose()
import pytest from protowhat.failure import InstructorError from protowhat.sct_syntax import ( ChainedCall, ChainExtender, EagerChain, ExGen, LazyChain, LazyChainStart, state_dec_gen, ) from tests.helper import state, dummy_checks sct_dict = {} Ex = ExGen(sct_dict, None) state_dec = state_dec_gen(sct_dict) state = pytest.fixture(state) dummy_checks = pytest.fixture(dummy_checks) @pytest.fixture def addx(): return lambda state, x: state + x @pytest.fixture def f(): return LazyChain(ChainedCall(lambda state, b: state + b, kwargs={"b": "b"}))