def test_signature_and_get_conf(circuit): """Test input signature related functions.""" blk1 = Noop('test1', comment=' without unnamed inputs').connect( inp2=20, inp3=30, inp1=10) blk2 = Noop('test2', comment='with unnamed inputs', ).connect( 100, 101, 102, 103, # unnamed (group '_') inpA=edzed.Const('A2'), # named single input inpB=[edzed.Const('B2')], # named sequence inpC=range(5), # named iterator ) assert 'inputs' not in blk1.get_conf() # no data before finalization init(circuit) assert blk1.input_signature() == ({'inp1': None, 'inp2': None, 'inp3': None,}) conf1 = blk1.get_conf() conf1ok = { # future versions may add additional keys to get_conf() 'class': 'Noop', 'debug': False, 'comment': ' without unnamed inputs', 'inputs': { 'inp1': "<Const 10>", 'inp2': "<Const 20>", 'inp3': "<Const 30>", }, 'name': 'test1', 'type': 'combinational', } assert all(conf1[key] == value for key, value in conf1ok.items()) assert blk2.input_signature() == ({'inpA': None, '_': 4, 'inpB': 1, 'inpC': 5}) assert blk2.get_conf()['inputs'] == { '_': ('<Const 100>', '<Const 101>', '<Const 102>', '<Const 103>'), 'inpA': "<Const 'A2'>", 'inpB': ("<Const 'B2'>",), # a 1-tuple 'inpC': ('<Const 0>', '<Const 1>', '<Const 2>', '<Const 3>', '<Const 4>'), } blk2.check_signature({'inpA': None, '_': 4, 'inpB': 1, 'inpC': 5}) blk2.check_signature({'inpA': None, '_': [4, 5], 'inpB': [None, None], 'inpC': [0, 5]}) with pytest.raises(ValueError, match="missing: 'extra'"): blk2.check_signature({'inpA': None, 'extra': None, '_': 4, 'inpB': 1, 'inpC': 5}) with pytest.raises(ValueError, match="unexpected: 'inpA'"): blk2.check_signature({'_': 4, 'inpB': 1, 'inpC': 5}) with pytest.raises(ValueError, match="count is 5, expected was 10"): blk2.check_signature({'inpA': None, '_': 4, 'inpB': 1, 'inpC': 10}) with pytest.raises(ValueError, match="did you mean 'inp_A'"): blk2.check_signature({'inp_A': None, '_': 4, 'inpB': 1, 'inpC': 5}) with pytest.raises(ValueError, match="count is 1, minimum is 2"): blk2.check_signature({'inpA': None, '_': 4, 'inpB': [2, None], 'inpC': 5}) with pytest.raises(ValueError, match="count is 4, maximum is 3"): blk2.check_signature({'inpA': None, '_': (2, 3), 'inpB': [0, 1], 'inpC': 5}) with pytest.raises(ValueError, match="invalid"): blk2.check_signature({'inpA': None, '_': 4.5, 'inpB': 1, 'inpC': 5}) with pytest.raises(ValueError, match="invalid"): blk2.check_signature({'inpA': None, '_': [4], 'inpB': 1, 'inpC': 5}) with pytest.raises(ValueError, match="invalid"): blk2.check_signature({'inpA': None, '_': [0, 1, 2, 3], 'inpB': 1, 'inpC': 5})
def test_const(): """Const objects are reused.""" CONST = 'some value' ch1 = edzed.Const(CONST) assert ch1.output == CONST ch2 = edzed.Const(CONST) assert ch1 is ch2 # ch1 was reused # make sure our implementation with a dict of all instances # can handle also unhashable values UNHASHABLE = [0] cu1 = edzed.Const(UNHASHABLE) assert cu1.output == UNHASHABLE
def test_named_inputs(circuit): """ Named inputs are ordered by name. This test also covers that no unnamed inputs is a special case in FuncBlock. """ d4 = edzed.FuncBlock( '4 digits', func=lambda a, b, c, d: 1000*a + 100*b + 10*c + d ).connect(d=9, c=edzed.Const(1), a=2, b=edzed.Const(0)) init(circuit) d4.eval_block() assert d4.output == 2019
def test_no_invert_invert(circuit): """_not__not_name has no special meaning.""" edzed.Not('src').connect(edzed.Const(True)) edzed.FuncBlock('F', func=lambda x: x).connect('_not_src') edzed.FuncBlock('T', func=lambda x: x).connect('_not__not_src') with pytest.raises(Exception, match="not found"): init(circuit)
def test_is_multiple_and_to_tuple(): """Check _is_multiple() and _to_tuple() helpers.""" is_multiple = edzed.block._is_multiple to_tuple = edzed.block._to_tuple # None is special case assert not is_multiple(None) assert to_tuple(None, lambda x: None) == () SINGLE_VALUES = ('name', 10, {'a', 'b', 'c'}, set(), True, edzed.Const(-1)) for arg in SINGLE_VALUES: assert not is_multiple(arg) assert to_tuple(arg, lambda x: None) == (arg, ) MULTI_VALUES = ((1, 2, 3), [0], (), []) for arg in MULTI_VALUES: assert is_multiple(arg) assert to_tuple(arg, lambda x: None) == tuple(arg) # iterators are multiple values and are not consumed by is_multiple iterator = (x for x in range(4)) # a generator is always an iterator assert is_multiple(iterator) # test that the iterator is not exausted yet assert to_tuple(iterator, lambda x: None) == (0, 1, 2, 3) # check that validators are being called to_tuple(1, lambda x: 1 / x) with pytest.raises(ZeroDivisionError): to_tuple(0, lambda x: 1 / x)
def test_input_by_name(circuit): """ Input block may be specified by name. This implies that string constants require explicit Const('...') """ Noop('blk_X').connect('blk_Y', edzed.Const('blk_Z')) # blk_Z is just a string Noop('blk_Y').connect(a=('blk_X', None)) init(circuit)
def test_input_groups(circuit): """Input group is a sequence of input values.""" d4 = edzed.FuncBlock( '4 digits', func=lambda f4: 1000*f4[0] + 100*f4[1] + 10*f4[2] + f4[3] ).connect(f4=[edzed.Const(3), 5, 7, 9]) init(circuit) d4.eval_block() assert d4.output == 3579
def test_unnamed_inputs(circuit): """ Unnamed inputs are processed in order as they are connected. Const blocks are created on the fly. """ d4 = edzed.FuncBlock( '4 digits', func=lambda a, b, c, d: 1000*a + 100*b + 10*c + d ).connect(1, 9, 8, edzed.Const(4)) init(circuit) d4.eval_block() assert d4.output == 1984
def test_getblocks(circuit): """Test getblocks() and findblock().""" # 1 Const, 2 CBlocks and 2 SBlocks edzed.Const(0) f1 = edzed.FuncBlock('F1', func=lambda: None) # CBlock f2 = edzed.FuncBlock('F2', func=lambda: None) # CBlock t1 = edzed.Timer('T1') # SBlock t2 = edzed.Timer('T2') # SBlock assert circuit.findblock('F1') is f1 assert circuit.findblock('T2') is t2 assert set(circuit.getblocks()) == {f1, f2, t1, t2} assert not set(circuit.getblocks( btype=edzed.Const)) # Const blocks are not registered assert set(circuit.getblocks(btype=edzed.CBlock)) == {f1, f2} assert set(circuit.getblocks(btype=edzed.SBlock)) == {t1, t2}
def test_no_cross_circuit_inputs(circuit): """No cross circuit connections. Constants are an exception.""" noop1 = Noop('noop') msg1 = edzed.Const("hello!") init(circuit) edzed.reset_circuit() noop2 = Noop('noop').connect(noop1) circuit2 = edzed.get_circuit() with pytest.raises(ValueError, match="not in the current circuit"): init(circuit2) # Const objects do not belong to specific circuit edzed.reset_circuit() noop3 = Noop('noop').connect(msg1) circuit3 = edzed.get_circuit() init(circuit3)
def test_input_access(circuit): """Test various methods to access input values.""" class MyBlock(edzed.CBlock): def calc_output(self): return [ self._in['_'], # tuple of all unnanmed inputs self._in['ctrl'], # input name as a key self._in.ctrl, # input name as an attr ] blk = MyBlock('test').connect(50, 51, 52, 53, 54, ctrl=edzed.Const('CTRL')) init(circuit) blk.eval_block() assert blk.output == [ (50, 51, 52, 53, 54), 'CTRL', 'CTRL', ]
def test_no_unknown_inputs(circuit): """Test unknown block error. """ Noop('blk_X').connect('blk_Y', edzed.Const('blk_Z')) Noop('blk_Y').connect(a=('blk_X', 'blk_Z')) # there is no blk_Z with pytest.raises(Exception, match="not found"): init(circuit)