Una ejecución real

Un cambio real en Spectre.Console (commit 01027ef), del mapa de cobertura al job summary. Toda la salida de esta página está copiada de esa ejecución, no escrita a mano.

El cambio

Una refactorización de una línea en el widget Rule. No cambia ningún comportamiento, pero es un cambio de código, así que un pipeline normal ejecutaría las 787 pruebas de 141 clases en cada framework de destino.

// src/Spectre.Console/Widgets/Rule.cs, GetLineWithoutTitle
 var text = border.GetPart(BoxBorderPart.Top).Repeat(maxWidth);
+var style = Style ?? Spectre.Console.Style.Plain;

 return
 [
-    new Segment(text, Style ?? Spectre.Console.Style.Plain),
+    new Segment(text, style),
     Segment.LineBreak
 ];

La ejecución

El mapa se registró en main con td record. Después, la pull request ejecuta:

$ td test --base main -- -f net10.0td: 1 changed file(s), 1 affected test project(s).Tests to run:  filtered src/Spectre.Console.Tests/Spectre.Console.Tests.csproj    (14 of 141 test class(es) run or reference the changed code, 0.6 s of 3.0 s (80% saved))Recorded test time: 0.6 s of 3.4 s (83% saved).Passed!  - Failed:     0, Passed:   156, Skipped:     0, Total:   156 - Spectre.Console.Tests.dll (net10.0)td summary:  passed  src/Spectre.Console.Tests/Spectre.Console.Tests.csproj  (14 class(es), 4.6 s)1 project(s) run, 0 failed, 0 skipped.Recorded test time: 0.6 s of 3.4 s (83% saved).

Se ejecutaron 156 pruebas de 14 clases en lugar de 787 de 141. El proyecto Spectre.Console.Ansi.Tests ni siquiera se compiló: no cambió nada de lo que depende.

¿Eran esas las 14 correctas?

Para comprobarlo, hicimos que GetLineWithoutTitle lanzara una excepción y ejecutamos el conjunto completo de pruebas. Fallaron exactamente 14 clases, y son las 14 que TestDetta seleccionó: AlignTests+Center, +Left, +Right, CanvasTests, ColumnsTests, ExceptionTests, LayoutTests, PadderTests, PanelTests, RecorderTests, RowsTests, RuleTests, TableTests y TextPathTests. La mayoría nunca menciona Rule: llegan a él a través de layouts y paneles, que es lo que conoce el mapa de cobertura y no encontraría una búsqueda por nombre.

El job summary

Con --summary-file "$GITHUB_STEP_SUMMARY" (la GitHub Action lo define por ti), cada pull request recibe este resumen: qué se ejecutó, por qué se ejecutó cada clase y qué se omitió.

TestDetta test selection

1 changed file(s) compared with main.

  • 1 of 1 affected test project(s) ran, 0 skipped.
  • Recorded test time: 0.6 s of 3.4 s (83% saved).
  • All selected tests passed.
  • src/Spectre.Console.Tests/Spectre.Console.Tests.csproj: passed, 14 test class(es), recorded time 0.6 s of 3.0 s (80% saved)
    • why: 14 of 141 test class(es) run or reference the changed code
    • classes:
      • AlignTests+Center: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • AlignTests+Left: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • AlignTests+Right: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • CanvasTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • ColumnsTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • ExceptionTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • LayoutTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • PadderTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • PanelTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • RecorderTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • RowsTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • RuleTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • TableTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
      • TextPathTests: ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)
    • skipped: 127 class(es) that ran none of the changed code and name none of it

Aquí los nombres de clase están abreviados; el resumen los muestra completos (Spectre.Console.Tests.Unit.RuleTests).

Preguntar por qué

Cada decisión tiene un motivo que puedes leer. -v escribe el rastro en stderr:

[DBG] CodeChangeDetector: src/Spectre.Console/Widgets/Rule.cs: Modified Spectre.Console.Rule GetLineWithoutTitle(RenderOptions,int)
[DBG] CodeChangeDetector: src/Spectre.Console/Widgets/Rule.cs: 1 changed symbol(s), affects whole project: False
[DBG] TestSelector: Spectre.Console.Rule GetLineWithoutTitle(RenderOptions,int) is affected because it changed
[DBG] TestSelector: Spectre.Console.Rule Render(RenderOptions,int) is affected because GetLineWithoutTitle
[DBG] TestSelector: Spectre.Console.Rule GetLineWithoutTitle(RenderOptions,int): test classes taken from coverage

Y --format json da la decisión de cada clase, con sus evidencias y una estimación de tiempo, para scripts y paneles:

{
  "project": "src/Spectre.Console.Tests/Spectre.Console.Tests.csproj",
  "mode": "Filtered",
  "reason": "14 of 141 test class(es) run or reference the changed code",
  "estimate": {
    "fullSeconds": 3,
    "selectedSeconds": 0.6,
    "savedSeconds": 2.4
  },
  "decisions": [
    {
      "class": "Spectre.Console.Tests.Unit.AlignTests+Center",
      "selected": true,
      "kind": "coverage",
      "evidence": "ran Spectre.Console.Rule.GetLineWithoutTitle(RenderOptions,int), which changed (map 01027efcd6c4)"
    },
    {
      "class": "Spectre.Console.Tests.PublicApiTests",
      "selected": false,
      "kind": "skipped",
      "evidence": "ran none of the changed code and names none of it (map 01027efcd6c4)"
    },
    "…139 more"
  ]
}

Ejecutar las pruebas a tu manera

td affected --format commands muestra la llamada a dotnet test que haría TestDetta, para que puedas ejecutarla en tu propio script:

dotnet test src/Spectre.Console.Tests/Spectre.Console.Tests.csproj --filter 'FullyQualifiedName~Spectre.Console.Tests.Unit.AlignTests+Center.|FullyQualifiedName~Spectre.Console.Tests.Unit.AlignTests+Left.|FullyQualifiedName~Spectre.Console.Tests.Unit.AlignTests+Right.|FullyQualifiedName~Spectre.Console.Tests.Unit.CanvasTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.ColumnsTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.ExceptionTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.LayoutTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.PadderTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.PanelTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.RecorderTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.RowsTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.RuleTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.TableTests.|FullyQualifiedName~Spectre.Console.Tests.Unit.TextPathTests.'

En 40 repositorios

Un cambio es un solo dato. En el corpus de evaluación se cambian, uno a uno, miembros de 40 bibliotecas de código abierto; para un cambio en un solo miembro, TestDetta ejecutó una mediana del 28 % de las clases de prueba, y una cuarta parte o menos en 18 de los 40 repositorios. Las bibliotecas en las que casi todas las pruebas tocan el núcleo (Scriban, Markdig) ganan poco; el código organizado en capas es el que más gana. Cada clase que falló se comprobó frente a la selección: se pasaron por alto 3 de 2,941 cambios con fallos, y cada una se corrigió.

Porcentaje de clases de prueba ejecutadas por cambio, por repositorio Ronda 1 del corpus de evaluación: para cada uno de los 40 repositorios, el porcentaje medio de sus clases de prueba que TestDetta seleccionó para un cambio en un solo miembro. Mediana: 28 %. 0% 25% 50% 75% 100% opentelemetry-dotnet: 6 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosopentelemetry-dotnet6% polly: 7 % de las clases de prueba ejecutadas de media, sobre 65 cambios con fallospolly7% roslynator: 8 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosroslynator8% spectre: 9 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosspectre9% swashbuckle: 12 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosswashbuckle12% nsec: 13 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosnsec13% aspnetcore-odata: 13 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosaspnetcore-odata13% guardclauses: 13 % de las clases de prueba ejecutadas de media, sobre 24 cambios con fallosguardclauses13% mimekit: 14 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosmimekit14% autofixture: 16 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosautofixture16% communitytoolkit: 18 % de las clases de prueba ejecutadas de media, sobre 76 cambios con falloscommunitytoolkit18% libgit2sharp: 19 % de las clases de prueba ejecutadas de media, sobre 57 cambios con falloslibgit2sharp19% castle-core: 19 % de las clases de prueba ejecutadas de media, sobre 80 cambios con falloscastle-core19% serilog: 20 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosserilog20% mediator: 21 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosmediator21% fastendpoints: 23 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosfastendpoints23% fluid: 23 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosfluid23% carter: 24 % de las clases de prueba ejecutadas de media, sobre 80 cambios con falloscarter24% lamar: 26 % de las clases de prueba ejecutadas de media, sobre 80 cambios con falloslamar26% awesomeassertions: 27 % de las clases de prueba ejecutadas de media, sobre 81 cambios con fallosawesomeassertions27% nsubstitute: 29 % de las clases de prueba ejecutadas de media, sobre 3 cambios con fallosnsubstitute29% immediate-handlers: 29 % de las clases de prueba ejecutadas de media, sobre 43 cambios con fallosimmediate-handlers29% open-xml-sdk: 30 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosopen-xml-sdk30% fluentvalidation: 32 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosfluentvalidation32% unitsnet: 34 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosunitsnet34% vogen: 36 % de las clases de prueba ejecutadas de media, sobre 77 cambios con fallosvogen36% nodatime: 43 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosnodatime43% graphql-dotnet: 45 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosgraphql-dotnet45% newtonsoft-json: 47 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosnewtonsoft-json47% autofac: 49 % de las clases de prueba ejecutadas de media, sobre 77 cambios con fallosautofac49% scrutor: 57 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosscrutor57% meziantou-analyzer: 63 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosmeziantou-analyzer63% automapper: 67 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosautomapper67% protobuf-net: 78 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosprotobuf-net78% yamldotnet: 78 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosyamldotnet78% cliwrap: 81 % de las clases de prueba ejecutadas de media, sobre 69 cambios con falloscliwrap81% nlog: 82 % de las clases de prueba ejecutadas de media, sobre 65 cambios con fallosnlog82% mapperly: 89 % de las clases de prueba ejecutadas de media, sobre 79 cambios con fallosmapperly89% markdig: 98 % de las clases de prueba ejecutadas de media, sobre 80 cambios con fallosmarkdig98% scriban: 100 % de las clases de prueba ejecutadas de media, sobre 71 cambios con fallosscriban100%
Porcentaje medio de clases de prueba ejecutadas por cambio, ronda de evaluación 1. Pasa el cursor sobre una fila para ver el número de cambios en que se basa.