| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import logging
- import pytest
- from unittest.mock import ANY, Mock, call, patch
- import cipy
- import cipy.workflow
- from cipy.workflow import Job
- from matchers import HasAttributes
- class NoOp(cipy.action.Action):
- def run(self, context: cipy.Context) -> cipy.Status:
- return cipy.Status.NOT_RUN
- class MockRunAction(cipy.action.Action, arbitrary_types_allowed=True):
- mock: Mock
- def run(self, context: cipy.Context) -> cipy.Status:
- return self.mock.run(name=self.name, context=context)
- def test_workflow_constructs_depends_map() -> None:
- workflow = cipy.workflow.Workflow(
- name="Test",
- jobs=[
- Job(id="A", action=NoOp(name="A")),
- Job(id="B", needs=["A"], action=NoOp(name="B")),
- Job(id="C", needs=["A"], action=NoOp(name="C")),
- Job(id="D", needs=["B", "C"], action=NoOp(name="D")),
- ],
- )
- assert workflow._depends == {
- "A": [],
- "B": ["A"],
- "C": ["A"],
- "D": ["B", "C"],
- }
- def test_workflow_calls_in_order() -> None:
- mock = Mock()
- mock.run.return_value = cipy.Status.SUCCESS
- workflow = cipy.workflow.Workflow(
- name="Test",
- jobs=[
- Job(id="A", action=MockRunAction(name="A", mock=mock)),
- Job(id="B", needs=["A"], action=MockRunAction(name="B", mock=mock)),
- Job(id="C", needs=["A"], action=MockRunAction(name="C", mock=mock)),
- Job(id="D", needs=["B", "C"], action=MockRunAction(name="D", mock=mock)),
- ],
- )
- workflow.run(cipy.Context())
- mock.run.assert_has_calls(
- [
- call(name="A", context=ANY),
- call(name="B", context=ANY),
- call(name="C", context=ANY),
- call(name="D", context=ANY),
- ],
- any_order=False,
- )
- def test_workflow_stops_after_failure() -> None:
- mock = Mock()
- mock.run.side_effect = [cipy.Status.SUCCESS, cipy.Status.FAILURE]
- workflow = cipy.workflow.Workflow(
- name="Test",
- jobs=[
- Job(id="A", action=MockRunAction(name="A", mock=mock)),
- Job(id="B", needs=["A"], action=MockRunAction(name="B", mock=mock)),
- Job(id="C", needs=["A"], action=MockRunAction(name="C", mock=mock)),
- Job(id="D", needs=["B", "C"], action=MockRunAction(name="D", mock=mock)),
- ],
- )
- workflow.run(cipy.Context())
- mock.run.assert_has_calls(
- [
- call(name="A", context=ANY),
- call(name="B", context=ANY),
- ],
- any_order=False,
- )
- assert len(mock.run.call_args_list) == 2
- def test_matrix_expands_combinatorally(ci_logger) -> None:
- mock = Mock()
- mock.run.return_value = cipy.Status.SUCCESS
- matrix = cipy.workflow.Matrix(
- on={
- "compiler": ["clang12", "clang18"],
- "os": ["ubuntu22", "ubuntu24"],
- "release": [True, False],
- },
- uses=MockRunAction(name="Test", mock=mock),
- )
- with patch("copy.deepcopy", side_effect=lambda x: x):
- matrix.run(cipy.Context())
- assert len(mock.run.call_args_list) == 8
- def call_with_args(**kwargs):
- return call(HasAttributes(levelno=logging.INFO, args=({**kwargs})))
- ci_logger.format.assert_has_calls(
- [
- call_with_args(compiler="clang12", os="ubuntu22", release=True),
- call_with_args(compiler="clang12", os="ubuntu22", release=False),
- call_with_args(compiler="clang12", os="ubuntu24", release=True),
- call_with_args(compiler="clang12", os="ubuntu24", release=False),
- call_with_args(compiler="clang18", os="ubuntu22", release=True),
- call_with_args(compiler="clang18", os="ubuntu22", release=False),
- call_with_args(compiler="clang18", os="ubuntu24", release=True),
- call_with_args(compiler="clang18", os="ubuntu24", release=False),
- ],
- any_order=True,
- )
- def test_matrix_deep_copies_used_action(ci_logger) -> None:
- mock = Mock()
- mock.run.return_value = cipy.Status.SUCCESS
- matrix = cipy.workflow.Matrix(
- on={
- "compiler": ["clang12", "clang18"],
- "os": ["ubuntu22", "ubuntu24"],
- "release": [True, False],
- },
- uses=MockRunAction(name="Test", mock=mock),
- )
- matrix.run(cipy.Context())
- assert len(mock.run.call_args_list) == 0
- def test_matrix_fails_fast(ci_logger) -> None:
- mock = Mock()
- mock.run.side_effect = [cipy.Status.SUCCESS, cipy.Status.FAILURE]
- mock.run.return_value = cipy.Status.SUCCESS
- matrix = cipy.workflow.Matrix(
- on={
- "compiler": ["clang12", "clang18"],
- "os": ["ubuntu22", "ubuntu24"],
- "release": [True, False],
- },
- uses=MockRunAction(name="Test", mock=mock),
- )
- with patch("copy.deepcopy", side_effect=lambda x: x):
- matrix.run(cipy.Context())
- assert len(mock.run.call_args_list) == 2
- def test_matrix_can_disable_failfast(ci_logger) -> None:
- mock = Mock()
- mock.run.side_effect = [
- cipy.Status.SUCCESS,
- cipy.Status.FAILURE,
- cipy.Status.SUCCESS,
- cipy.Status.SUCCESS,
- ]
- matrix = cipy.workflow.Matrix(
- on={
- "compiler": ["clang12", "clang18"],
- "os": ["ubuntu22", "ubuntu24"],
- },
- uses=MockRunAction(name="Test", mock=mock),
- fail_fast=False,
- )
- with patch("copy.deepcopy", side_effect=lambda x: x):
- matrix.run(cipy.Context())
- assert len(mock.run.call_args_list) == 4
|