Ctrl K
ComunidadGuías…

phpunit.xml

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="tests/bootstrap.php" colors="true" cacheDirectory=".phpunit.cache" executionOrder="random" failOnRisky="true" failOnWarning="true" beStrictAboutOutputDuringTests="true" displayDetailsOnTestsThatTriggerDeprecations="true" displayDetailsOnTestsThatTriggerWarnings="true" displayDetailsOnTestsThatTriggerErrors="true" displayDetailsOnSkippedTests="true">
  <!--
    ============================================================================
    S345 — displayDetailsOnSkippedTests="true" (on the root element above).
    ============================================================================
    Before this, the only thing a run published about its skips was the COUNT in
    "Tests: N, ..., Skipped: M." A count cannot distinguish the three things that
    move it — a test STARTED skipping, a conditionally-skipped test became a
    FAILURE, or a test DISAPPEARED — and it drifts on its own between runs because
    this repo has legitimate environment-dependent skips (this repo's CI reports 3;
    a developer box with no MySQL/Chromium/FFI reports ~259). Two audits were
    forced to record skip movement as "unproven" for exactly that reason.

    Why the CONFIG ATTRIBUTE and not the `display-skipped` CLI flag (CLI flags are
    spelled here without their two leading hyphens, because an XML comment may not
    contain them): the flag is per-invocation, and PHPUnit is invoked from five
    places in this repo — twice in .github/workflows/phpunit.yml, twice in
    .github/workflows/syncplay-e2e.yml, once in scripts/assertion-escape-audit.php —
    plus every developer's shell. A flag would have to be re-added to each of them
    and to every job added later — a hand-written list that never re-derives. This
    attribute is read by every invocation that loads this file, so a new job cannot
    silently ship without it.

    Those sites are cited by FILE, not by line: the revision of this comment that
    cited lines was made stale by its own commit. The count and the per-file
    multiplicity are RE-DERIVED from the repo by
    SkippedTestNameReportingTest::test_the_documented_invocation_sites_are_still_the_real_ones,
    which fails if an invocation is added, moved or removed.

    ⚠ It is NOT universal, and the two exceptions are real rather than theoretical:

      * `testdox`. PHPUnit builds the default result printer for a testdox run with
        its displayDetailsOnSkippedTests constructor argument hardcoded false
        (vendor/phpunit/phpunit/src/TextUI/Output/Facade.php:204-221; the
        configuration value is not consulted on that path), so such a run counts its
        skips in "Skipped: N" and can never name them, whatever this attribute or a
        `display-skipped` flag say. syncplay-e2e.yml used to pass it and no longer
        does, and SkippedTestNameReportingTest re-derives that ban from every .yml and
        .yaml workflow, folding shell continuations first. scripts/skipped-test-names.sh detects testdox output and exits 5 saying
        so, instead of blaming this file.
      * the assertion-escape prober (scripts/assertion-escape-audit.php) captures
        PHPUnit's output into a PHP variable and never echoes it, so no name set is
        obtainable from that job. Its config is fine; its output never leaves the
        process.

    It is purely additive: this setting gates only two printList calls in
    ResultPrinter::print() (lines 125-128), which APPEND lists. It does not change
    the process exit status (that is `failOnSkipped`, a separate setting this repo
    does not set) and it suppresses nothing.

    It enables TWO lists, not one: printSkippedTestSuites() as well as
    printSkippedTests(), because "Skipped: N" is the SUM of both event kinds
    (SummaryPrinter.php:106). A skipped SUITE (class-level Requires attribute, or
    markTestSkipped() in setUpBeforeClass()) is named under a different header and its
    entries carry no "::"; scripts/skipped-test-names.sh parses both and emits a suite
    as "SUITE:name".

    scripts/skipped-test-names.sh turns the resulting output into a sorted,
    `comm`-ready name set, and exits 4 rather than printing an empty set if this
    attribute ever goes missing. tests/Unit/Support/SkippedTestNameReportingTest.php
    fails if it is removed from this file.
  -->
  <testsuites>
    <testsuite name="Unit">
      <directory suffix="Test.php">tests/Unit</directory>
    </testsuite>
    <testsuite name="Integration">
      <directory suffix="Test.php">tests/Integration</directory>
    </testsuite>
    <testsuite name="E2E">
      <directory suffix="Test.php">tests/E2E</directory>
    </testsuite>
  </testsuites>
  <groups>
    <exclude>
      <group>network</group>
    </exclude>
  </groups>
  <!--
    ============================================================================
    S141 — HOW TO READ THIS REPO'S COVERAGE NUMBERS. This is the ONE
    authoritative statement; nothing else in the repo gets to contradict it.
    ============================================================================

    THE POLICY (decided 2026-08-03, on the measurement below):
    **This repo carries NO PHPUnit coverage metadata in tests/.** No `@covers`,
    no `@coversNothing`, no `#[CoversClass]` / `#[CoversMethod]` /
    `#[CoversFunction]` / `#[CoversNothing]`. Coverage is read WHOLE-RUN: a
    file's figure is everything the suite executed in it.

    THEREFORE:
      * A 0.00% file is a file the suite NEVER EXECUTES. That reading is now
        sound, and it was not before.
      * A non-zero figure is what the suite executed. It is NOT a claim that
        every executed line is asserted — see "the cost", below.
      * The headline statement percentage, and the MIN_COVERAGE floor in
        scripts/coverage-threshold-check.php, are a ratchet over that figure.

    tests/Unit/Support/CoverageMetadataPolicyTest.php enforces all of it and
    fails if metadata reappears, if this comment is deleted, or if
    phpunit.xml starts requiring coverage metadata. Do NOT re-add an annotation
    to silence it.

    WHY — measured, not assumed. PHP 8.3.6 + PCOV 1.0.11, the CI invocation
    (`php -d max_execution_time=0 vendor/bin/phpunit` with the clover report),
    real MySQL 8.0, one identical random seed, the SAME tree twice with only the
    603 annotation lines removed on the second pass. Both runs: Tests 8971,
    Skipped 7, same failures.

      metric (Clover /coverage/project/metrics)   with @covers    without
      statements                                  65.49%          67.22%
                                                  41939/64039     43045/64039
      methods                                     3110/6057       3231/6057
      classes                                     160/672         175/672

      files reporting 0.00% .................... 143             101
        of which FALSE zeros (executed anyway) ..  42               0
        of which reach 100% once un-narrowed ....   6               -
      files that LOST coverage .................   -               0

    So 42 of the 143 zeros — 29% of them — were files the suite runs and the
    report called untested; six of those are executed in FULL. That is the
    defect: "untested" and "attributed elsewhere by an annotation" were
    indistinguishable, silently, and the natural reaction to a 0% file is to
    write duplicate tests for behaviour that is already covered. (S96 and S121
    each came within one step of doing exactly that.)

    The mechanism, for the record: PHPUnit 10.5
    `Framework/TestRunner.php:163` asks `Metadata/Api/CodeCoverage.php` for the
    lines to be covered, and php-code-coverage
    `CodeCoverage::applyCoversAndUsesFilter()` then does one of three things —
    `false` (@coversNothing) CLEARS the whole run's data for that test, a
    NON-EMPTY list keeps only the named units' listed lines and DELETES every
    other file, and `[]` (no metadata) keeps everything. No warning is emitted
    in any of the three cases.

    Two further reasons the annotations had to go rather than be completed:
      * The report was not one measurement but two incompatible ones mixed —
        369 of 707 test files narrowed, 338 did not. An aggregate over that
        mixture is not a measurement of anything.
      * The annotations were inert as documentation and destructive as
        configuration: this file sets neither `requireCoverageMetadata` nor
        `beStrictAboutCoverageMetadata`, which are the only two settings that
        make coverage metadata load-bearing. Nothing checked that a `@covers`
        was correct, complete, or even resolvable.

    THE COST, stated rather than hidden: whole-run coverage counts a line that
    was merely EXECUTED, not necessarily asserted. 67.22% is therefore an upper
    bound on "behaviour pinned by an assertion" in exactly the way 65.49% was a
    lower bound on "code the suite runs". The defences against vacuous
    execution are separate and already in place — the S120 assertion-escape
    extension registered below, scripts/assertion-escape-audit.php in probe mode, and
    per-step mutation testing. Coverage is not, and was never, that defence.

    ONE DOCUMENTED EXCEPTION.
    tests/Unit/Server/Http/Controllers/Admin/AdminSettingsControllerTest.php
    still carries a `@covers`. A concurrent writer owned that file (uncommitted)
    when this policy landed, so it was deliberately left alone rather than have
    two writers edit one file. It is named explicitly in
    CoverageMetadataPolicyTest's allow-list, and that test FAILS once the
    annotation is gone, so the exception cannot rot into a permanent hole.
  -->
  <coverage>
    <report>
      <html outputDirectory="coverage-report"/>
      <clover outputFile="coverage.xml"/>
      <text outputFile="php://stdout" showUncoveredFiles="true" showOnlySummary="true"/>
    </report>
  </coverage>
  <extensions>
    <!--
      S120 — reports any test where a failed assertion did not decide the test's
      outcome (an assertion swallowed between the assertion and PHPUnit — by
      `catch (\Throwable)`, `catch (\Exception)` or `catch (\RuntimeException)`, or by
      a `return` inside a `finally` with no catch at all). See
      tests/Support/AssertionEscape/EscapeCollector.php for the mechanism, for the four
      blind spots (Assert::fail(), mock parameter/invocation rules,
      markTestSkipped/Incomplete, and the failed-outcome budget itself), and for the
      known false-positive class (a test that deliberately catches its own assertion
      failure) together with the reasoning for providing no opt-out.
      scripts/assertion-escape-check.php turns a report into a non-zero exit for CI.

      DO NOT DELETE THE LINE BELOW. Removing this registration is silent: the guard
      loads nothing, writes no report, and the CI check script exits 0 on a run full of
      swallowed assertions. tests/Unit/Support/AssertionEscapeGuardWiringTest.php fails
      if it goes missing — or is commented out, which it checks against the parsed
      document rather than the file's text.
    -->
    <bootstrap class="Phlix\Tests\Support\AssertionEscape\AssertionEscapeGuardExtension"/>
    <!--
      S439 (finish-S167) — zero-residue census: snapshots /tmp/phlix_* before execution
      and fails the run (named STDERR report + thrown subscriber warning, exit 1 via
      failOnPhpunitWarning's true default) if the suite leaves any behind. Complements
      TempDirMintGuardTest, which guards only the src-side logger mint sites and is
      blind to test fixtures by construction. DO NOT DELETE THE LINE BELOW — its removal
      would be silent; tests/Unit/Media/ZeroResidueCensusTest.php pins this wiring.
    -->
    <bootstrap class="Phlix\Tests\Support\ResidueCensus\ZeroResidueCensusExtension"/>
  </extensions>
  <php>
    <env name="APP_ENV" value="testing"/>
    <env name="DB_HOST" value="127.0.0.1"/>
    <env name="DB_PORT" value="3306"/>
    <env name="DB_DATABASE" value="phlix_test"/>
    <env name="DB_USER" value="root"/>
    <env name="DB_PASSWORD" value="root"/>
  </php>
  <source>
    <include>
      <directory suffix=".php">src</directory>
    </include>
    <exclude>
      <directory suffix=".php">src/vendor</directory>
    </exclude>
  </source>
</phpunit>