Orcraft Configuration Fields
Config fields are how a component declares what can be configured about it. They are plain class attributes, and they are read by the controller to build its run configuration panel, to validate a run before it starts, and to render the config table in the log.
Declaration
Config fields come from the OrcraftConfigField namespace and are declared on a config
class:
class MyTestConfig(OrcraftTestConfig):
name = OrcraftConfigField.Text(default="My Test")
app_folder = OrcraftConfigField.PackageFolder(
default="",
label="App Folder",
required=True,
layout=OrcraftConfigField.Layout.NewRow,
)
attempts = OrcraftConfigField.Numeric(default=3, label="Attempts")
Inside the component the field reads as an ordinary attribute -
self.config.app_folder - holding either the configured value or the declared
default. There is no lookup by string key and no dictionary to keep in sync.
Output fields work the same way, from the OrcraftResultField namespace - see
Orcraft Result Fields.
Inherited fields
Every config class starts with fields it did not declare. They fall into three groups by who may change them. All the overridable ones are optional - each has a working default, and redeclaring one in your config replaces that default without changing what the field is for.
Overridable in code
Set in your config class, fixed for the run.
| Field | Declared on | Default | Purpose |
|---|---|---|---|
name |
Every component | per component | Display name in the GUI, CLI and logs. Defaults to "Test", "Step", "Flow", "Test Group" or "Test Suite". |
description |
Every component | "" |
Free text describing what the component does. |
log_orientation |
Every component | HORIZONTAL |
Orientation of the logged config table, or OFF to leave it out of the log. On a test it also sets the tables of its cycles and cases. The result table has its own log_orientation. |
version |
Suite, group, test | "" |
Version recorded with the results. |
palette |
Test | "gray" |
Log color palette for the test and everything it runs. |
endpoints_file_path |
Test | "" |
The saved endpoints file the test's endpoint pools pick from outside the controller. Left empty, a test with a pool is refused there; the controller picks from its own endpoints. |
groups / tests / items |
Suite / group / batch | [] |
The children the component runs. Optional to the framework, but an empty list runs nothing. |
Redeclare a field with the same OrcraftConfigField type and a new default -
name = OrcraftConfigField.Text(default="My Test"). A bare assignment such as
name = "My Test" is not an override: it shadows the field with a plain attribute, and the
GUI, the log table and required validation no longer see it.
Overridable in code or GUI
Set in your config class, and changeable again from the run configuration panel before a run.
| Field | Declared on | Default | Purpose |
|---|---|---|---|
test_endpoint |
Test | "localhost" |
The endpoint the test executes against. |
fanout_permutations |
Test | None |
The endpoint permutations a test with two or more fanned-out endpoint fields runs, as the run
dialog's ticks set them; None runs them all. The CI export writes the ticks into the
snapshot as an OrcraftConfigField.Raw field, the type of a value the framework sets
whole. Hidden from the panel unless redeclared. |
matrix |
Test | None |
The dimensions that expand into cases. |
number_of_cycles |
Test | 1 |
How many times the whole test repeats. |
case_number_of_cycles |
Test | 1 |
How many times each case repeats. |
upload_test_package |
Test | "if_missing" |
Whether the test package is uploaded to the endpoint before the run. |
update_endpoint_app |
Test | "on_mismatch" |
Whether the endpoint application is updated before the run. |
Same rule as above: redeclare with the same OrcraftConfigField type and a new default,
never a bare value. The default you set here is what the run configuration panel shows until someone
changes it there.
Reserved
Written by the framework while a run is in progress. A config that redeclares one of these, or code that assigns one, is refused with an error naming the field.
| Field | Declared on | Purpose |
|---|---|---|
target_endpoint |
Step | The endpoint the step is dispatched to. Set by the flow that runs the step. |
expect_endpoint_disconnect |
Step | Marks a step that is meant to take its endpoint offline. Set by the flow through run_step. |
start_time |
Every component | When the run started, stamped at run time. |
log_tab |
Every component | Log indentation. Assigned by the framework at every level. |
A class of your own that declares one of these names fails as soon as it is defined, with an error naming the field, and an assignment to one at run time raises the same error. The framework alone writes them.
Common config field options
Every field type is constructed with keyword arguments, and these are the ones every type accepts. They describe the field to the controller - its default, how it is labelled and laid out, whether a run may start without it - rather than what the value means to your test:
input_folder = OrcraftConfigField.Folder(
default="",
label="Input Folder",
required=True,
layout=OrcraftConfigField.Layout.NewRow,
input_placeholder="Folder containing the input files",
)
Type-specific arguments - options for a selection, port for an endpoint,
endpoints for a package field - are covered with their types below.
| Option | Meaning |
|---|---|
default |
The value used when nothing else is configured. |
default_factory |
A callable producing the default. Use this - never default - for lists and for anything that constructs objects, so instances are not shared. Only one of the two may be given. |
label |
The name shown in the GUI and in log tables. Without it the field is not user-facing. |
required |
Blocks a run that leaves the field empty, reporting it as a missing argument. Numeric fields are required by default; pass required=False for one that may legitimately be blank. |
layout |
Where the field is placed in the GUI panel - see Layout and presentation. |
width |
Width hint, in grid units, for the input in the GUI. |
formatter |
A callable that renders the value for display, leaving the stored value untouched. |
input_placeholder |
Placeholder text shown in an empty input. |
button_text |
Caption for the field's action button, where it has one. |
options |
The fixed set of values a selection field offers. |
options_lambda |
A callable evaluated when the panel opens, for a selection whose values are not known in advance. |
endpoints |
For package fields: which endpoint fields this file or folder should be uploaded to. |
Config field types
Every field type in the OrcraftConfigField namespace, grouped by what it selects.
Basic types
| Type | Value | Notes |
|---|---|---|
Text |
str |
Free text. Non-string values are coerced with str(); None becomes an empty string. |
Numeric |
int or float |
Required by default, because a cleared numeric input has nothing to fall back on. |
Boolean |
bool |
Rendered as a checkbox. |
name = OrcraftConfigField.Text(default="Boolean Parameter")
verbose = OrcraftConfigField.Boolean(default=False, label="Verbose")
attempts = OrcraftConfigField.Numeric(default=3, label="Attempts")
timeout = OrcraftConfigField.Numeric(default=None, label="Timeout", required=False)
Selection types
| Type | Value | Notes |
|---|---|---|
Select |
str |
One value from a fixed options tuple. |
MultiSelect |
tuple[str, ...] |
Any number of values from a fixed options tuple. |
SelectLambda |
str |
One value from a list produced by options_lambda at configuration time. |
MultiSelectLambda |
tuple[str, ...] |
Any number of values from a list produced by options_lambda. |
Use the lambda variants when the choices are not knowable when the test is written - a list of firmware versions on a server, branches in a repository, devices currently attached. The callable runs when the run configuration is opened, so the list is current rather than committed to source.
mode = OrcraftConfigField.Select(
default="fast",
label="Mode",
options=("fast", "thorough"),
)
version = OrcraftConfigField.SelectLambda(
default="",
label="Version",
options_lambda=list_available_versions,
)
Filesystem types
| Type | Value | Selects |
|---|---|---|
File |
str |
A single file path. |
Folder |
str |
A single folder path. |
FileList |
tuple[str, ...] |
Any number of file paths. |
FolderList |
tuple[str, ...] |
Any number of folder paths. |
These give the GUI a browse button and store the chosen path. The path is used as-is: the file stays where it is, and the test reads it from there.
Package upload types
| Type | Value | Selects |
|---|---|---|
PackageFile |
str |
A single file, uploaded with the test package. |
PackageFolder |
str |
A single folder, uploaded with the test package. |
PackageFileList |
tuple[str, ...] |
Any number of files, uploaded with the test package. |
PackageFolderList |
tuple[str, ...] |
Any number of folders, uploaded with the test package. |
A package field is a filesystem field that travels. The controller uploads what it points at to the
endpoints that need it before the run, and inside the test the value resolves to the path on the
machine the code is executing on. That is the difference that matters: a Folder is a path
on the controller, a PackageFolder is a path that exists wherever the step runs.
The endpoints option binds a package field to specific endpoint fields, so files upload
only where they are actually needed:
test_endpoint = OrcraftConfigField.Endpoint(default="localhost", label="Test Endpoint")
aux_endpoints = OrcraftConfigField.MultiSelectEndpoints(default=("localhost",), label="Aux Endpoints")
input_file = OrcraftConfigField.PackageFile(
default="",
label="Input File",
required=True,
endpoints=[test_endpoint],
)
input_folders = OrcraftConfigField.PackageFolderList(
default=(),
label="Input Folders",
required=True,
endpoints=[test_endpoint, aux_endpoints],
)
Without endpoints, a package field is uploaded to every endpoint the test uses.
Endpoint types
| Type | Value | Meaning |
|---|---|---|
Endpoint |
str |
One endpoint the test uses. A test may declare several - a producer and a consumer, for instance. |
MultiSelectEndpoints |
tuple[str, ...] |
A set of endpoints used together within one run, iterated by the test itself. |
EndpointFanout |
tuple[str, ...] |
A set of endpoints that each get their own run. Selecting three endpoints spawns three test runs. |
All three accept port, secure_enabled and shared in addition to
the shared options, and all three take an
endpoint pool as their default in place of
a machine name. Orcraft Endpoints covers what they
do and when to reach for each field type.
Composition types
| Type | Declared on | Holds |
|---|---|---|
Groups |
Orcraft Test Suite config | The group instances the suite runs. |
Tests |
Orcraft Test Group config | The test instances the group runs. |
Matrix |
Orcraft Test config | The dimensions that expand into cases. |
Dimension |
Inside a matrix | One named axis and its values. |
Groups and Tests always use default_factory, because they
construct component instances:
tests = OrcraftConfigField.Tests(
default_factory=lambda: [
MyFirstTest(),
MySecondTest(),
]
)
Matrix and Dimension have options of their own - generated values, exclusion
rules, randomization and seeding. They are covered in
Orcraft Matrix, alongside what they
produce.
Layout and presentation
| Member | Meaning |
|---|---|
Layout.NewRow |
Start the field on a new row of the configuration panel. |
Layout.UnderPrevious |
Place the field directly beneath the previous one. |
Format |
A presentation-only field, used for settings such as log orientation. |
LogTableOrientation.HORIZONTAL |
Log the config table as a row of columns - compact for a handful of fields. |
LogTableOrientation.VERTICAL |
Log one field per line - readable for many fields or long values. |
LogTableOrientation.OFF |
Do not log the table at all - for components whose fields add nothing to the log. |
log_orientation = OrcraftConfigField.Format(
default=OrcraftConfigField.LogTableOrientation.VERTICAL
)
Every component logs two tables: a configuration table before it runs and a result table after.
Each has its own log_orientation - the one on the config class controls the configuration
table, the one on the result class controls the result table - so you can turn off one and keep the
other. Turning off the configuration table also hides the description banner printed above it.
Test cycles, matrix cases and case cycles are built by the framework and have no config class of
their own, so their Configuration tables follow the test's config: horizontal or vertical as the
test's is, and OFF on a test's config hides the framework's cycle, case and matrix
tables beneath it. The test's own Result table follows the result class the test assigns to
self.result, and a case's Result table follows the class the body assigns to
case.result, or reads as the test's does when the body assigns none. The demo suite's
test_log_tables_off runs a matrix with two test cycles and two case cycles and prints
nothing but plain log lines.
For example, a step whose inputs are obvious from its name can drop its configuration table and still log its result:
class MyStepConfig(OrcraftTestStepConfig):
name = OrcraftConfigField.Text(default="My Step")
log_orientation = OrcraftConfigField.Format(
default=OrcraftConfigField.LogTableOrientation.OFF
)
Plain attributes
Not everything a config carries has to be an Orcraft field. A plain class attribute is a legitimate way to hand a value to a component when nobody needs to see or edit it - the framework leaves it alone entirely:
class MyConsumeStepConfig(OrcraftTestStepConfig):
name = OrcraftConfigField.Text(default="My Consume Step")
sample_rate = OrcraftConfigField.Numeric(default=48000, label="Sample Rate")
# Plain attributes: filled in by the calling flow, invisible everywhere else.
calibration = None
channel_map = ()
A flow sets them the same way it sets declared fields, through run_step keywords or the
config constructor:
second_result = self.run_step(
MyConsumeStep,
sample_rate=self.config.sample_rate,
calibration=first_result.calibration,
channel_map=first_result.channel_map,
)
| Aspect | Orcraft field | Plain attribute |
|---|---|---|
| Shown in the run configuration panel | Yes, when it has a label | Never |
| Logged in the config table | Yes | Never |
Validated, coerced, required-checked |
Yes | No - the value is stored as given |
Settable through run_step keywords |
Yes | Yes, as long as the attribute exists on the class |
| Travels to a remote endpoint with the step | Yes | Yes - see Type fidelity |
The one rule is that the attribute must be declared on the class, even if only as None.
run_step checks that the config has the name before setting it, so a keyword that matches
nothing raises at the call site instead of silently creating an attribute nobody reads.
Use a plain attribute for data that is an implementation detail between a flow and its steps - a calibration table, a handle, a structure too large or too structured to be a GUI input. Use an Orcraft field for anything a person should be able to see or change before a run.
Not every inherited field is user-facing. name and description, for
instance, are configured in code but not editable from the GUI, and fields whose value is at its
default - a single cycle, an empty matrix - are hidden from the run panel rather than shown as
clutter.
The two policy fields take a fixed set of values. upload_test_package:
| Value | Meaning |
|---|---|
always |
Upload on every run. The safe choice while the suite is changing. |
if_missing |
Upload only when the endpoint does not already have the package. The default. |
never |
Never upload - the endpoint is expected to have the package already. |
update_endpoint_app:
| Value | Meaning |
|---|---|
always |
Update the endpoint application before every run. |
on_mismatch |
Update only when the endpoint application does not match the controller. The default. |
never |
Leave the endpoint application as it is. |