<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Chapitre 02.05 — Orchestration on Ocarina — précis</title>
    <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/</link>
    <description>Recent content in Chapitre 02.05 — Orchestration on Ocarina — précis</description>
    <generator>Hugo</generator>
    <language>fr-FR</language>
    <lastBuildDate>Thu, 04 Jun 2026 17:04:59 +0200</lastBuildDate>
    <atom:link href="https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>02.05.01 — Test[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/01-test/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/01-test/</guid>
      <description>&lt;h1 id=&#34;020501testdriver&#34;&gt;02.05.01 — &lt;code&gt;Test[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020501testdriver&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;signature&#34;&gt;Signature&lt;a class=&#34;anchor&#34; href=&#34;#signature&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Test&lt;/span&gt;[Driver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name: TestName,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_id: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_scenario: TestScenario[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pre_test_scenarios_fragments: Sequence[TestScenarioFragment[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        post_test_scenarios_fragments: Sequence[TestScenarioFragment[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        skipped: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_test_scenario &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_scenario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_pre_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; pre_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_post_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; post_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_skipped &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; skipped&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Six paramètres :&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Paramètre&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Rôle&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestName&lt;/code&gt; (&lt;code&gt;str&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Label humain ; apparaît dans le rapport, et &lt;strong&gt;devient le nom du fichier de log&lt;/strong&gt; (donc soumis à &lt;code&gt;is_valid_filename&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_id&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Identifiant stable pour &lt;code&gt;--only&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt;. Si absent : prend &lt;code&gt;name&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_scenario&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestScenario[Driver]&lt;/code&gt; (alias = &lt;code&gt;Callable[[Driver, ILogger], Scenario[Driver]]&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Factory&lt;/em&gt; qui construit le &lt;code&gt;Scenario&lt;/code&gt; au moment de l&amp;rsquo;exécution.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pre_test_scenarios_fragments&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[TestScenarioFragment[Driver]] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fonctions &lt;code&gt;(driver, logger) -&amp;gt; TestChain&lt;/code&gt; exécutées &lt;strong&gt;avant&lt;/strong&gt; le scénario principal.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;post_test_scenarios_fragments&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[TestScenarioFragment[Driver]] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fonctions &lt;code&gt;(driver, logger) -&amp;gt; TestChain&lt;/code&gt; exécutées &lt;strong&gt;après&lt;/strong&gt; le scénario principal.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;skipped&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Si &lt;code&gt;True&lt;/code&gt;, le test est enregistré mais pas exécuté.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;1-final&#34;&gt;1. &lt;code&gt;@final&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-final&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Pas d&amp;rsquo;héritage utilisateur. Si l&amp;rsquo;on veut un test « &lt;em&gt;spécial&lt;/em&gt; », on &lt;em&gt;compose&lt;/em&gt; via les fragments ou via un scénario, on ne sous-classe pas.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.02 — TestExecutor[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/02-test-executor/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/02-test-executor/</guid>
      <description>&lt;h1 id=&#34;020502testexecutordriver&#34;&gt;02.05.02 — &lt;code&gt;TestExecutor[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020502testexecutordriver&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_executor.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/internals/test_executor.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;strong&gt;Responsabilité unique&lt;/strong&gt; : exécuter &lt;strong&gt;une seule tentative&lt;/strong&gt; d&amp;rsquo;un test, avec &lt;strong&gt;un seul driver&lt;/strong&gt;. Ne connaît ni le rejeu, ni la pool de drivers, ni l&amp;rsquo;agrégation au niveau suite.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;executionoutcome&#34;&gt;&lt;code&gt;ExecutionOutcome&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#executionoutcome&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, slots&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ExecutionOutcome&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result: TestResult&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    skipped: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    setup_failed: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    should_retry: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps_count: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Champ&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Sens&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;result&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestResult&lt;/code&gt; = &lt;code&gt;Result[Any] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Le résultat de la chaîne (Ok, Fail), ou &lt;code&gt;None&lt;/code&gt; si skip / setup_failed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;skipped&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True si &lt;code&gt;test_runner.skipped&lt;/code&gt; (i.e. &lt;code&gt;Test(skipped=True)&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;setup_failed&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True si la fonction &lt;code&gt;setup()&lt;/code&gt; du scénario a levé.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;should_retry&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True si la &lt;em&gt;règle de rejeu&lt;/em&gt; s&amp;rsquo;applique (transient_error détecté).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;steps_count&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Nombre d&amp;rsquo;&lt;code&gt;act&lt;/code&gt; appelés ; &lt;code&gt;-1&lt;/code&gt; si skip ou setup_failed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;slots=True&lt;/code&gt;&lt;/strong&gt; : empêche l&amp;rsquo;ajout dynamique d&amp;rsquo;attributs et économise mémoire. C&amp;rsquo;est un objet qui circule en hot-path, le &lt;code&gt;slots&lt;/code&gt; est justifié.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;frozen=True&lt;/code&gt;&lt;/strong&gt; : immutable, sûr à partager entre threads.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;@final&lt;/code&gt;&lt;/strong&gt; : pas d&amp;rsquo;héritage.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;ordre-dexécution-dune-tentative&#34;&gt;Ordre d&amp;rsquo;exécution d&amp;rsquo;une tentative&lt;a class=&#34;anchor&#34; href=&#34;#ordre-dex%c3%a9cution-dune-tentative&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────────┐&#xA;│  test_runner = test.spawn(driver, logger_with_taxonomy)              │&#xA;└──────────────────────────────┬───────────────────────────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  test_runner.skipped ?          │── True ─► return Outcome(skipped=True, ...)&#xA;              └────────────────┬────────────────┘&#xA;                               │ False&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  logger.test_name(test.name)    │   (annotation)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  setup() (si non-None)          │── leve ─► teardown() (si non-None)&#xA;              └────────────────┬────────────────┘            ↓&#xA;                               │                  return Outcome(setup_failed=True, should_retry=True, ...)&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  watchers.start(driver, logger, │&#xA;              │                 take_screenshot)│   (1 daemon thread par watcher)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  _run_chain(chain_runners, ...) │── retourne (result, should_retry)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  watchers.stop()                │   (toujours)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  teardown() (si non-None)       │   (TOUJOURS, même si chain a fail)&#xA;              └────────────────┬────────────────┘     (les exceptions sont logguées &amp;amp; avalées)&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  steps_count = act_counter.get()│&#xA;              │  return Outcome(...)            │&#xA;              └─────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;execute&#34;&gt;&lt;code&gt;execute&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#execute&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;execute&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, test: Test[Driver], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver: Driver,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger_with_taxonomy: ILogger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger_without_taxonomy: ILogger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    attempt: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, max_attempts: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ExecutionOutcome:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_runner &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;spawn(driver, logger_with_taxonomy)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;skipped:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger_without_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_name(test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;setup &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;setup()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;except&lt;/span&gt; Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; exc:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            msg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt; -- Setup failed (attempt &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;attempt&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;max_attempts&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;): &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;exc&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger_with_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;warning(msg)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_teardown(test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                    should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    watchers: Sequence[Watcher[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;watchers &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_start_watchers(watchers, driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;taxonomy)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result, should_retry &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_chain(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;chain_runners, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, attempt&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;attempt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_without_taxonomy,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logger_with_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy, max_attempts&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;max_attempts,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_stop_watchers(watchers)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_teardown(test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps_count &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_act_counter&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;get()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;result, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;should_retry, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;steps_count)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;deux-loggers-pourquoi&#34;&gt;Deux loggers : pourquoi&lt;a class=&#34;anchor&#34; href=&#34;#deux-loggers-pourquoi&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;logger_with_taxonomy&lt;/code&gt; vs &lt;code&gt;logger_without_taxonomy&lt;/code&gt; :&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.03 — TestFlow[Driver] — politique de rejeu</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/03-test-flow-retries/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/03-test-flow-retries/</guid>
      <description>&lt;h1 id=&#34;020503testflowdriverpolitique-de-rejeu&#34;&gt;02.05.03 — &lt;code&gt;TestFlow[Driver]&lt;/code&gt; — politique de rejeu&lt;a class=&#34;anchor&#34; href=&#34;#020503testflowdriverpolitique-de-rejeu&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_flow.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/internals/test_flow.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Responsabilité unique&lt;/strong&gt; : pour un seul &lt;code&gt;Test&lt;/code&gt;, exécuter la boucle de rejeu, acquérir un driver propre à chaque tentative, et appliquer la politique de backoff.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pourquoi-un-niveau-intermédiaire-entre-testsuite-et-testexecutor&#34;&gt;Pourquoi un niveau intermédiaire entre &lt;code&gt;TestSuite&lt;/code&gt; et &lt;code&gt;TestExecutor&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pourquoi-un-niveau-interm%c3%a9diaire-entre-testsuite-et-testexecutor&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Niveau&lt;/th&gt;&#xA;          &lt;th&gt;Ne sait pas&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestExecutor&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Le rejeu, la pool de drivers&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestFlow&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;La concurrence inter-tests, l&amp;rsquo;agrégation suite-level&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestSuite&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;L&amp;rsquo;exécution d&amp;rsquo;une tentative, le rejeu&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;C&amp;rsquo;est une séparation &lt;em&gt;vraiment stricte&lt;/em&gt;. &lt;code&gt;TestFlow&lt;/code&gt; est le seul à connaître à la fois la pool et la politique de rejeu : son job est de &lt;em&gt;combiner&lt;/em&gt; les deux.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.04 — TestSuite[Driver] — moteur parallélisé</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/04-test-suite/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/04-test-suite/</guid>
      <description>&lt;h1 id=&#34;020504testsuitedrivermoteur-parallélisé&#34;&gt;02.05.04 — &lt;code&gt;TestSuite[Driver]&lt;/code&gt; — moteur parallélisé&lt;a class=&#34;anchor&#34; href=&#34;#020504testsuitedrivermoteur-parall%c3%a9lis%c3%a9&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_suite.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_suite.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Responsabilité unique&lt;/strong&gt; : exécuter une séquence de &lt;code&gt;Test&lt;/code&gt; en parallèle, gérer la &lt;em&gt;saturation&lt;/em&gt;, le filtrage par IDs, et la validation des invariants pré-exécution.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;constructeur&#34;&gt;Constructeur&lt;a class=&#34;anchor&#34; href=&#34;#constructeur&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tests: Sequence[Test[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    create_logger: Thunk[ILogger],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drivers_pool: WebDriversPool[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    take_screenshot: ITakeScreenshot[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    act_counter: ActCounter &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    transient_errors: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt;[Exception], &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    copy_indicator: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;COPY&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    put_space_after_copy_indicator: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max_retries_per_test: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    autoscreen_on_fail: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only_ids: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude_ids: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_guards_on_invoke(tests)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;# ... assignations ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_tests &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; filter_tests_by_ids(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tests,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        only&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;only_ids,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        exclude&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exclude_ids,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;create_logger()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;set_prefix(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;: filtering tests...&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Paramètre&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Rôle&lt;/th&gt;&#xA;          &lt;th&gt;Défaut&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Nom de la suite (apparaît dans logs &amp;amp; rapports)&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[Test[Driver]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Liste des tests à exécuter&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_logger&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[ILogger]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Factory de logger&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;drivers_pool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;WebDriversPool[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pool partagée&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;take_screenshot&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ITakeScreenshot[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Callable &lt;code&gt;(driver, logger, category) -&amp;gt; None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;act_counter&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActCounter | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Si &lt;code&gt;None&lt;/code&gt; → &lt;code&gt;ThreadsBasedActCounter()&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;transient_errors&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tuple[type[Exception], ...]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exceptions qui déclenchent un retry&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;copy_indicator&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Préfixe pour les tests clonés en saturation&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;COPY&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;put_space_after_copy_indicator&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[COPY 1]&lt;/code&gt; (&lt;code&gt;True&lt;/code&gt;) vs &lt;code&gt;[COPY1]&lt;/code&gt; (&lt;code&gt;False&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;max_retries_per_test&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Nombre maximum de retentatives d&amp;rsquo;un test potentiellement flaky&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; → &lt;code&gt;_DEFAULT_MAX_RETRIES_PER_TEST&lt;/code&gt; (8)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;autoscreen_on_fail&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Capture automatiquement un ou des screenshots (rafale) en cas d&amp;rsquo;échec&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;saturate_workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Clonage des tests pour saturer tous les workers si la pool est suffisamment grosse&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; → cascade : suite &amp;gt; campaign &amp;gt; bootstrap&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;only_ids&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Filtre &lt;code&gt;--only&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;exclude_ids&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Filtre &lt;code&gt;--exclude&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;garde-fous&#34;&gt;Garde-fous&lt;a class=&#34;anchor&#34; href=&#34;#garde-fous&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;au-moment-du-__init__&#34;&gt;Au moment du &lt;code&gt;__init__&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#au-moment-du-__init__&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_guards_on_invoke&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, tests: Sequence[Test[Driver]]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    validate_test_runners_ids(tests&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;tests, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ Tous les &lt;code&gt;test_id&lt;/code&gt; doivent être uniques. Sinon : &lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;. &lt;em&gt;Levé dès la construction&lt;/em&gt;, avant la moindre tentative d&amp;rsquo;exécution. Si l&amp;rsquo;utilisateur a dupliqué un &lt;code&gt;test_id&lt;/code&gt; par erreur, il l&amp;rsquo;apprend immédiatement.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.05 — Saturation des workers</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/05-saturation/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/05-saturation/</guid>
      <description>&lt;h1 id=&#34;020505saturation-des-workers&#34;&gt;02.05.05 — Saturation des workers&lt;a class=&#34;anchor&#34; href=&#34;#020505saturation-des-workers&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Mécanisme propre à Ocarina : si une suite a moins de tests que de workers disponibles, &lt;strong&gt;on clone aléatoirement&lt;/strong&gt; des tests jusqu&amp;rsquo;à atteindre le nombre de workers. Les copies sont renommées &lt;code&gt;[COPY 1] &amp;lt;name&amp;gt;&lt;/code&gt;, &lt;code&gt;[COPY 2] &amp;lt;name&amp;gt;&lt;/code&gt;, etc.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pourquoi&#34;&gt;Pourquoi&lt;a class=&#34;anchor&#34; href=&#34;#pourquoi&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Le Holy Book formalise la motivation dans le chapitre « &lt;em&gt;Premiers obstacles du monde réel&lt;/em&gt; » :&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Son option &lt;code&gt;saturate_workers&lt;/code&gt; permet de forcer du clonage aléatoire de tests à l&amp;rsquo;intérieur d&amp;rsquo;une suite.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.06 — TestCampaign[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/06-test-campaign/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/06-test-campaign/</guid>
      <description>&lt;h1 id=&#34;020506testcampaigndriver&#34;&gt;02.05.06 — &lt;code&gt;TestCampaign[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020506testcampaigndriver&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_campaign.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_campaign.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Responsabilité unique&lt;/strong&gt; : exécuter une &lt;strong&gt;séquence de suites&lt;/strong&gt; dans l&amp;rsquo;ordre, partager une config de workers, et déclarer &lt;code&gt;campaign_has_failed&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;TestCampaign&lt;/span&gt;[Driver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites: Sequence[TestSuite[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        max_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        validate_test_suites_names(suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;suites, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;suites&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; suites&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results: TestCampaignResults &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_max_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; max_workers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; saturate_workers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_campaign_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;run_all&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, skip_all: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaignResults:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;clear()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resolved_saturate_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt; saturate_workers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; skip_all:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results[suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    name: (&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, test_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; name, test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_names_and_ids&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results[suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                max_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_max_workers,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                saturate_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;resolved_saturate_workers,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;campaign_has_failed&lt;/span&gt;(results: TestCampaignResults) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;any&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        is_test_result_fail(outcome)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; campaign_results &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;values()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; outcome, _, _ &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; campaign_results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;values()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;anatomie&#34;&gt;Anatomie&lt;a class=&#34;anchor&#34; href=&#34;#anatomie&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;garde-fou-au-constructeur&#34;&gt;Garde-fou au constructeur&lt;a class=&#34;anchor&#34; href=&#34;#garde-fou-au-constructeur&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;validate_test_suites_names(suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;suites, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;suites&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ Les noms de suites de la campagne doivent être uniques — de façon insensible à la casse depuis &lt;code&gt;1.1.10&lt;/code&gt; (NFC + casefold). Levé &lt;em&gt;dès la construction&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.07 — TestCycle[Driver] &#43; modes</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/07-test-cycle-modes/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/07-test-cycle-modes/</guid>
      <description>&lt;h1 id=&#34;020507testcycledriver--modes&#34;&gt;02.05.07 — &lt;code&gt;TestCycle[Driver]&lt;/code&gt; + modes&lt;a class=&#34;anchor&#34; href=&#34;#020507testcycledriver--modes&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_cycle.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_cycle.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Responsabilité unique&lt;/strong&gt; : orchestrer les campagnes &lt;em&gt;smoke&lt;/em&gt; puis &lt;em&gt;main&lt;/em&gt;, et appliquer un mode de gestion d&amp;rsquo;échec des smoke.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;deux-modes&#34;&gt;Deux modes&lt;a class=&#34;anchor&#34; href=&#34;#deux-modes&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Mode &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fail-fast-on-first-smoke-campaigns-sequence-fail&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wait-for-all-smoke-tests&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Mode&lt;/th&gt;&#xA;          &lt;th&gt;Comportement&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;fail-fast-on-first-smoke-campaigns-sequence-fail&lt;/code&gt; (par défaut)&lt;/td&gt;&#xA;          &lt;td&gt;Dès qu&amp;rsquo;une campagne de smoke fail, les &lt;strong&gt;suivantes&lt;/strong&gt; sont &lt;em&gt;skippées&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;wait-for-all-smoke-tests&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Toutes&lt;/strong&gt; les campagnes de smoke s&amp;rsquo;exécutent. Si &lt;em&gt;au moins une&lt;/em&gt; fail, les campagnes &lt;em&gt;main&lt;/em&gt; sont skippées.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Cas&lt;/th&gt;&#xA;          &lt;th&gt;Mode&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Smoke par dépendance (par exemple : &amp;ldquo;login&amp;rdquo; puis &amp;ldquo;dashboard accessible&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;fail-fast&lt;/code&gt; (si login échoue, dashboard est inutile)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Smoke parallèles (par exemple : &amp;ldquo;homepage&amp;rdquo; + &amp;ldquo;API ping&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;wait-for-all&lt;/code&gt; (on veut voir les deux)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Le default est &lt;code&gt;fail-fast&lt;/code&gt; parce que c&amp;rsquo;est le cas le plus fréquent.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.08 — filter_tests_by_ids</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/08-filter-tests-by-ids/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/fr/02-ocarina/05-orchestration/08-filter-tests-by-ids/</guid>
      <description>&lt;h1 id=&#34;020508filter_tests_by_ids&#34;&gt;02.05.08 — &lt;code&gt;filter_tests_by_ids&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020508filter_tests_by_ids&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Fichier source : &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/filter_tests_by_ids.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/filter_tests_by_ids.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Utilisé par &lt;code&gt;TestSuite.__init__&lt;/code&gt; pour appliquer les flags CLI &lt;code&gt;--only &amp;lt;ids&amp;gt;&lt;/code&gt; et &lt;code&gt;--exclude &amp;lt;ids&amp;gt;&lt;/code&gt;. Mutex : on ne peut pas passer les deux.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;filter_tests_by_ids&lt;/span&gt;[Driver](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tests: Sequence[Test[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger: ILogger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Sequence[Test[Driver]]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;set&lt;/span&gt;(only)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude_set &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;set&lt;/span&gt;(exclude)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; exclude_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only and --exclude cannot be used together&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    known_ids &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; only_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        matched &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&lt;/span&gt; known_ids&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; matched:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            joined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sorted&lt;/span&gt;(matched))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only: matched test IDs: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;joined&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; [test &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; only_set]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; exclude_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        matched &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; exclude_set &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&lt;/span&gt; known_ids&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; matched:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            joined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sorted&lt;/span&gt;(matched))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--exclude: matched test IDs: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;joined&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; [test &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; exclude_set]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; tests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;quatre-garanties&#34;&gt;Quatre garanties&lt;a class=&#34;anchor&#34; href=&#34;#quatre-garanties&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-mutex---only---exclude&#34;&gt;1. Mutex &lt;code&gt;--only&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-mutex---only---exclude&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; exclude_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only and --exclude cannot be used together&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tenter de passer les deux est une erreur &lt;em&gt;runtime&lt;/em&gt; (pas une erreur &lt;em&gt;mypy&lt;/em&gt;). Sémantiquement, ça n&amp;rsquo;aurait pas de sens : « &lt;em&gt;ne garder que A, B&lt;/em&gt; » ET « &lt;em&gt;exclure C, D&lt;/em&gt; » ? Qu&amp;rsquo;est-ce que c&amp;rsquo;est que ces putains de conneries (à l&amp;rsquo;échelle d&amp;rsquo;une CLI) ? Le mutex évite l&amp;rsquo;ambiguïté.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
