Orcraft 1.1.3 public beta released 08-Nov-2026. The Free Plan and the Team trial are open; card payments and invoices open shortly.

Orcraft 1.1.3 public beta released 08-Nov-2026. The Free Plan and the Team trial are open; card payments and invoices open shortly.

Orcraft

The platform for test development and orchestration
that lets you and your AI Agents run the entire testing operation

Watch the demo

Orcraft Test Batch

A batch sits above suites. It holds any mix of suites, groups and tests, runs them as one and summarizes them as one. It is what a CI snapshot exported from the controller puts on top of its mirrors, and what you write yourself to run several suites, possibly from several repositories, with a single command. Like a suite it is declared rather than coded.

from __future__ import annotations

from orcraft import (
	OrcraftConfigField,
	OrcraftTestBatch,
	OrcraftTestBatchConfig,
	OrcraftTestBatchItem,
	OrcraftTestBatchResult,
)

from my_suite import MySuite
from smoke.smoke_group import SmokeGroup


class NightlyBatchConfig(OrcraftTestBatchConfig):
	name = OrcraftConfigField.Text(default="Nightly")
	description = OrcraftConfigField.Text(
		default="Everything that must be green before a release."
	)
	items = OrcraftConfigField.Items(
		default_factory=lambda: [
			MySuite(),
			SmokeGroup(),
			OrcraftTestBatchItem(source_path="../orcraft-test-suite-network/test_suite_network.py"),
		]
	)


class NightlyBatchResult(OrcraftTestBatchResult):
	pass


class NightlyBatch(OrcraftTestBatch):
	def __init__(self) -> None:
		super().__init__(config=NightlyBatchConfig())
		self.result = NightlyBatchResult()


def main() -> None:
	NightlyBatch().run()


if __name__ == "__main__":
	main()

Items field

items uses default_factory for the same reason groups does.

Instance or reference

An item is one of two things:

Item When to use it
An instance The suite, group or test comes from the batch file's own repository, or from one already on its import path. It is imported at the top of the batch file and constructed in the list, like a group's tests.
A reference: OrcraftTestBatchItem The item comes from another repository, or the batch must run where the code sits somewhere else. It names the item by its source file; only the subprocesses that run it import it, never the batch's own process.

What a reference adds

A reference does two things an instance cannot. Two repositories may both define a top-level package with the same name - a common package, say - and importing both into one interpreter would let one shadow the other; a reference keeps each item in its own interpreter all the way down. And a reference is only a path, so it can be pointed elsewhere before the run: ci_run.py re-roots each one when a pipeline checks the repositories out in another place. That is why a CI snapshot lists its items this way.

Reference fields

A reference needs only its file, one suite, group or test per file. The batch finds the rest the way the controller does when it is given a file: the class, its kind and its config's name, from a short process of its own that imports the file; the folders the file's imports need, read from the file itself; and the repository whose code endpoint uploads ship. That is the file's own repository, or, for a CI mirror or a configured copy that extends a class of the same name, the repository of that class.

OrcraftTestBatchItem field Meaning
source_path The .py file of the suite, group or test. The only field an item needs.
import_roots The directories to import the file with, in place of the ones read from it, for a file whose imports reach a repository beside its own folder rather than above it. A CI mirror lists the snapshot folder and the live suite repository it mirrors.
label A display label, shown after the item's name and kept so the controller can show it again when it opens the batch.

Relative paths in a reference are relative to the batch file, so a folder holding a batch and its mirrors can be copied as a whole.

Config fields

Overridable in code

Set in your config class, fixed for the run. A batch has no run configuration panel: the controller shows a batch's items, never the batch itself.

Field Default Meaning
name "Test Batch" Display name in the CLI and logs.
description "" Free text describing what the batch covers.
version "" Batch version, recorded with the results.
items [] The suites, groups, tests and references this batch runs.
log_orientation HORIZONTAL Orientation of the logged config table, or OFF to leave it out of the log.
Override carefully

Redeclare a field with the same OrcraftConfigField type and a new default - name = OrcraftConfigField.Text(default="Nightly"). A bare assignment such as name = "Nightly" is not an override: it shadows the field with a plain attribute, and the log table no longer sees it.

Reserved

Written by the framework while a run is in progress.

Field Meaning
start_time When the run started. Stamped by the framework at run time.
log_tab Log indentation. Assigned by the framework at every level.
Refused by the framework

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.

Item paths

The CI export computes the paths of every item when it writes the snapshot. It knows the folder it writes into and the folder each loaded suite came from, and it stores the relative path from the first to the second in the item. Take a repository that keeps its suites and its exported snapshot together:

orcraft-validation/
  CI/
    Validation/
      batch.py
      ci_run.py
      test_suite_controller/            <- mirrors written by the export
  Suites/
    test-suite-controller/              <- the suite as the team wrote it
      test_suite_orcraft_controller_validation.py

The snapshot was written into CI/Validation, so the item carries the way from there to Suites/test-suite-controller:

OrcraftTestBatchItem(
	source_path="test_suite_controller/test_suite_orcraft_controller_validation.py",
	import_roots=(".", "../../Suites/test-suite-controller"),
)

source_path points at the mirror beside the batch. import_roots puts the snapshot folder and the suite folder on the import path, and the suite folder, the repository of the class the mirror extends, is what uploads ship to the endpoints. Nothing is edited by hand: as long as the snapshot and the suites stay where they were at export time, python batch.py runs. When a pipeline checks the suites out somewhere else, ci_run.py re-roots the items by repository name, from --suites-root or from an ORCRAFT_SUITE_ROOT_<REPO> variable, as Batch execution below describes.

Snapshot and suites move together

The stored paths are valid only for the placement at export time. Moving the snapshot folder, or the suites, breaks them: the items no longer find their suite and the batch fails to load. Either move the snapshot and the suites together, keeping their relative position, or run the snapshot through ci_run.py and tell it where the suites are.

Batch execution

The same file serves every way of running:

Where How
Standalone Execute the batch file directly, via its main().
Controller GUI Add the batch file like any other item. The controller flattens it: its items land at the top of the session tree and the batch itself never becomes a node. An item written by the CI export shows the original suite, group or test with the exported values as its plan and the CI tag on each test. The session itself is untouched.
CI A CI snapshot exported from the controller is a batch: batch.py lists the exported items as references to the mirrors beside it, and ci_run.py runs it on a CI machine, resolving each item's repository from --suites-root, from an ORCRAFT_SUITE_ROOT_<REPO> variable or from the paths baked in at export time, and exiting with 0, 10 or 20 for pass, fail or error.
AI agent Let an AI agent write a batch of the items it was asked to run, each configured through a subclass, and run it from the command line.

A batch always runs its items concurrently, each in its own subprocess. Suite and group items start at once and start their own children; the tests of the whole batch, test items included, take their turn in one queue kept for the run, in the order the items list them, and a test starts when every endpoint it needs is free of the batch's other tests, exactly as it would under the controller. When an item finishes, its whole log is appended to the batch log as one block, so the batch log reads as nested blocks: the batch header, then each item's complete log as it finishes, then the batch summary.

A batch cannot contain another batch.

Result fields

A batch has no body: its result is written entirely by the framework as its items complete, and an empty OrcraftTestBatchResult subclass reports everything below. Items may be of different kinds, so the batch counts what every kind has, tests and cases, plus the items themselves.

overridable by user (optional)  ·  written by the framework, may still be set directly  ·  reserved

Inherited field Overridden by user (optional) Reserved Meaning
status – PASS, FAIL or ERROR, derived from the items.
failure_description, error_description, warning_description – The first such description reported by an item.
finish_time, elapsed_time – When the batch finished and how long it took.
log_orientation – Whether the result table is logged horizontally, vertically or not at all (OFF). Defaults to vertical.
total_items, passed_items, failed_items, error_items, warning_items – Item counters, one per suite, group or test in the batch.
total_tests, passed_tests, failed_tests, error_tests, warning_tests – Test counters, summed across every item; a test item counts as one test.
total_cases, done_cases, passed_cases, failed_cases, error_cases, warning_cases – Case counters, summed across every test of every item: the planned total, the done cases and the passed, failed, errored and warned ones, each logged with its share of the total.
Override carefully

The only field that is yours here is log_orientation, and only its default: redeclare it with the same OrcraftResultField type - log_orientation = OrcraftResultField.Format(default=OrcraftResultField.LogTableOrientation.VERTICAL) - never as a bare value, which shadows the field so the log table no longer sees it.

Refused by the framework

A class of your own that declares one of the reserved 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. Only log_orientation is meant to be redeclared, and only to change its default.

After its result table the batch logs an Items table, one row per item with that item's status, descriptions, times and counters, and then its warnings, failures and errors tables with an Item column in front, so a failing case can be traced to the item, group and test it belongs to.

Type to search the docs.

    ↑↓ navigate ↵ open Esc close