Skip to content

Commit 95bd0fe

Browse files
authored
Merge pull request labgrid-project#882 from Bastian-Krause/bst/doctest
doc: test code/config snippets in docs
2 parents b599dbe + 3449a15 commit 95bd0fe

7 files changed

Lines changed: 425 additions & 86 deletions

File tree

.github/workflows/unit-tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
${{ runner.os }}-pip-
2626
- name: Install system dependencies
2727
run: |
28-
sudo apt-get install -yq libow-dev openssh-server openssh-client libsnappy-dev ncurses-term
28+
sudo apt-get install -yq libow-dev openssh-server openssh-client libsnappy-dev ncurses-term graphviz
2929
sudo mkdir -p /var/cache/labgrid/runner && sudo chown runner /var/cache/labgrid/runner
3030
- name: Prepare local SSH
3131
run: |
@@ -58,6 +58,7 @@ jobs:
5858
rm man/*.[1-8]
5959
make -C man all
6060
git --no-pager diff --exit-code
61+
make -C doc doctest
6162
- uses: codecov/codecov-action@v1
6263
docker:
6364
runs-on: ubuntu-latest

doc/conf.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3737
# ones.
3838
extensions = ['sphinx.ext.autodoc',
39+
'sphinx.ext.doctest',
3940
'sphinx.ext.napoleon',
4041
'sphinx.ext.coverage',
4142
'sphinx.ext.viewcode',
@@ -202,3 +203,45 @@ def run_apidoc(app):
202203

203204
def setup(app):
204205
app.connect('builder-inited', run_apidoc)
206+
app.connect('doctree-read', write_literal_blocks)
207+
208+
# -- Options for doctest --------------------------------------------------
209+
210+
doctest_global_setup = '''
211+
import os
212+
import shutil
213+
from unittest.mock import Mock, patch
214+
215+
doctest_dir = '.build/doctest'
216+
217+
if not os.getcwd().endswith(doctest_dir):
218+
os.chdir(doctest_dir)
219+
'''
220+
221+
doctest_global_cleanup = '''
222+
if os.getcwd().endswith(doctest_dir):
223+
os.chdir('../..')
224+
'''
225+
226+
def write_literal_blocks(app, doctree):
227+
"""
228+
Writes named literal blocks to a file with that respective name in the temporary doctest build
229+
directory. This allows doctest to test code snippets referring to these files as-is.
230+
"""
231+
blocks = doctree.traverse(
232+
condition=lambda node: node.tagname == 'literal_block'
233+
)
234+
235+
for block in blocks:
236+
name = '_'.join(block['names'])
237+
238+
if not name:
239+
continue
240+
241+
out_path = os.path.join(app.outdir, name)
242+
243+
if os.path.exists(out_path):
244+
raise Exception(f'literal block name "{name}" used multiple times')
245+
246+
with open(out_path, 'w') as f:
247+
f.write(block.astext())

doc/configuration.rst

Lines changed: 129 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,8 @@ Implements:
26082608
Arguments:
26092609
- None
26102610

2611+
.. _conf-strategies:
2612+
26112613
Strategies
26122614
----------
26132615

@@ -2623,19 +2625,44 @@ A BareboxStrategy has four states:
26232625
- barebox
26242626
- shell
26252627

2628+
Here is an example environment config:
2629+
2630+
.. code-block:: yaml
2631+
:name: barebox-env.yaml
26262632
2627-
to transition to the shell state:
2633+
targets:
2634+
main:
2635+
resources:
2636+
RawSerialPort:
2637+
port: '/dev/ttyUSB0'
2638+
drivers:
2639+
ManualPowerDriver: {}
2640+
SerialDriver: {}
2641+
BareboxDriver: {}
2642+
ShellDriver:
2643+
prompt: 'root@\w+:[^ ]+ '
2644+
login_prompt: ' login: '
2645+
username: root
2646+
BareboxStrategy: {}
26282647
2629-
::
2648+
In order to use the BareboxStrategy via labgrid as a library and transition to
2649+
the ``shell`` state:
2650+
2651+
.. testsetup:: barebox-strategy
2652+
2653+
from labgrid.strategy import BareboxStrategy
2654+
2655+
BareboxStrategy.transition = Mock(return_value=None)
2656+
2657+
.. doctest:: barebox-strategy
26302658

26312659
>>> from labgrid import Environment
2632-
>>> e = Environment("local.yaml")
2660+
>>> e = Environment("barebox-env.yaml")
26332661
>>> t = e.get_target("main")
26342662
>>> s = t.get_driver("BareboxStrategy")
26352663
>>> s.transition("shell")
26362664

2637-
2638-
this command would transition from the bootloader into a Linux shell and
2665+
This command would transition from the bootloader into a Linux shell and
26392666
activate the ShellDriver.
26402667

26412668
ShellStrategy
@@ -2646,19 +2673,42 @@ A ShellStrategy has three states:
26462673
- off
26472674
- shell
26482675

2676+
Here is an example environment config:
2677+
2678+
.. code-block:: yaml
2679+
:name: shell-env.yaml
2680+
2681+
targets:
2682+
main:
2683+
resources:
2684+
RawSerialPort:
2685+
port: '/dev/ttyUSB0'
2686+
drivers:
2687+
ManualPowerDriver: {}
2688+
SerialDriver: {}
2689+
ShellDriver:
2690+
prompt: 'root@\w+:[^ ]+ '
2691+
login_prompt: ' login: '
2692+
username: root
2693+
ShellStrategy: {}
2694+
2695+
In order to use the ShellStrategy via labgrid as a library and transition to
2696+
the ``shell`` state:
26492697

2650-
to transition to the shell state:
2698+
.. testsetup:: shell-strategy
26512699

2652-
::
2700+
from labgrid.strategy import ShellStrategy
2701+
2702+
ShellStrategy.transition = Mock(return_value=None)
2703+
2704+
.. doctest:: shell-strategy
26532705

26542706
>>> from labgrid import Environment
2655-
>>> e = Environment("local.yaml")
2707+
>>> e = Environment("shell-env.yaml")
26562708
>>> t = e.get_target("main")
26572709
>>> s = t.get_driver("ShellStrategy")
2658-
>>> s.transition("shell")
26592710

2660-
2661-
this command would transition directly into a Linux shell and
2711+
This command would transition directly into a Linux shell and
26622712
activate the ShellDriver.
26632713

26642714
UBootStrategy
@@ -2670,65 +2720,108 @@ A UBootStrategy has four states:
26702720
- uboot
26712721
- shell
26722722

2723+
Here is an example environment config:
2724+
2725+
.. code-block:: yaml
2726+
:name: uboot-env.yaml
2727+
2728+
targets:
2729+
main:
2730+
resources:
2731+
RawSerialPort:
2732+
port: '/dev/ttyUSB0'
2733+
drivers:
2734+
ManualPowerDriver: {}
2735+
SerialDriver: {}
2736+
UBootDriver: {}
2737+
ShellDriver:
2738+
prompt: 'root@\w+:[^ ]+ '
2739+
login_prompt: ' login: '
2740+
username: root
2741+
UBootStrategy: {}
2742+
2743+
In order to use the UBootStrategy via labgrid as a library and transition to
2744+
the ``shell`` state:
2745+
2746+
.. testsetup:: uboot-strategy
26732747

2674-
to transition to the shell state:
2748+
from labgrid.strategy import UBootStrategy
26752749

2676-
::
2750+
UBootStrategy.transition = Mock(return_value=None)
2751+
2752+
.. doctest:: uboot-strategy
26772753

26782754
>>> from labgrid import Environment
2679-
>>> e = Environment("local.yaml")
2755+
>>> e = Environment("uboot-env.yaml")
26802756
>>> t = e.get_target("main")
26812757
>>> s = t.get_driver("UBootStrategy")
26822758
>>> s.transition("shell")
26832759

2684-
2685-
this command would transition from the bootloader into a Linux shell and
2760+
This command would transition from the bootloader into a Linux shell and
26862761
activate the ShellDriver.
26872762

2688-
DockerShellStrategy
2689-
~~~~~~~~~~~~~~~~~~~
2690-
A DockerShellStrategy has three states:
2763+
DockerStrategy
2764+
~~~~~~~~~~~~~~
2765+
A DockerStrategy has three states:
26912766

26922767
- unknown
2693-
- off
2694-
- shell
2768+
- gone
2769+
- accessible
26952770

2771+
Here is an example environment config:
26962772

2697-
To transition to the shell state:
2773+
.. code-block:: yaml
2774+
:name: docker-env.yaml
2775+
2776+
targets:
2777+
main:
2778+
resources:
2779+
DockerDaemon:
2780+
docker_daemon_url: unix://var/run/docker.sock
2781+
drivers:
2782+
DockerDriver:
2783+
image_uri: "rastasheep/ubuntu-sshd:16.04"
2784+
container_name: "ubuntu-lg-example"
2785+
host_config: {"network_mode":"bridge"}
2786+
network_services: [{"port":22,"username":"root","password":"root"}]
2787+
DockerStrategy: {}
2788+
2789+
In order to use the DockerStrategy via labgrid as a library and transition to
2790+
the ``accessible`` state:
26982791

2699-
::
2792+
.. testsetup:: docker-strategy
2793+
2794+
from labgrid.strategy import DockerStrategy
2795+
2796+
DockerStrategy.transition = Mock(return_value=None)
2797+
2798+
.. doctest:: docker-strategy
27002799

27012800
>>> from labgrid import Environment
2702-
>>> e = Environment("local.yaml")
2801+
>>> e = Environment("docker-env.yaml")
27032802
>>> t = e.get_target("main")
2704-
>>> s = t.get_driver("DockerShellStrategy")
2705-
>>> s.transition("shell")
2706-
2803+
>>> s = t.get_driver("DockerStrategy")
2804+
>>> s.transition("accessible")
27072805

27082806
These commands would activate the docker driver which creates and starts
27092807
a docker container. This will subsequently make `NetworkService`_ instance(s)
27102808
available which can be used for e.g. SSH access.
27112809

2712-
Note: Transitioning to the "off" state will make any `NetworkService`_
2713-
instance(s) unresponsive - which may in turn invalidate SSH connection
2714-
sharing. Therefore, during automated test suites, refrain from transitioning
2715-
to the "off" state.
2716-
27172810
Reporters
27182811
---------
27192812

27202813
StepReporter
27212814
~~~~~~~~~~~~
27222815
The StepReporter outputs individual labgrid steps to `STDOUT`.
27232816

2724-
::
2817+
.. doctest::
27252818

27262819
>>> from labgrid import StepReporter
27272820
>>> StepReporter.start()
27282821

27292822
The Reporter can be stopped with a call to the stop function:
27302823

2731-
::
2824+
.. doctest::
27322825

27332826
>>> from labgrid import StepReporter
27342827
>>> StepReporter.stop()
@@ -2746,14 +2839,14 @@ ConsoleLoggingReporter
27462839
The ConsoleLoggingReporter outputs read calls from the console transports into
27472840
files. It takes the path as a parameter.
27482841

2749-
::
2842+
.. doctest::
27502843

27512844
>>> from labgrid import ConsoleLoggingReporter
27522845
>>> ConsoleLoggingReporter.start(".")
27532846

27542847
The Reporter can be stopped with a call to the stop function:
27552848

2756-
::
2849+
.. doctest::
27572850

27582851
>>> from labgrid import ConsoleLoggingReporter
27592852
>>> ConsoleLoggingReporter.stop()

0 commit comments

Comments
 (0)