Exemple #1
0
def test_average_stream(RE, hw):
    # Create callback chain
    avg = AverageStream(10)
    c = CallbackCounter()
    d = DocCollector()
    avg.subscribe(c)
    avg.subscribe(d.insert)
    # Run a basic plan
    RE(stepscan(hw.det, hw.motor), {'all': avg})
    assert c.value == 1 + 1 + 2  # events, descriptor, start and stop
    # See that we made sensible descriptor
    start_uid = d.start[0]['uid']
    assert start_uid in d.descriptor
    desc_uid = d.descriptor[start_uid][0]['uid']
    assert desc_uid in d.event
    evt = d.event[desc_uid][0]
    assert evt['seq_num'] == 1
    assert all([
        key in d.descriptor[start_uid][0]['data_keys']
        for key in evt['data'].keys()
    ])
    # See that we returned the correct average
    assert evt['data']['motor'] == -0.5  # mean of range(-5, 5)
    assert evt['data']['motor_setpoint'] == -0.5  # mean of range(-5, 5)
    assert start_uid in d.stop
    assert d.stop[start_uid]['num_events'] == {'primary': 1}
Exemple #2
0
def test_multirun_smoke(RE, hw):
    dc = DocCollector()

    def interlaced_plan(dets, motor):
        to_read = (motor, *dets)
        run_ids = list("abc")
        for rid in run_ids:
            yield from drw(bps.open_run(md={rid: rid}), run=rid)

        for j in range(5):
            for i, rid in enumerate(run_ids):
                yield from bps.mov(motor, j + 0.1 * i)
                yield from drw(bps.trigger_and_read(to_read), run=rid)

        for rid in run_ids:
            yield from drw(bps.close_run(), run=rid)

    RE(interlaced_plan([hw.det], hw.motor), dc.insert)

    assert len(dc.start) == 3
    for start in dc.start:
        desc, = dc.descriptor[start["uid"]]
        assert len(dc.event[desc["uid"]]) == 5

    for stop in dc.stop.values():
        for start in dc.start:
            assert start["time"] < stop["time"]
def test_seq_recommender(RE, hw):

    recommender = SequenceRecommender([[
        1,
    ], [
        2,
    ], [
        3,
    ]])  # noqa

    cb, queue = recommender_factory(recommender, ["motor"], ["det"])
    dc = DocCollector()

    # pre-poison the queue to simulate a messy reccomender
    queue.put(None)
    queue.put({})

    RE(
        adaptive_plan([hw.det], {hw.motor: 0},
                      to_recommender=cb,
                      from_recommender=queue),
        dc.insert,
    )

    assert len(dc.start) == 4
    assert len(dc.event) == 4
    for ev in dc.event.values():
        assert len(ev) == 1

    # check that our reccomender does not leave anything behind
    with pytest.raises(Empty):
        queue.get(block=False)
Exemple #4
0
def test_mesh_pseudo(hw, RE):

    p3x3 = hw.pseudo3x3
    sig = hw.sig
    d = DocCollector()

    RE.subscribe(d.insert)
    rs = RE(bp.grid_scan([sig], p3x3.pseudo1, 0, 3, 5, p3x3.pseudo2, 7, 10, 7))

    if RE.call_returns_result:
        uid = rs.run_start_uids[0]
    else:
        uid = rs[0]

    df = pd.DataFrame(
        [_['data'] for _ in d.event[d.descriptor[uid][0]['uid']]])

    for k in p3x3.describe():
        assert k in df

    for k in sig.describe():
        assert k in df

    assert all(df[sig.name] == 0)
    assert all(df[p3x3.pseudo3.name] == 0)
Exemple #5
0
def test_plan_header(RE, hw):
    args = []

    ##
    args.append((bp.grid_scan([hw.det],
                              hw.motor, 1, 2, 3,
                              hw.motor1, 4, 5, 6,
                              hw.motor2, 7, 8, 9,
                              snake_axes=True),
                 {'motors': ('motor', 'motor1', 'motor2'),
                  'extents': ([1, 2], [4, 5], [7, 8]),
                  'shape': (3, 6, 9),
                  'snaking': (False, True, True),
                  'plan_pattern_module': 'bluesky.plan_patterns',
                  'plan_pattern': 'outer_product',
                  'plan_name': 'grid_scan'}))

    ##
    args.append((bp.inner_product_scan([hw.det], 9,
                                       hw.motor, 1, 2,
                                       hw.motor1, 4, 5,
                                       hw.motor2, 7, 8),
                {'motors': ('motor', 'motor1', 'motor2')}))

    for plan, target in args:
        c = DocCollector()
        RE(plan, c.insert)
        for s in c.start:
            _validate_start(s, target)
def test_multirun_smoke(RE, hw):
    """Test on interlaced runs (using wrapper on each command)"""
    dc = DocCollector()

    def interlaced_plan(dets, motor):
        to_read = (motor, *dets)
        run_names = ["run_one", "run_two", "run_three"]
        for rid in run_names:
            yield from srkw(bps.open_run(md={rid: rid}), run=rid)

        for j in range(5):
            for i, rid in enumerate(run_names):
                yield from bps.mov(motor, j + 0.1 * i)
                yield from srkw(bps.trigger_and_read(to_read), run=rid)

        for rid in run_names:
            yield from srkw(bps.close_run(), run=rid)

    RE(interlaced_plan([hw.det], hw.motor), dc.insert)

    assert len(dc.start) == 3
    for start in dc.start:
        desc, = dc.descriptor[start["uid"]]
        assert len(dc.event[desc["uid"]]) == 5

    for stop in dc.stop.values():
        for start in dc.start:
            assert start["time"] < stop["time"]
def test_multirun_run_key_type(RE, hw):
    """Test calls to wrapper with run key set to different types"""

    dc = DocCollector()

    @bsp.run_decorator(md={})
    def empty_plan():
        yield from bps.mov(hw.motor, 5)

    # The wrapper is expected to raise an exception if called with run ID = None
    with pytest.raises(ValueError, match="run ID can not be None"):
        def plan1():
            yield from srkw(empty_plan(), None)
        RE(plan1(), dc.insert)

    # Check with run ID of type reference
    def plan2():
        yield from srkw(empty_plan(), object())
    RE(plan2(), dc.insert)

    # Check with run ID of type 'int'
    def plan3():
        yield from srkw(empty_plan(), 10)
    RE(plan3(), dc.insert)

    # Check if call with correct parameter type are successful
    def plan4():
        yield from srkw(empty_plan(), "run_name")
    RE(plan4(), dc.insert)

    def plan5():
        yield from srkw(empty_plan(), run="run_name")
    RE(plan5(), dc.insert)
Exemple #8
0
def test_force_stop_exit_status(bail_func, status, RE):
    d = DocCollector()
    RE.subscribe(d.insert)

    @run_decorator()
    def bad_plan():
        print('going in')
        yield Msg('pause')

    with pytest.raises(RunEngineInterrupted):
        RE(bad_plan())

    rs = getattr(RE, bail_func)()

    if RE.call_returns_result:
        if bail_func == "resume":
            assert rs.plan_result is not RE.NO_PLAN_RETURN
        else:
            assert rs.plan_result is RE.NO_PLAN_RETURN
        uid = rs.run_start_uids[0]
        assert rs.exit_status == status
    else:
        uid = rs[0]
    assert len(d.start) == 1
    assert d.start[0]['uid'] == uid
    assert len(d.stop) == 1
    assert d.stop[uid]['exit_status'] == status
Exemple #9
0
def test_rmesh_pseudo(hw, RE):
    p3x3 = hw.pseudo3x3
    p3x3.set(1, -2, 100)
    init_pos = p3x3.position
    sig = hw.sig
    d = DocCollector()

    RE.subscribe(d.insert)
    rs = RE(
        bp.rel_grid_scan([sig], p3x3.pseudo1, 0, 3, 5, p3x3.pseudo2, 7, 10, 7))

    if RE.call_returns_result:
        uid = rs.run_start_uids[0]
    else:
        uid = rs[0]

    df = pd.DataFrame(
        [_['data'] for _ in d.event[d.descriptor[uid][0]['uid']]])

    for k in p3x3.describe():
        assert k in df

    for k in sig.describe():
        assert k in df

    assert all(df[sig.name] == 0)
    assert all(df[p3x3.pseudo3.name] == 100)
    assert len(df) == 35
    assert min(df[p3x3.pseudo1.name]) == 1
    assert init_pos == p3x3.position
Exemple #10
0
def test_grid_scans(RE, hw, args, snake_axes, plan, is_relative):
    """
    Basic test of functionality of `grid_scan` and `rel_grid_scan`:
    Tested:
    - positions of the simulated motors at each step of the scan
    - contents of the 'snaking' field of the start document
    """

    # Convert motor names to actual motors in the argument list using fixture 'hw'
    args = [getattr(hw, _) if isinstance(_, str) else _ for _ in args]
    # Do the same in `snake_axes` if it contains the list of motors
    if isinstance(snake_axes, collections.abc.Iterable):
        snake_axes = [getattr(hw, _) for _ in snake_axes]

    # Place motors at random initial positions. Do it both for relative and
    #   absolute scans. The absolute scans will ignore the inital positions
    #   automatically.
    motors = [_[0] for _ in chunk_outer_product_args(args)]
    motors_pos = [2 * random.random() - 1 for _ in range(len(motors))]
    for _motor, _pos in zip(motors, motors_pos):
        RE(bps.mv(_motor, _pos))

    c = DocCollector()
    RE(plan([hw.det], *args, snake_axes=snake_axes), c.insert)
    positions = _retrieve_motor_positions(c, [hw.motor, hw.motor1, hw.motor2])
    # Retrieve snaking data from the start document
    snaking = c.start[0]["snaking"]

    # Generate the list of positions based on
    positions_expected, snaking_expected = \
        _grid_scan_position_list(args=args, snake_axes=snake_axes)

    assert snaking == snaking_expected, \
        "The contents of the 'snaking' field in the start document "\
        "does not match the expected values"

    assert set(positions.keys()) == set(positions_expected.keys()), \
        "Different set of motors in dictionaries of actual and expected positions"

    # The dictionary of the initial postiions
    motor_pos_shift = {
        _motor.name: _pos
        for (_motor, _pos) in zip(motors, motors_pos)
    }

    for name in positions_expected.keys():
        # The positions should be shifted only if the plan is relative.
        #   Absolute plans will ignore the initial motor positions
        shift = motor_pos_shift[name] if is_relative else 0
        npt.assert_array_almost_equal(
            positions[name],
            np.array(positions_expected[name]) + shift,
            err_msg=
            f"Expected and actual positions for the motor '{name}' don't match"
        )
Exemple #11
0
def test_bec_peak_stats_derivative_and_stats(RE, hw):
    bec = BestEffortCallback(calc_derivative_and_stats=True)
    RE.subscribe(bec)

    c = DocCollector()
    RE.subscribe(c.insert)

    res = RE(scan([hw.ab_det], hw.motor, 1, 5, 5))

    if RE.call_returns_result:
        uid = res.run_start_uids[0]
    else:
        uid = res[0]

    desc_uid = c.descriptor[uid][0]["uid"]
    ps = bec._peak_stats[desc_uid]["det_a"]

    assert hasattr(ps, "derivative_stats")

    fields = ["min", "max", "com", "cen", "fwhm", "crossings"]
    der_fields = ["x", "y"] + fields
    for field in der_fields:
        assert hasattr(ps.derivative_stats,
                       field), f"{field} is not an attribute of ps.der"

    assert isinstance(ps.__repr__(), str)

    # These imports are needed by the `eval` below:
    from numpy import array  # noqa F401
    from collections import OrderedDict  # noqa F401

    out = eval(str(ps))
    assert isinstance(out, dict)
    for key in ("stats", "derivative_stats"):
        assert key in out

    for field in fields:
        stats_value = getattr(ps.stats, field)
        out_value = out["stats"][field]
        if stats_value is not None:
            assert np.allclose(stats_value, out_value)
        else:
            stats_value == out_value

    for field in der_fields:
        stats_value = getattr(ps.derivative_stats, field)
        out_value = out["derivative_stats"][field]
        if stats_value is not None:
            assert np.allclose(stats_value, out_value)
        else:
            stats_value == out_value
Exemple #12
0
def test_ops_dimension_hints(RE, hw):
    det = hw.det
    motor = hw.motor
    motor1 = hw.motor1
    c = DocCollector()
    RE.subscribe(c.insert)
    RE(bp.grid_scan([det], motor, -1, 1, 7, motor1, 0, 2, 3))

    st = c.start[0]

    assert 'dimensions' in st['hints']

    assert st['hints']['dimensions'] == [(m.hints['fields'], 'primary')
                                         for m in (motor, motor1)]
Exemple #13
0
def test_force_stop_exit_status(bail_func, status, RE):
    d = DocCollector()
    RE.subscribe(d.insert)

    @run_decorator()
    def bad_plan():
        yield Msg('pause')
    try:
        RE(bad_plan())
    except:
        ...
    rs, = getattr(RE, bail_func)()

    assert len(d.start) == 1
    assert d.start[0]['uid'] == rs
    assert len(d.stop) == 1
    assert d.stop[rs]['exit_status'] == status
Exemple #14
0
def test_plotting_hints(RE, hw, db):
    ''' This tests the run and checks that the correct hints are created.
        Hints are mainly created to help the BestEffortCallback in plotting the
        data.
        Use a callback to do the checking.
    '''
    dc = DocCollector()
    RE.subscribe(dc.insert)

    # check that the inner product hints are passed correctly
    hint = {
        'dimensions': [([hw.motor1.name, hw.motor2.name,
                         hw.motor3.name], 'primary')]
    }
    RE(
        inner_product_scan([hw.det], 20, hw.motor1, -1, 1, hw.motor2, -1, 1,
                           hw.motor3, -2, 0))
    assert dc.start[-1]['hints'] == hint

    # check that the outer product (grid_scan) hints are passed correctly
    hint = {
        'dimensions': [(['motor1'], 'primary'), (['motor2'], 'primary'),
                       (['motor3'], 'primary')]
    }
    # grid_scan passes "rectilinear" gridding as well
    # make sure this is also passed
    output_hint = hint.copy()
    output_hint['gridding'] = 'rectilinear'
    RE(
        grid_scan([hw.det], hw.motor1, -1, 1, 2, hw.motor2, -1, 1, 2, True,
                  hw.motor3, -2, 0, 2, True))

    assert dc.start[-1]['hints'] == output_hint

    # check that if gridding is supplied, it's not overwritten by grid_scan
    # check that the outer product (grid_scan) hints are passed correctly
    hint = {
        'dimensions': [(['motor1'], 'primary'), (['motor2'], 'primary'),
                       (['motor3'], 'primary')],
        'gridding':
        'rectilinear'
    }
    RE(
        grid_scan([hw.det], hw.motor1, -1, 1, 2, hw.motor2, -1, 1, 2, True,
                  hw.motor3, -2, 0, 2, True))
    assert dc.start[-1]['hints'] == hint
Exemple #15
0
def test_describe_config_optional(RE):
    class Simple:
        """A trivial flyer that omits the configuration methods"""

        name = "simple_flyer"
        parent = None

        def kickoff(self):
            return NullStatus()

        def describe_collect(self):
            return {"stream_name": {}}

        def complete(self):
            return NullStatus()

        def collect(self):
            for i in range(100):
                yield {"data": {}, "timestamps": {}, "time": i, "seq_num": i}

        def stop(self, *, success=False):
            pass

    d = DocCollector()

    flyer = Simple()

    RE(
        [
            Msg("open_run"),
            Msg("kickoff", flyer, group="foo"),
            Msg("wait", group="foo"),
            Msg("complete", flyer, group="bar"),
            Msg("wait", group="bar"),
            Msg("collect", flyer),
            Msg("close_run"),
        ],
        d.insert,
    )

    ((desc, ), ) = d.descriptor.values()
    assert desc["name"] == "stream_name"

    assert "simple_flyer" in desc["object_keys"]
    assert "simple_flyer" in desc["configuration"]
Exemple #16
0
def test_mesh_pseudo(hw, RE):
    p3x3 = hw.pseudo3x3
    sig = hw.sig
    d = DocCollector()

    RE.subscribe(d.insert)
    rs, = RE(bp.grid_scan([sig], p3x3.pseudo1, 0, 3, 5, p3x3.pseudo2, 7, 10,
                          7))
    df = pd.DataFrame([_['data'] for _ in d.event[d.descriptor[rs][0]['uid']]])

    for k in p3x3.describe():
        assert k in df

    for k in sig.describe():
        assert k in df

    assert all(df[sig.name] == 0)
    assert all(df[p3x3.pseudo3.name] == 0)
Exemple #17
0
def test_force_stop_exit_status(bail_func, status, RE):
    d = DocCollector()
    RE.subscribe(d.insert)

    @run_decorator()
    def bad_plan():
        print('going in')
        yield Msg('pause')

    with pytest.raises(RunEngineInterrupted):
        RE(bad_plan())

    rs, = getattr(RE, bail_func)()

    assert len(d.start) == 1
    assert d.start[0]['uid'] == rs
    assert len(d.stop) == 1
    assert d.stop[rs]['exit_status'] == status
def test_multirun_smoke_fail(RE, hw):
    dc = DocCollector()

    def interlaced_plan(dets, motor):
        run_names = ["run_one", "run_two", "run_three"]
        for rid in run_names:
            yield from srkw(bps.open_run(md={rid: rid}), run=rid)
        raise Exception("womp womp")

    with pytest.raises(Exception):
        RE(interlaced_plan([hw.det], hw.motor), dc.insert)

    assert len(dc.start) == len(dc.stop)
    assert len(dc.start) == 3

    for v in dc.stop.values():
        assert v["exit_status"] == "fail"
        assert v["reason"] == "womp womp"
def test_exceptions_exit_status(RE):
    d = DocCollector()
    RE.subscribe(d.insert)

    class Snowflake(Exception):
        ...

    @run_decorator()
    def bad_plan():
        yield Msg('null')
        raise Snowflake('boo')

    with pytest.raises(Snowflake) as sf:
        RE(bad_plan())

    assert len(d.start) == 1
    rs = d.start[0]['uid']
    assert len(d.stop) == 1
    assert d.stop[rs]['exit_status'] == 'fail'
    assert d.stop[rs]['reason'] == str(sf.value)
def test_ramp(RE):
    from ophyd.positioner import SoftPositioner
    from ophyd import StatusBase
    from ophyd.sim import SynGauss

    tt = SoftPositioner(name='mot')
    tt.set(0)
    dd = SynGauss('det', tt, 'mot', 0, 3)

    st = StatusBase()

    def kickoff():
        yield Msg('null')
        for j, v in enumerate(np.linspace(-5, 5, 10)):
            RE.loop.call_later(.1 * j, lambda v=v: tt.set(v))
        RE.loop.call_later(1.2, st._finished)
        return st

    def inner_plan():
        yield from trigger_and_read([dd])

    g = ramp_plan(kickoff(), tt, inner_plan, period=0.08)
    db = DocCollector()
    RE.subscribe(db.insert)
    rs = RE(g)
    if RE.call_returns_result:
        uid = rs.run_start_uids[0]
    else:
        uid = rs[0]
    assert db.start[0]['uid'] == uid
    assert len(db.descriptor[uid]) == 2
    descs = {d['name']: d for d in db.descriptor[uid]}

    assert set(descs) == set(['primary', 'mot_monitor'])

    primary_events = db.event[descs['primary']['uid']]
    assert len(primary_events) > 11

    monitor_events = db.event[descs['mot_monitor']['uid']]
    # the 10 from the updates, 1 from 'run at subscription time'
    assert len(monitor_events) == 11
def test_straight_through_stream(RE, hw):
    # Just a stream that sinks the events it receives
    ss = NegativeStream()
    # Create callback chain
    c = CallbackCounter()
    d = DocCollector()
    ss.subscribe(c)
    ss.subscribe(d.insert)
    # Run a basic plan
    RE(stepscan(hw.det, hw.motor), {'all': ss})
    # Check that our metadata is there
    assert c.value == 10 + 1 + 2  # events, descriptor, start and stop
    assert d.start[0]['stream_level'] == 'boring'
    desc = d.descriptor[d.start[0]['uid']][0]
    events = d.event[desc['uid']]
    print(desc)
    print([evt['data'] for evt in events])
    assert all([
        evt['data'][key] <= 0 for evt in events for key in evt['data'].keys()
    ])
    assert all([key in desc['data_keys'] for key in events[0]['data'].keys()])
def test_multirun_smoke_nested(RE, hw):
    """Test on nested runs (using decorator on each plan)"""
    dc = DocCollector()
    to_read = (hw.motor, hw.det)

    def some_plan():
        """This plan is called on each level of nesting"""
        for j in range(5):
            yield from bps.mov(hw.motor, j)
            yield from bps.trigger_and_read(to_read)

    @bsp.set_run_key_decorator("run_one")
    @bsp.run_decorator(md={})
    def plan_inner():
        yield from some_plan()

    @bsp.set_run_key_decorator("run_two")
    @bsp.run_decorator(md={})
    def plan_middle():
        yield from some_plan()
        yield from plan_inner()

    @bsp.set_run_key_decorator(run="run_three")  # Try kwarg
    @bsp.run_decorator(md={})
    def plan_outer():
        yield from some_plan()
        yield from plan_middle()

    RE(plan_outer(), dc.insert)

    assert len(dc.start) == 3
    for start in dc.start:
        desc, = dc.descriptor[start["uid"]]
        assert len(dc.event[desc["uid"]]) == 5

    for stop in dc.stop.values():
        for start in dc.start:
            assert start["time"] < stop["time"]
Exemple #23
0
def test_rmesh_pseudo(hw, RE):
    p3x3 = hw.pseudo3x3
    p3x3.set(1, -2, 100)
    init_pos = p3x3.position
    sig = hw.sig
    d = DocCollector()

    RE.subscribe(d.insert)
    rs, = RE(
        bp.rel_grid_scan([sig], p3x3.pseudo1, 0, 3, 5, p3x3.pseudo2, 7, 10, 7,
                         False))
    df = pd.DataFrame([_['data'] for _ in d.event[d.descriptor[rs][0]['uid']]])

    for k in p3x3.describe():
        assert k in df

    for k in sig.describe():
        assert k in df

    assert all(df[sig.name] == 0)
    assert all(df[p3x3.pseudo3.name] == 100)
    assert len(df) == 35
    assert min(df[p3x3.pseudo1.name]) == 1
    assert init_pos == p3x3.position
def test_seq_recommender(RE, hw):

    recommender = SequenceRecommender([[
        1,
    ], [
        2,
    ], [
        3,
    ]])  # noqa

    cb, queue = recommender_factory(recommender, ["motor"], ["det"])
    dc = DocCollector()

    RE(
        adaptive_plan([hw.det], {hw.motor: 0},
                      to_recommender=cb,
                      from_recommender=queue),
        dc.insert,
    )

    assert len(dc.start) == 1
    assert len(dc.event) == 1
    (events, ) = dc.event.values()
    assert len(events) == 4
Exemple #25
0
def test_plotting_hints(RE, hw, db):
    """ This tests the run and checks that the correct hints are created.
        Hints are mainly created to help the BestEffortCallback in plotting the
        data.
        Use a callback to do the checking.
    """
    dc = DocCollector()
    RE.subscribe(dc.insert)

    # check that the inner product hints are passed correctly
    hint = {
        "dimensions": [([hw.motor1.name, hw.motor2.name,
                         hw.motor3.name], "primary")]
    }
    RE(
        inner_product_scan([hw.det], 20, hw.motor1, -1, 1, hw.motor2, -1, 1,
                           hw.motor3, -2, 0))
    assert dc.start[-1]["hints"] == hint

    # check that the outer product (grid_scan) hints are passed correctly
    hint = {
        "dimensions": [
            (["motor1"], "primary"),
            (["motor2"], "primary"),
            (["motor3"], "primary"),
        ]
    }
    # grid_scan passes "rectilinear" gridding as well
    # make sure this is also passed
    output_hint = hint.copy()
    output_hint["gridding"] = "rectilinear"
    RE(
        grid_scan(
            [hw.det],
            hw.motor1,
            -1,
            1,
            2,
            hw.motor2,
            -1,
            1,
            2,
            True,
            hw.motor3,
            -2,
            0,
            2,
            True,
        ))

    assert dc.start[-1]["hints"] == output_hint

    # check that if gridding is supplied, it's not overwritten by grid_scan
    # check that the outer product (grid_scan) hints are passed correctly
    hint = {
        "dimensions": [
            (["motor1"], "primary"),
            (["motor2"], "primary"),
            (["motor3"], "primary"),
        ],
        "gridding":
        "rectilinear",
    }
    RE(
        grid_scan(
            [hw.det],
            hw.motor1,
            -1,
            1,
            2,
            hw.motor2,
            -1,
            1,
            2,
            True,
            hw.motor3,
            -2,
            0,
            2,
            True,
        ))
    assert dc.start[-1]["hints"] == hint
Exemple #26
0
def test_plan_header(fresh_RE, plan, target):
    RE = fresh_RE
    c = DocCollector()
    RE(plan, c.insert)
    for s in c.start:
        _validate_start(s, target)