avocado_i2n.cartgraph.graph module

Main test suite data structure.

SUMMARY

The data structure contains tests as nodes in a bidirected graph with edges to their dependencies (parents) and dependables (children) but also with a separate edge for each stateful object.

Copyright: Intra2net AG

INTERFACE

avocado_i2n.cartgraph.graph.set_graph_logging_level(level: int = 20) None[source]

Set the logging level specifically for the Cartesian graph.

This determines what descriptions of the graph will be dumped for debugging purposes.

class avocado_i2n.cartgraph.graph.TestGraph[source]

Bases: object

The main parsed and traversed test data structure.

This data structure uses a tree for each test object all of which overlap in a directed graph. All tests are using objects that can be brought to certain states and need some specific setup. All states can thus be saved and reused for other tests, resulting in a tree structure of derived states for each object. These object trees are then interconnected as a test might use multiple objects (vms) at once resulting in a directed graph. Running all tests is nothing more but traversing this graph in DFS-like way to minimize setup repetition. The policy of this traversal determines whether an automated setup (tests not defined by the user but needed for his/her tests) will be performed, ignored, overwritten, etc. The overall graph is extracted from the given Cartesian configuration, expanding Cartesian products of tests and tracing their object dependencies.

property nodes: tuple[TestNode]

Read-only list of test nodes.

property objects: tuple[TestObject]

Read-only list of test objects.

logdir = None
new_objects(objects: list[TestObject] | TestObject) None[source]

Add new objects excluding (old) repeating ones as ID.

Parameters:

objects – candidate test objects

new_nodes(nodes: list[TestNode] | TestNode) None[source]

Add new nodes excluding (old) repeating ones as ID.

Parameters:

nodes – candidate test nodes

new_workers(workers: list[TestWorker] | TestWorker) None[source]

Add new workers excluding (old) repeating ones as ID.

Parameters:

workers – candidate test workers

load_setup_list(dump_dir: str, filename: str = 'setup_list') None[source]

Load the setup state of each node from a list file.

Parameters:
  • dump_dir – directory for the dump image

  • filename – file to load the setup information from

save_setup_list(dump_dir: str, filename: str = 'setup_list') None[source]

Save the setup state of each node to a list file.

Parameters:
  • dump_dir – directory for the dump image

  • filename – file to save the setup information to

report_progress() None[source]

Report the total test run progress.

The progress is counted as the number and percentage of tests that are fully finished will not be run again

The estimation includes setup tests which might be reused and therefore provides worst case scenario for the number of remaining tests. It also does not take into account the duration of each test which could vary significantly.

visualize(dump_dir: str, tag: str = '0') None[source]

Dump a visual description of the Cartesian graph at a given parsing/traversal step.

Parameters:
  • dump_dir – directory for the dump image

  • tag – tag of the dump, e.g. parsing/traversal step and slot

flag_children(node_name: str = '', object_name: str = '', worker_name: str = '', flag_type: str = 'run', flag: function = <function TestGraph.<lambda>>, skip_parents: bool = False, skip_children: bool = False) None[source]

Set the run/clean flag for all children of a parent node of a given name.

Parameters:
  • node_name – name of the parent node or root if None

  • object_name – test object whose state is set or shared root if None

  • worker_name – test worker whose’s run/clean policy will be modified

  • flag_type – ‘run’ or ‘clean’ categorization of the children

  • flag – whether and when the run/clean action should be executed

  • skip_parents – whether the parents should not be flagged (just children)

  • skip_children – whether the children should not be flagged (just roots)

Raises:

AssertionError if obtained # of root tests is != 1

..note:: Works only with connected graphs and will skip any disconnected nodes.

flag_intersection(graph: TestGraph, flag_type: str = 'run', flag: function = <function TestGraph.<lambda>>, skip_object_roots: bool = False, skip_shared_root: bool = False) None[source]

Set the run/clean flag for all test nodes intersecting with the test nodes from another graph.

Parameters:
  • graph – Cartesian graph to intersect the current graph with

  • flag_type – ‘run’ or ‘clean’ categorization of the children

  • flag – whether and when the run/clean action should be executed

  • skip_object_roots – whether the object roots should not be flagged as well

  • skip_shared_root – whether the shared root should not be flagged as well

..note:: Works also with disconnected graphs and will not skip any disconnected nodes.

static parse_flat_objects(suffix: str, category: str, restriction: str = '', params: Params = None, unique: bool = False) list[TestObject] | TestObject[source]

Parse flat objects for each variant of a suffix satisfying a restriction.

Parameters:
  • suffix – suffix to expand into variant objects

  • category – category of the suffix that will determine the type of the objects

  • restriction – single or multi-line restriction to use

  • params – additional parameters to add to or overwrite all objects’ parameters

  • unique – whether to expect, validate, and return a unique object

Returns:

a list of parsed flat test objects

static parse_composite_objects(suffix: str, category: str, restriction: str = '', component_restrs: dict[str, str] = None, params: Params = None, verbose: bool = False, unique: bool = False) list[TestObject] | TestObject[source]

Parse a composite object for each variant from joined component variants.

Parameters:
  • suffix – suffix to expand into variant objects

  • category – category of the suffix that will determine the type of the objects

  • restriction – single or multi-line restriction to use

  • component_restrs – object-specific suffixes (keys) and variant restrictions (values) for the components

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

  • unique – whether to expect, validate, and return a unique object

Returns:

parsed test objects

static parse_suffix_objects(category: str, suffix_restrs: dict[str, str] = None, params: Params = None, verbose: bool = False, flat: bool = False) list[TestObject][source]

Parse all available test objects and their configuration determined by available suffixes.

Parameters:
  • category – category of suffixes that will determine the type of the objects

  • suffix_restrs – object-specific suffixes (keys) and variant restrictions (values) for the final objects

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

  • flat – whether to parse flat or composite objects

Returns:

parsed test objects

static parse_object_from_objects(suffix: str, category: str, test_objects: tuple[TestObject], params: Params = None, verbose: bool = False) TestObject[source]

Parse a unique composite object from joined already parsed component objects.

Parameters:
  • suffix – suffix to expand into variant objects

  • category – category of the suffix that will determine the type of the objects

  • test_objects – fully parsed test objects to parse the composite from

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

Returns:

parsed test objects

Raises:

exceptions.AssertionError if the parsed composite is not unique

static parse_components_for_object(test_object: TestObject, category: str, restriction: str = '', params: Params = None, verbose: bool = False, unflatten: bool = False) list[TestObject][source]

Parse all component objects for an already parsed composite object.

Parameters:
  • test_object – flat or fully parsed test object to parse for components of

  • category – category of the suffix that will determine the type of the objects

  • restriction – restriction for the unflattened object if needed

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

  • unflatten – whether to unflatten flat objects with their components

static parse_net_from_object_restrs(suffix: str, object_restrs: dict[str, str] = None) TestObject | NetObject[source]

Parse a default net with object strings as compatibility.

Parameters:
  • suffix – suffix of the net to parse

  • object_restrs – object (vm) restrictions as component restrictions for the net

Returns:

default net object

static parse_flat_nodes(restriction: str = '', params: Params = None, unique: bool = False) list[TestNode] | TestNode[source]

Parse a flat node for each variant of satisfying a restriction.

Parameters:
  • restriction – single or multi-line restriction to use

  • params – runtime parameters used for extra customization

  • unique – whether to expect, validate, and return a unique node

Returns:

a list of parsed flat test nodes

static parse_node_from_object(test_object: TestObject, restriction: str = '', prefix: str = '', params: Params = None) TestNode[source]

Get a unique test node of some restriction for the given object.

Parameters:
  • test_object – fully parsed test object to parse the node from, typically a test net

  • restriction – single or multi-line restriction to use

  • prefix – extra name identifier for the test to be run

  • params – runtime parameters used for extra customization

Returns:

parsed test node for the object

Raises:

ValueError if the node is parsed from a non-net object

Raises:

param.EmptyCartesianProduct if a vm variant is not compatible with another vm variant within the same test node

parse_nodes_from_flat_node_and_object(test_node: TestNode, test_object: TestObject, prefix: str = '', params: Params = None, verbose: bool = False) list[TestNode][source]

Parse composite nodes from a flat node and a flat object.

Parameters:
  • test_node – flat test node to use as a source of parameters and restrictions

  • test_object – possibly flat test object to compose the node on top of, typically a test net

  • prefix – extra name identifier for the test to be run

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

Returns:

parsed test nodes

Raises:

param.EmptyCartesianProduct if no result on preselected vm

All already parsed test objects will be used to also validate test object uniqueness and main test object.

parse_composite_nodes(restriction: str = '', test_object: TestObject = None, prefix: str = '', params: Params = None, verbose: bool = False, unique: bool = False) list[TestNode] | TestNode[source]

Parse all user defined tests (leaf nodes).

Use the nodes restriction string and possibly restrict to a single test object for the singleton tests.

Parameters:
  • restriction – single or multi-line restriction to use

  • test_object – possibly flat test object to compose the node on top of, typically a test net

  • prefix – extra name identifier for the test to be run

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

  • unique – whether to expect, validate, and return a unique node

Returns:

parsed test nodes

Raises:

param.EmptyCartesianProduct if no result on preselected vm

All already parsed test objects will be used to also validate test object uniqueness and main test object.

static parse_object_nodes(worker: TestWorker = None, restriction: str = '', prefix: str = '', object_restrs: dict[str, str] = None, params: Params = None, verbose: bool = False) tuple[list[TestNode], list[TestObject]][source]

Parse test nodes based on a selection of parsable objects.

Parameters:
  • worker – worker to parse the objects and nodes with or none for backward compatibility

  • restriction – single or multi-line restriction to use

  • prefix – extra name identifier for the test to be run

  • object_restrs – vm restrictions as component restrictions for the nets and thus nodes

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

Returns:

parsed test nodes and test objects

Raises:

param.EmptyCartesianProduct if no test variants for the given vm variants

The rest of the parameters are identical to the methods before.

We will parse all available objects in the configs, then parse all selected nodes and finally restrict to the selected objects specified via the object strings (if set) on a test by test basis.

parse_cloned_branches_for_node_and_object(test_node: TestNode, test_object: TestObject, test_nodes: list[TestNode]) list[TestNode][source]

Clone a test node and all of its descendants with one branch for each parent test node.

Parameters:
  • test_node – node to use as first round clone source

  • test_object – stateful object whose state will be modified for cloning

  • test_nodes – nodes to clone the source for as first round parents

parse_branches_for_node_and_object(test_node: TestNode, test_object: TestObject, params: Params = None) tuple[list[TestNode], list[TestNode]][source]

Parse all objects, parent object dependencies, and child clones for the current node and object.

Parameters:
  • test_node – possibly flat test node to parse and get nodes for

  • test_object – possibly flat test object to get network suffixes from (currently mostly a flat net)

  • params – runtime parameters used for extra customization

Returns:

a tuple of all reused and newly parsed parent test nodes as well as final child test nodes

parse_paths_to_object_roots(test_node: TestNode, test_object: TestObject, params: Params = None) list[tuple[list[TestNode], list[TestNode], TestNode]][source]

Parse the setup paths from a flat node to the terminal nodes of all its objects.

Parameters:
  • test_node – possibly flat test node to parse and get the complete graph paths for

  • test_object – possibly flat test object to get network suffixes from (currently mostly a flat net)

  • params – runtime parameters used for extra customization

Returns:

a generator of all resolved pairs of parents and children

parse_shared_root_from_object_roots(params: Params = None) TestNode[source]

Parse the shared root node from used test objects (roots) into a connected graph.

Parameters:

params – runtime parameters used for extra customization

Returns:

parsed shared root node of all object trees

static parse_workers(params: Params = None) list[TestWorker][source]

Parse all workers with special strings provided by the runtime.

Parameters:

params – extra parameters to be used as overwrite dictionary

Returns:

parsed test workers sorted by name with used ones having runtime strings

static parse_object_trees(worker: TestWorker = None, restriction: str = '', prefix: str = '', object_restrs: dict[str, str] = None, params: Params = None, verbose: bool = False, with_shared_root: bool = True) TestGraph[source]

Parse a complete test graph.

Parameters:
  • worker – worker traversing the graph with or none for backward compatibility

  • restriction – single or multi-line restriction to use

  • prefix – extra name identifier for the test to be run

  • object_restrs – vm restrictions as component restrictions for the nets and thus nodes

  • params – runtime parameters used for extra customization

  • verbose – whether to print extra messages or not

  • with_shared_root – whether to connect all object trees via shared root node

Returns:

parsed graph of test nodes and test objects

Parse all user defined tests (leaves) and their dependencies (internal nodes) connecting them according to the required/provided setup states of each test object (vm) and the required/provided objects per test node (test), obtaining and independent graph copy for each worker.

async traverse_terminal_node(object_name: str, worker: TestWorker, params: Params) bool[source]

Traverse an extra set of tests necessary for creating a given test object.

Parameters:
  • object_name – name of the test object to be created

  • worker – worker traversing the terminal node

  • params – runtime parameters used for extra customization

Returns:

whether the terminal node was run successfully or not

Raises:

NotImplementedError if using incompatible installation variant

The current implementation with implicit knowledge on the types of test objects internally spawns an original (otherwise unmodified) install test.

async traverse_node(test_node: TestNode, worker: TestWorker, params: Params) None[source]

Traverse a test node according to a given run policy and additional runner conditions.

Parameters:
  • test_node – node to be traversed

  • worker – worker traversing the terminal node

  • params – runtime parameters used for extra customization

async reverse_node(test_node: TestNode, worker: TestWorker, params: Params) None[source]

Reverse or traverse in the opposite direction a test node according to a given clean policy.

Parameters:
  • test_node – node to be reversed (traversed in the opposite direction)

  • worker – worker reversing the terminal node

  • params – runtime parameters used for extra customization

The reversal consists of cleanup or sync of any states that could be created by this node instead of running via the test runner which is done for the traversal.

async traverse_object_trees(worker: TestWorker, params: Params = None) None[source]

Run all user and system defined tests.

Optimize the setup reuse and minimize the repetition of demanded tests.

Parameters:
  • worker – worker traversing the graph

  • params – runtime parameters used for extra customization

Raises:

AssertionError if some traversal assertions are violated

The highest priority is at the setup tests (parents) since the test cannot be run without the required setup, then the current test, then a single child of its children (DFS), and finally the other children (tests that can benefit from the fact that this test/setup was done) followed by the other siblings (tests benefiting from its parent/setup.

Of course all possible children are restricted by the user-defined “only” and the number of internal test nodes is minimized for achieving this goal.