You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2022/03/23 03:51:37 UTC

[pulsar-test-infra] branch master updated (76c227d -> 89acb76)

This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-test-infra.git.


    from 76c227d  [Docbot]Fix problems in docbot scripts (#26)
     new b9df410  Add copy of cirruslabs/http-cache-action@master action
     new 89acb76  Add copy of dorny/test-reporter@v1 action

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 http-cache-action/.github/workflows/tests.yml      |    30 +
 http-cache-action/.gitignore                       |     2 +
 http-cache-action/LICENSE                          |    21 +
 http-cache-action/README.md                        |    74 +
 http-cache-action/action.yml                       |    14 +
 http-cache-action/dist/index.js                    |  3010 ++
 http-cache-action/dist/package.json                |     3 +
 http-cache-action/package-lock.json                |  1809 +
 http-cache-action/package.json                     |    47 +
 http-cache-action/proxy/Dockerfile                 |    17 +
 http-cache-action/proxy/go.mod                     |     5 +
 http-cache-action/proxy/go.sum                     |     2 +
 http-cache-action/proxy/proxy.go                   |   266 +
 http-cache-action/proxy/proxy_test.go              |    62 +
 http-cache-action/src/action.ts                    |    23 +
 http-cache-action/tsconfig.json                    |     7 +
 test-reporter/.editorconfig                        |    10 +
 {paths-filter => test-reporter}/.eslintignore      |     0
 test-reporter/.eslintrc.json                       |    57 +
 test-reporter/.gitattributes                       |     2 +
 test-reporter/.github/workflows/ci.yml             |    36 +
 test-reporter/.github/workflows/test-report.yml    |    20 +
 test-reporter/.gitignore                           |   102 +
 test-reporter/.prettierignore                      |     5 +
 test-reporter/.prettierrc.json                     |    10 +
 test-reporter/.vscode/launch.json                  |    35 +
 test-reporter/CHANGELOG.md                         |    44 +
 test-reporter/LICENSE                              |    22 +
 test-reporter/README.md                            |   321 +
 test-reporter/__tests__/__outputs__/dart-json.md   |    28 +
 test-reporter/__tests__/__outputs__/dotnet-trx.md  |    31 +
 .../__outputs__/fluent-validation-test-results.md  |  1057 +
 test-reporter/__tests__/__outputs__/jest-junit.md  |    26 +
 .../__tests__/__outputs__/jest-test-results.md     |   432 +
 test-reporter/__tests__/__outputs__/mocha-json.md  |    29 +
 .../__tests__/__outputs__/mocha-test-results.md    |    41 +
 .../__tests__/__outputs__/provider-test-results.md |   374 +
 .../__outputs__/pulsar-test-results-no-merge.md    |    12 +
 .../__tests__/__outputs__/pulsar-test-results.md   |  1518 +
 .../__outputs__/silent-notes-test-results.md       |   136 +
 .../__tests__/__snapshots__/dart-json.test.ts.snap |  2052 ++
 .../__snapshots__/dotnet-trx.test.ts.snap          |  6271 ++++
 .../__snapshots__/java-junit.test.ts.snap          |  6880 ++++
 .../__snapshots__/jest-junit.test.ts.snap          | 32220 ++++++++++++++++++
 .../__snapshots__/mocha-json.test.ts.snap          |  7587 +++++
 test-reporter/__tests__/dart-json.test.ts          |    69 +
 test-reporter/__tests__/dotnet-trx.test.ts         |    86 +
 test-reporter/__tests__/fixtures/dart-json.json    |    32 +
 test-reporter/__tests__/fixtures/dotnet-trx.trx    |   184 +
 .../__tests__/fixtures/empty/dart-json.json        |     9 +
 .../__tests__/fixtures/empty/dotnet-trx.trx        |    19 +
 .../__tests__/fixtures/empty/java-junit.xml        |     3 +
 .../__tests__/fixtures/empty/jest-junit.xml        |     3 +
 .../__tests__/fixtures/empty/mocha-json.json       |    16 +
 .../fixtures/external/FluentValidation.Tests.trx   |  4662 +++
 .../__tests__/fixtures/external/SilentNotes.trx    |   609 +
 .../__tests__/fixtures/external/flutter/files.txt  |    57 +
 .../external/flutter/provider-test-results.json    |   637 +
 ...rg.apache.pulsar.AddMissingPatchVersionTest.xml |    34 +
 .../__tests__/fixtures/external/java/files.txt     |  5873 ++++
 .../fixtures/external/java/pulsar-test-report.xml  |   862 +
 .../__tests__/fixtures/external/jest/files.txt     |  2317 ++
 .../fixtures/external/jest/jest-test-results.xml   |  9357 ++++++
 .../__tests__/fixtures/external/mocha/files.txt    |   516 +
 .../external/mocha/mocha-test-results.json         | 15097 +++++++++
 test-reporter/__tests__/fixtures/jest-junit.xml    |    61 +
 test-reporter/__tests__/fixtures/mocha-json.json   |   158 +
 test-reporter/__tests__/java-junit.test.ts         |    75 +
 test-reporter/__tests__/jest-junit.test.ts         |    69 +
 test-reporter/__tests__/mocha-json.test.ts         |    67 +
 test-reporter/__tests__/utils/parse-utils.test.ts  |    37 +
 test-reporter/action.yml                           |    93 +
 test-reporter/assets/fluent-validation-report.png  |   Bin 0 -> 58858 bytes
 test-reporter/assets/mocha-groups.png              |   Bin 0 -> 42308 bytes
 test-reporter/assets/provider-error-details.png    |   Bin 0 -> 53949 bytes
 test-reporter/assets/provider-error-summary.png    |   Bin 0 -> 67397 bytes
 test-reporter/dist/index.js                        | 32687 +++++++++++++++++++
 test-reporter/dist/index.js.map                    |     1 +
 test-reporter/dist/licenses.txt                    |  1532 +
 test-reporter/dist/sourcemap-register.js           |     1 +
 {paths-filter => test-reporter}/jest.config.js     |     0
 test-reporter/package-lock.json                    | 11080 +++++++
 test-reporter/package.json                         |    75 +
 test-reporter/reports/dart/.gitignore              |     9 +
 test-reporter/reports/dart/analysis_options.yaml   |    14 +
 test-reporter/reports/dart/lib/main.dart           |     3 +
 test-reporter/reports/dart/pubspec.lock            |   348 +
 test-reporter/reports/dart/pubspec.yaml            |     9 +
 test-reporter/reports/dart/test/main_test.dart     |    29 +
 test-reporter/reports/dart/test/second_test.dart   |    12 +
 test-reporter/reports/dotnet/.gitignore            |   362 +
 .../reports/dotnet/DotnetTests.Unit/Calculator.cs  |    11 +
 .../DotnetTests.Unit/DotnetTests.Unit.csproj       |     7 +
 .../DotnetTests.XUnitTests/CalculatorTests.cs      |    70 +
 .../DotnetTests.XUnitTests.csproj                  |    20 +
 test-reporter/reports/dotnet/DotnetTests.sln       |    36 +
 test-reporter/reports/jest/__tests__/main.test.js  |    23 +
 .../reports/jest/__tests__/second.test.js          |     7 +
 test-reporter/reports/jest/lib/main.js             |     5 +
 test-reporter/reports/jest/package-lock.json       |  4728 +++
 test-reporter/reports/jest/package.json            |    25 +
 test-reporter/reports/mocha/lib/main.js            |     5 +
 test-reporter/reports/mocha/package-lock.json      |   761 +
 test-reporter/reports/mocha/package.json           |    14 +
 test-reporter/reports/mocha/test/main.test.js      |    24 +
 test-reporter/reports/mocha/test/second.test.js    |     8 +
 .../src/input-providers/artifact-provider.ts       |   108 +
 .../src/input-providers/input-provider.ts          |    13 +
 .../src/input-providers/local-file-provider.ts     |    25 +
 test-reporter/src/main.ts                          |   219 +
 .../src/parsers/dart-json/dart-json-parser.ts      |   251 +
 .../src/parsers/dart-json/dart-json-types.ts       |   132 +
 .../src/parsers/dotnet-trx/dotnet-trx-parser.ts    |   196 +
 .../src/parsers/dotnet-trx/dotnet-trx-types.ts     |    60 +
 .../src/parsers/java-junit/java-junit-parser.ts    |   206 +
 .../src/parsers/java-junit/java-junit-types.ts     |    45 +
 .../src/parsers/jest-junit/jest-junit-parser.ts    |   117 +
 .../src/parsers/jest-junit/jest-junit-types.ts     |    34 +
 .../src/parsers/mocha-json/mocha-json-parser.ts    |   118 +
 .../src/parsers/mocha-json/mocha-json-types.ts     |    23 +
 test-reporter/src/report/get-annotations.ts        |   107 +
 test-reporter/src/report/get-report.ts             |   272 +
 test-reporter/src/test-parser.ts                   |    11 +
 test-reporter/src/test-results.ts                  |   123 +
 .../src => test-reporter/src/utils}/exec.ts        |     0
 test-reporter/src/utils/git.ts                     |    22 +
 test-reporter/src/utils/github-utils.ts            |   130 +
 test-reporter/src/utils/markdown-utils.ts          |    48 +
 test-reporter/src/utils/node-utils.ts              |    30 +
 test-reporter/src/utils/parse-utils.ts             |    24 +
 test-reporter/src/utils/path-utils.ts              |    39 +
 test-reporter/src/utils/slugger.ts                 |    14 +
 test-reporter/tsconfig.json                        |    13 +
 133 files changed, 160038 insertions(+)
 create mode 100644 http-cache-action/.github/workflows/tests.yml
 create mode 100644 http-cache-action/.gitignore
 create mode 100644 http-cache-action/LICENSE
 create mode 100644 http-cache-action/README.md
 create mode 100644 http-cache-action/action.yml
 create mode 100644 http-cache-action/dist/index.js
 create mode 100644 http-cache-action/dist/package.json
 create mode 100644 http-cache-action/package-lock.json
 create mode 100644 http-cache-action/package.json
 create mode 100644 http-cache-action/proxy/Dockerfile
 create mode 100644 http-cache-action/proxy/go.mod
 create mode 100644 http-cache-action/proxy/go.sum
 create mode 100644 http-cache-action/proxy/proxy.go
 create mode 100644 http-cache-action/proxy/proxy_test.go
 create mode 100644 http-cache-action/src/action.ts
 create mode 100644 http-cache-action/tsconfig.json
 create mode 100644 test-reporter/.editorconfig
 copy {paths-filter => test-reporter}/.eslintignore (100%)
 create mode 100644 test-reporter/.eslintrc.json
 create mode 100644 test-reporter/.gitattributes
 create mode 100644 test-reporter/.github/workflows/ci.yml
 create mode 100644 test-reporter/.github/workflows/test-report.yml
 create mode 100644 test-reporter/.gitignore
 create mode 100644 test-reporter/.prettierignore
 create mode 100644 test-reporter/.prettierrc.json
 create mode 100644 test-reporter/.vscode/launch.json
 create mode 100644 test-reporter/CHANGELOG.md
 create mode 100644 test-reporter/LICENSE
 create mode 100644 test-reporter/README.md
 create mode 100644 test-reporter/__tests__/__outputs__/dart-json.md
 create mode 100644 test-reporter/__tests__/__outputs__/dotnet-trx.md
 create mode 100644 test-reporter/__tests__/__outputs__/fluent-validation-test-results.md
 create mode 100644 test-reporter/__tests__/__outputs__/jest-junit.md
 create mode 100644 test-reporter/__tests__/__outputs__/jest-test-results.md
 create mode 100644 test-reporter/__tests__/__outputs__/mocha-json.md
 create mode 100644 test-reporter/__tests__/__outputs__/mocha-test-results.md
 create mode 100644 test-reporter/__tests__/__outputs__/provider-test-results.md
 create mode 100644 test-reporter/__tests__/__outputs__/pulsar-test-results-no-merge.md
 create mode 100644 test-reporter/__tests__/__outputs__/pulsar-test-results.md
 create mode 100644 test-reporter/__tests__/__outputs__/silent-notes-test-results.md
 create mode 100644 test-reporter/__tests__/__snapshots__/dart-json.test.ts.snap
 create mode 100644 test-reporter/__tests__/__snapshots__/dotnet-trx.test.ts.snap
 create mode 100644 test-reporter/__tests__/__snapshots__/java-junit.test.ts.snap
 create mode 100644 test-reporter/__tests__/__snapshots__/jest-junit.test.ts.snap
 create mode 100644 test-reporter/__tests__/__snapshots__/mocha-json.test.ts.snap
 create mode 100644 test-reporter/__tests__/dart-json.test.ts
 create mode 100644 test-reporter/__tests__/dotnet-trx.test.ts
 create mode 100644 test-reporter/__tests__/fixtures/dart-json.json
 create mode 100644 test-reporter/__tests__/fixtures/dotnet-trx.trx
 create mode 100644 test-reporter/__tests__/fixtures/empty/dart-json.json
 create mode 100644 test-reporter/__tests__/fixtures/empty/dotnet-trx.trx
 create mode 100644 test-reporter/__tests__/fixtures/empty/java-junit.xml
 create mode 100644 test-reporter/__tests__/fixtures/empty/jest-junit.xml
 create mode 100644 test-reporter/__tests__/fixtures/empty/mocha-json.json
 create mode 100644 test-reporter/__tests__/fixtures/external/FluentValidation.Tests.trx
 create mode 100644 test-reporter/__tests__/fixtures/external/SilentNotes.trx
 create mode 100644 test-reporter/__tests__/fixtures/external/flutter/files.txt
 create mode 100644 test-reporter/__tests__/fixtures/external/flutter/provider-test-results.json
 create mode 100644 test-reporter/__tests__/fixtures/external/java/TEST-org.apache.pulsar.AddMissingPatchVersionTest.xml
 create mode 100644 test-reporter/__tests__/fixtures/external/java/files.txt
 create mode 100644 test-reporter/__tests__/fixtures/external/java/pulsar-test-report.xml
 create mode 100644 test-reporter/__tests__/fixtures/external/jest/files.txt
 create mode 100644 test-reporter/__tests__/fixtures/external/jest/jest-test-results.xml
 create mode 100644 test-reporter/__tests__/fixtures/external/mocha/files.txt
 create mode 100644 test-reporter/__tests__/fixtures/external/mocha/mocha-test-results.json
 create mode 100644 test-reporter/__tests__/fixtures/jest-junit.xml
 create mode 100644 test-reporter/__tests__/fixtures/mocha-json.json
 create mode 100644 test-reporter/__tests__/java-junit.test.ts
 create mode 100644 test-reporter/__tests__/jest-junit.test.ts
 create mode 100644 test-reporter/__tests__/mocha-json.test.ts
 create mode 100644 test-reporter/__tests__/utils/parse-utils.test.ts
 create mode 100644 test-reporter/action.yml
 create mode 100644 test-reporter/assets/fluent-validation-report.png
 create mode 100644 test-reporter/assets/mocha-groups.png
 create mode 100644 test-reporter/assets/provider-error-details.png
 create mode 100644 test-reporter/assets/provider-error-summary.png
 create mode 100644 test-reporter/dist/index.js
 create mode 100644 test-reporter/dist/index.js.map
 create mode 100644 test-reporter/dist/licenses.txt
 create mode 100644 test-reporter/dist/sourcemap-register.js
 copy {paths-filter => test-reporter}/jest.config.js (100%)
 create mode 100644 test-reporter/package-lock.json
 create mode 100644 test-reporter/package.json
 create mode 100644 test-reporter/reports/dart/.gitignore
 create mode 100644 test-reporter/reports/dart/analysis_options.yaml
 create mode 100644 test-reporter/reports/dart/lib/main.dart
 create mode 100644 test-reporter/reports/dart/pubspec.lock
 create mode 100644 test-reporter/reports/dart/pubspec.yaml
 create mode 100644 test-reporter/reports/dart/test/main_test.dart
 create mode 100644 test-reporter/reports/dart/test/second_test.dart
 create mode 100644 test-reporter/reports/dotnet/.gitignore
 create mode 100644 test-reporter/reports/dotnet/DotnetTests.Unit/Calculator.cs
 create mode 100644 test-reporter/reports/dotnet/DotnetTests.Unit/DotnetTests.Unit.csproj
 create mode 100644 test-reporter/reports/dotnet/DotnetTests.XUnitTests/CalculatorTests.cs
 create mode 100644 test-reporter/reports/dotnet/DotnetTests.XUnitTests/DotnetTests.XUnitTests.csproj
 create mode 100644 test-reporter/reports/dotnet/DotnetTests.sln
 create mode 100644 test-reporter/reports/jest/__tests__/main.test.js
 create mode 100644 test-reporter/reports/jest/__tests__/second.test.js
 create mode 100644 test-reporter/reports/jest/lib/main.js
 create mode 100644 test-reporter/reports/jest/package-lock.json
 create mode 100644 test-reporter/reports/jest/package.json
 create mode 100644 test-reporter/reports/mocha/lib/main.js
 create mode 100644 test-reporter/reports/mocha/package-lock.json
 create mode 100644 test-reporter/reports/mocha/package.json
 create mode 100644 test-reporter/reports/mocha/test/main.test.js
 create mode 100644 test-reporter/reports/mocha/test/second.test.js
 create mode 100644 test-reporter/src/input-providers/artifact-provider.ts
 create mode 100644 test-reporter/src/input-providers/input-provider.ts
 create mode 100644 test-reporter/src/input-providers/local-file-provider.ts
 create mode 100644 test-reporter/src/main.ts
 create mode 100644 test-reporter/src/parsers/dart-json/dart-json-parser.ts
 create mode 100644 test-reporter/src/parsers/dart-json/dart-json-types.ts
 create mode 100644 test-reporter/src/parsers/dotnet-trx/dotnet-trx-parser.ts
 create mode 100644 test-reporter/src/parsers/dotnet-trx/dotnet-trx-types.ts
 create mode 100644 test-reporter/src/parsers/java-junit/java-junit-parser.ts
 create mode 100644 test-reporter/src/parsers/java-junit/java-junit-types.ts
 create mode 100644 test-reporter/src/parsers/jest-junit/jest-junit-parser.ts
 create mode 100644 test-reporter/src/parsers/jest-junit/jest-junit-types.ts
 create mode 100644 test-reporter/src/parsers/mocha-json/mocha-json-parser.ts
 create mode 100644 test-reporter/src/parsers/mocha-json/mocha-json-types.ts
 create mode 100644 test-reporter/src/report/get-annotations.ts
 create mode 100644 test-reporter/src/report/get-report.ts
 create mode 100644 test-reporter/src/test-parser.ts
 create mode 100644 test-reporter/src/test-results.ts
 copy {paths-filter/src => test-reporter/src/utils}/exec.ts (100%)
 create mode 100644 test-reporter/src/utils/git.ts
 create mode 100644 test-reporter/src/utils/github-utils.ts
 create mode 100644 test-reporter/src/utils/markdown-utils.ts
 create mode 100644 test-reporter/src/utils/node-utils.ts
 create mode 100644 test-reporter/src/utils/parse-utils.ts
 create mode 100644 test-reporter/src/utils/path-utils.ts
 create mode 100644 test-reporter/src/utils/slugger.ts
 create mode 100644 test-reporter/tsconfig.json

[pulsar-test-infra] 02/02: Add copy of dorny/test-reporter@v1 action

Posted by lh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-test-infra.git

commit 89acb76689a5432a1213532026f00584946284bb
Author: Lari Hotari <la...@hotari.net>
AuthorDate: Wed Mar 23 05:48:19 2022 +0200

    Add copy of dorny/test-reporter@v1 action
    
    from https://github.com/dorny/test-reporter hash 0d00bb1
---
 test-reporter/.editorconfig                        |    10 +
 test-reporter/.eslintignore                        |     3 +
 test-reporter/.eslintrc.json                       |    57 +
 test-reporter/.gitattributes                       |     2 +
 test-reporter/.github/workflows/ci.yml             |    36 +
 test-reporter/.github/workflows/test-report.yml    |    20 +
 test-reporter/.gitignore                           |   102 +
 test-reporter/.prettierignore                      |     5 +
 test-reporter/.prettierrc.json                     |    10 +
 test-reporter/.vscode/launch.json                  |    35 +
 test-reporter/CHANGELOG.md                         |    44 +
 test-reporter/LICENSE                              |    22 +
 test-reporter/README.md                            |   321 +
 test-reporter/__tests__/__outputs__/dart-json.md   |    28 +
 test-reporter/__tests__/__outputs__/dotnet-trx.md  |    31 +
 .../__outputs__/fluent-validation-test-results.md  |  1057 +
 test-reporter/__tests__/__outputs__/jest-junit.md  |    26 +
 .../__tests__/__outputs__/jest-test-results.md     |   432 +
 test-reporter/__tests__/__outputs__/mocha-json.md  |    29 +
 .../__tests__/__outputs__/mocha-test-results.md    |    41 +
 .../__tests__/__outputs__/provider-test-results.md |   374 +
 .../__outputs__/pulsar-test-results-no-merge.md    |    12 +
 .../__tests__/__outputs__/pulsar-test-results.md   |  1518 +
 .../__outputs__/silent-notes-test-results.md       |   136 +
 .../__tests__/__snapshots__/dart-json.test.ts.snap |  2052 ++
 .../__snapshots__/dotnet-trx.test.ts.snap          |  6271 ++++
 .../__snapshots__/java-junit.test.ts.snap          |  6880 ++++
 .../__snapshots__/jest-junit.test.ts.snap          | 32220 ++++++++++++++++++
 .../__snapshots__/mocha-json.test.ts.snap          |  7587 +++++
 test-reporter/__tests__/dart-json.test.ts          |    69 +
 test-reporter/__tests__/dotnet-trx.test.ts         |    86 +
 test-reporter/__tests__/fixtures/dart-json.json    |    32 +
 test-reporter/__tests__/fixtures/dotnet-trx.trx    |   184 +
 .../__tests__/fixtures/empty/dart-json.json        |     9 +
 .../__tests__/fixtures/empty/dotnet-trx.trx        |    19 +
 .../__tests__/fixtures/empty/java-junit.xml        |     3 +
 .../__tests__/fixtures/empty/jest-junit.xml        |     3 +
 .../__tests__/fixtures/empty/mocha-json.json       |    16 +
 .../fixtures/external/FluentValidation.Tests.trx   |  4662 +++
 .../__tests__/fixtures/external/SilentNotes.trx    |   609 +
 .../__tests__/fixtures/external/flutter/files.txt  |    57 +
 .../external/flutter/provider-test-results.json    |   637 +
 ...rg.apache.pulsar.AddMissingPatchVersionTest.xml |    34 +
 .../__tests__/fixtures/external/java/files.txt     |  5873 ++++
 .../fixtures/external/java/pulsar-test-report.xml  |   862 +
 .../__tests__/fixtures/external/jest/files.txt     |  2317 ++
 .../fixtures/external/jest/jest-test-results.xml   |  9357 ++++++
 .../__tests__/fixtures/external/mocha/files.txt    |   516 +
 .../external/mocha/mocha-test-results.json         | 15097 +++++++++
 test-reporter/__tests__/fixtures/jest-junit.xml    |    61 +
 test-reporter/__tests__/fixtures/mocha-json.json   |   158 +
 test-reporter/__tests__/java-junit.test.ts         |    75 +
 test-reporter/__tests__/jest-junit.test.ts         |    69 +
 test-reporter/__tests__/mocha-json.test.ts         |    67 +
 test-reporter/__tests__/utils/parse-utils.test.ts  |    37 +
 test-reporter/action.yml                           |    93 +
 test-reporter/assets/fluent-validation-report.png  |   Bin 0 -> 58858 bytes
 test-reporter/assets/mocha-groups.png              |   Bin 0 -> 42308 bytes
 test-reporter/assets/provider-error-details.png    |   Bin 0 -> 53949 bytes
 test-reporter/assets/provider-error-summary.png    |   Bin 0 -> 67397 bytes
 test-reporter/dist/index.js                        | 32687 +++++++++++++++++++
 test-reporter/dist/index.js.map                    |     1 +
 test-reporter/dist/licenses.txt                    |  1532 +
 test-reporter/dist/sourcemap-register.js           |     1 +
 test-reporter/jest.config.js                       |    11 +
 test-reporter/package-lock.json                    | 11080 +++++++
 test-reporter/package.json                         |    75 +
 test-reporter/reports/dart/.gitignore              |     9 +
 test-reporter/reports/dart/analysis_options.yaml   |    14 +
 test-reporter/reports/dart/lib/main.dart           |     3 +
 test-reporter/reports/dart/pubspec.lock            |   348 +
 test-reporter/reports/dart/pubspec.yaml            |     9 +
 test-reporter/reports/dart/test/main_test.dart     |    29 +
 test-reporter/reports/dart/test/second_test.dart   |    12 +
 test-reporter/reports/dotnet/.gitignore            |   362 +
 .../reports/dotnet/DotnetTests.Unit/Calculator.cs  |    11 +
 .../DotnetTests.Unit/DotnetTests.Unit.csproj       |     7 +
 .../DotnetTests.XUnitTests/CalculatorTests.cs      |    70 +
 .../DotnetTests.XUnitTests.csproj                  |    20 +
 test-reporter/reports/dotnet/DotnetTests.sln       |    36 +
 test-reporter/reports/jest/__tests__/main.test.js  |    23 +
 .../reports/jest/__tests__/second.test.js          |     7 +
 test-reporter/reports/jest/lib/main.js             |     5 +
 test-reporter/reports/jest/package-lock.json       |  4728 +++
 test-reporter/reports/jest/package.json            |    25 +
 test-reporter/reports/mocha/lib/main.js            |     5 +
 test-reporter/reports/mocha/package-lock.json      |   761 +
 test-reporter/reports/mocha/package.json           |    14 +
 test-reporter/reports/mocha/test/main.test.js      |    24 +
 test-reporter/reports/mocha/test/second.test.js    |     8 +
 .../src/input-providers/artifact-provider.ts       |   108 +
 .../src/input-providers/input-provider.ts          |    13 +
 .../src/input-providers/local-file-provider.ts     |    25 +
 test-reporter/src/main.ts                          |   219 +
 .../src/parsers/dart-json/dart-json-parser.ts      |   251 +
 .../src/parsers/dart-json/dart-json-types.ts       |   132 +
 .../src/parsers/dotnet-trx/dotnet-trx-parser.ts    |   196 +
 .../src/parsers/dotnet-trx/dotnet-trx-types.ts     |    60 +
 .../src/parsers/java-junit/java-junit-parser.ts    |   206 +
 .../src/parsers/java-junit/java-junit-types.ts     |    45 +
 .../src/parsers/jest-junit/jest-junit-parser.ts    |   117 +
 .../src/parsers/jest-junit/jest-junit-types.ts     |    34 +
 .../src/parsers/mocha-json/mocha-json-parser.ts    |   118 +
 .../src/parsers/mocha-json/mocha-json-types.ts     |    23 +
 test-reporter/src/report/get-annotations.ts        |   107 +
 test-reporter/src/report/get-report.ts             |   272 +
 test-reporter/src/test-parser.ts                   |    11 +
 test-reporter/src/test-results.ts                  |   123 +
 test-reporter/src/utils/exec.ts                    |    21 +
 test-reporter/src/utils/git.ts                     |    22 +
 test-reporter/src/utils/github-utils.ts            |   130 +
 test-reporter/src/utils/markdown-utils.ts          |    48 +
 test-reporter/src/utils/node-utils.ts              |    30 +
 test-reporter/src/utils/parse-utils.ts             |    24 +
 test-reporter/src/utils/path-utils.ts              |    39 +
 test-reporter/src/utils/slugger.ts                 |    14 +
 test-reporter/tsconfig.json                        |    13 +
 117 files changed, 154681 insertions(+)

diff --git a/test-reporter/.editorconfig b/test-reporter/.editorconfig
new file mode 100644
index 0000000..ba48812
--- /dev/null
+++ b/test-reporter/.editorconfig
@@ -0,0 +1,10 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+
+[*.cs]
+indent_size = 4
diff --git a/test-reporter/.eslintignore b/test-reporter/.eslintignore
new file mode 100644
index 0000000..2186947
--- /dev/null
+++ b/test-reporter/.eslintignore
@@ -0,0 +1,3 @@
+dist/
+lib/
+node_modules/
\ No newline at end of file
diff --git a/test-reporter/.eslintrc.json b/test-reporter/.eslintrc.json
new file mode 100644
index 0000000..5e93274
--- /dev/null
+++ b/test-reporter/.eslintrc.json
@@ -0,0 +1,57 @@
+{
+    "plugins": ["jest", "@typescript-eslint"],
+    "extends": ["plugin:github/recommended"],
+    "parser": "@typescript-eslint/parser",
+    "parserOptions": {
+      "ecmaVersion": 9,
+      "sourceType": "module",
+      "project": "./tsconfig.json"
+    },
+    "rules": {
+      "camelcase": "off",
+      "eslint-comments/no-use": "off",
+      "import/no-namespace": "off",
+      "no-shadow": "off",
+      "no-unused-vars": "off",
+      "prefer-template": "off",
+      "semi": [ "error", "never"],
+      "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
+      "@typescript-eslint/array-type": "error",
+      "@typescript-eslint/await-thenable": "error",
+      "@typescript-eslint/ban-ts-comment": "error",
+      "@typescript-eslint/consistent-type-assertions": "error",
+      "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
+      "@typescript-eslint/func-call-spacing": ["error", "never"],
+      "@typescript-eslint/no-array-constructor": "error",
+      "@typescript-eslint/no-empty-interface": "error",
+      "@typescript-eslint/no-explicit-any": "error",
+      "@typescript-eslint/no-extraneous-class": "error",
+      "@typescript-eslint/no-for-in-array": "error",
+      "@typescript-eslint/no-inferrable-types": "error",
+      "@typescript-eslint/no-misused-new": "error",
+      "@typescript-eslint/no-namespace": "error",
+      "@typescript-eslint/no-require-imports": "error",
+      "@typescript-eslint/no-shadow": "error",
+      "@typescript-eslint/no-non-null-assertion": "warn",
+      "@typescript-eslint/no-unnecessary-qualifier": "error",
+      "@typescript-eslint/no-unnecessary-type-assertion": "error",
+      "@typescript-eslint/no-unused-vars": ["error", {"varsIgnorePattern": "^_"}],
+      "@typescript-eslint/no-useless-constructor": "error",
+      "@typescript-eslint/no-var-requires": "error",
+      "@typescript-eslint/prefer-for-of": "warn",
+      "@typescript-eslint/prefer-function-type": "warn",
+      "@typescript-eslint/prefer-includes": "error",
+      "@typescript-eslint/prefer-string-starts-ends-with": "error",
+      "@typescript-eslint/promise-function-async": "error",
+      "@typescript-eslint/require-array-sort-compare": "error",
+      "@typescript-eslint/restrict-plus-operands": "error",
+      "@typescript-eslint/semi": ["error", "never"],
+      "@typescript-eslint/type-annotation-spacing": "error",
+      "@typescript-eslint/unbound-method": "error"
+    },
+    "env": {
+      "node": true,
+      "es6": true,
+      "jest/globals": true
+    }
+  }
diff --git a/test-reporter/.gitattributes b/test-reporter/.gitattributes
new file mode 100644
index 0000000..00f4c25
--- /dev/null
+++ b/test-reporter/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto eol=lf
+dist/** -diff linguist-generated=true
diff --git a/test-reporter/.github/workflows/ci.yml b/test-reporter/.github/workflows/ci.yml
new file mode 100644
index 0000000..57144fc
--- /dev/null
+++ b/test-reporter/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: 'CI'
+on:
+  pull_request:
+    paths-ignore: [ '**.md' ]
+  push:
+    paths-ignore: [ '**.md' ]
+    branches:
+      - main
+  workflow_dispatch:
+
+jobs:
+  build-test:
+    name: Build & Test
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - run: npm ci
+      - run: npm run build
+      - run: npm run format-check
+      - run: npm run lint
+      - run: npm test
+
+      - name: Upload test results
+        if: success() || failure()
+        uses: actions/upload-artifact@v2
+        with:
+          name: test-results
+          path: __tests__/__results__/*.xml
+
+      - name: Create test report
+        uses: ./
+        if: success() || failure()
+        with:
+          name: JEST Tests
+          path: __tests__/__results__/*.xml
+          reporter: jest-junit
diff --git a/test-reporter/.github/workflows/test-report.yml b/test-reporter/.github/workflows/test-report.yml
new file mode 100644
index 0000000..1cf8442
--- /dev/null
+++ b/test-reporter/.github/workflows/test-report.yml
@@ -0,0 +1,20 @@
+name: Test Report
+
+on:
+  workflow_run:
+    workflows: ['CI']
+    types:
+      - completed
+
+jobs:
+  report:
+    name: Workflow test
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: ./
+      with:
+        artifact: test-results
+        name: Workflow Report
+        path: '*.xml'
+        reporter: jest-junit
diff --git a/test-reporter/.gitignore b/test-reporter/.gitignore
new file mode 100644
index 0000000..639696a
--- /dev/null
+++ b/test-reporter/.gitignore
@@ -0,0 +1,102 @@
+# Dependency directory
+node_modules
+
+# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# OS metadata
+.DS_Store
+Thumbs.db
+
+# Ignore built ts files
+__tests__/runner/*
+lib/**/*
+
+# Project specific
+__tests__/__results__
diff --git a/test-reporter/.prettierignore b/test-reporter/.prettierignore
new file mode 100644
index 0000000..481914d
--- /dev/null
+++ b/test-reporter/.prettierignore
@@ -0,0 +1,5 @@
+dist/
+lib/
+node_modules/
+__tests__/__outputs__
+__tests__/__snapshots__
diff --git a/test-reporter/.prettierrc.json b/test-reporter/.prettierrc.json
new file mode 100644
index 0000000..e93a59d
--- /dev/null
+++ b/test-reporter/.prettierrc.json
@@ -0,0 +1,10 @@
+{
+  "printWidth": 120,
+  "tabWidth": 2,
+  "useTabs": false,
+  "semi": false,
+  "singleQuote": true,
+  "trailingComma": "none",
+  "bracketSpacing": false,
+  "arrowParens": "avoid"
+}
diff --git a/test-reporter/.vscode/launch.json b/test-reporter/.vscode/launch.json
new file mode 100644
index 0000000..19fe8b0
--- /dev/null
+++ b/test-reporter/.vscode/launch.json
@@ -0,0 +1,35 @@
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest All",
+      "program": "${workspaceFolder}/node_modules/.bin/jest",
+      "args": ["--runInBand"],
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen",
+      "disableOptimisticBPs": true,
+      "windows": {
+        "program": "${workspaceFolder}/node_modules/jest/bin/jest",
+      }
+    },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest Current File",
+      "program": "${workspaceFolder}/node_modules/.bin/jest",
+      "args": [
+        "${fileBasenameNoExtension}",
+        "--config",
+        "jest.config.js"
+      ],
+      "console": "integratedTerminal",
+      "internalConsoleOptions": "neverOpen",
+      "disableOptimisticBPs": true,
+      "windows": {
+        "program": "${workspaceFolder}/node_modules/jest/bin/jest",
+      }
+    }
+  ]
+}
diff --git a/test-reporter/CHANGELOG.md b/test-reporter/CHANGELOG.md
new file mode 100644
index 0000000..bb12289
--- /dev/null
+++ b/test-reporter/CHANGELOG.md
@@ -0,0 +1,44 @@
+# Changelog
+
+## v1.5.0
+- [Add option to convert backslashes in path pattern to forward slashes](https://github.com/dorny/test-reporter/pull/128)
+- [Add option to generate only the summary from processed test results files](https://github.com/dorny/test-reporter/pull/123)
+
+## v1.4.3
+- [Patch java-junit to handle missing time field](https://github.com/dorny/test-reporter/pull/115)
+- [Fix dart-json parsing broken by print message](https://github.com/dorny/test-reporter/pull/114)
+
+## v1.4.2
+- [Fix dotnet-trx parsing of passed tests with non-empty error info](https://github.com/dorny/test-reporter/commit/43d89d5ee509bcef7bd0287aacc0c4a4fb9c1657)
+
+## v1.4.1
+- [Fix dotnet-trx parsing of tests with custom display names](https://github.com/dorny/test-reporter/pull/105)
+
+## v1.4.0
+- [Add support for mocha-json](https://github.com/dorny/test-reporter/pull/90)
+- [Use full URL to fix navigation from summary to suite details](https://github.com/dorny/test-reporter/pull/89)
+- [New report rendering with code blocks instead of tables](https://github.com/dorny/test-reporter/pull/88)
+- [Improve test error messages from flutter](https://github.com/dorny/test-reporter/pull/87)
+
+## v1.3.1
+- [Fix: parsing of .NET duration string without milliseconds](https://github.com/dorny/test-reporter/pull/84)
+- [Fix: dart-json - remove group name from test case names](https://github.com/dorny/test-reporter/pull/85)
+- [Fix: net-trx parser crashing on missing duration attribute](https://github.com/dorny/test-reporter/pull/86)
+
+## v1.3.0
+- [Add support for java-junit](https://github.com/dorny/test-reporter/pull/80)
+- [Fix: Handle test reports with no test cases](https://github.com/dorny/test-reporter/pull/70)
+- [Fix: Reduce number of API calls to get list of files tracked by GitHub](https://github.com/dorny/test-reporter/pull/69)
+
+## v1.2.0
+- [Set `listTests` and `listSuites` to lower detail if report is too big](https://github.com/dorny/test-reporter/pull/60)
+
+## v1.1.0
+- [Support public repo PR workflow](https://github.com/dorny/test-reporter/pull/56)
+
+## v1.0.0
+Supported languages / frameworks:
+- .NET / xUnit / NUnit / MSTest
+- Dart / test
+- Flutter / test
+- JavaScript / JEST
diff --git a/test-reporter/LICENSE b/test-reporter/LICENSE
new file mode 100644
index 0000000..4f87bd8
--- /dev/null
+++ b/test-reporter/LICENSE
@@ -0,0 +1,22 @@
+
+The MIT License (MIT)
+
+Copyright (c) 2021 Michal Dorner and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/test-reporter/README.md b/test-reporter/README.md
new file mode 100644
index 0000000..a2d2d4a
--- /dev/null
+++ b/test-reporter/README.md
@@ -0,0 +1,321 @@
+# Test Reporter
+
+This [Github Action](https://github.com/features/actions) displays test results from popular testing frameworks directly in GitHub.
+
+✔️ Parses test results in XML or JSON format and creates nice report as Github Check Run
+
+✔️ Annotates code where it failed based on message and stack trace captured during test execution
+
+✔️ Provides final `conclusion` and counts of `passed`, `failed` and `skipped` tests as output parameters
+
+**How it looks:**
+|![](assets/fluent-validation-report.png)|![](assets/provider-error-summary.png)|![](assets/provider-error-details.png)|![](assets/mocha-groups.png)|
+|:--:|:--:|:--:|:--:|
+
+**Supported languages / frameworks:**
+- .NET / [xUnit](https://xunit.net/) / [NUnit](https://nunit.org/) / [MSTest](https://github.com/Microsoft/testfx-docs)
+- Dart / [test](https://pub.dev/packages/test)
+- Flutter / [test](https://pub.dev/packages/test)
+- Java / [JUnit](https://junit.org/)
+- JavaScript / [JEST](https://jestjs.io/) / [Mocha](https://mochajs.org/)
+
+For more information see [Supported formats](#supported-formats) section.
+
+Do you miss support for your favorite language or framework?
+Please create [Issue](https://github.com/dorny/test-reporter/issues/new) or contribute with PR.
+
+## Example
+
+Following setup does not work in workflows triggered by pull request from forked repository.
+If that's fine for you, using this action is as simple as:
+
+```yaml
+on:
+  pull_request:
+  push:
+jobs:
+  build-test:
+    name: Build & Test
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2     # checkout the repo
+      - run: npm ci                   # install packages
+      - run: npm test                 # run tests (configured to use jest-junit reporter)
+
+      - name: Test Report
+        uses: dorny/test-reporter@v1
+        if: success() || failure()    # run this step even if previous step failed
+        with:
+          name: JEST Tests            # Name of the check run which will be created
+          path: reports/jest-*.xml    # Path to test results
+          reporter: jest-junit        # Format of test results
+```
+
+## Recommended setup for public repositories
+
+Workflows triggered by pull requests from forked repositories are executed with read-only token and therefore can't create check runs.
+To workaround this security restriction, it's required to use two separate workflows:
+1. `CI` runs in the context of the PR head branch with the read-only token. It executes the tests and uploads test results as a build artifact
+2. `Test Report` runs in the context of the repository main branch with read/write token. It will download test results and create reports
+
+**PR head branch:**  *.github/workflows/ci.yml*
+```yaml
+name: 'CI'
+on:
+  pull_request:
+jobs:
+  build-test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2         # checkout the repo
+      - run: npm ci                       # install packages
+      - run: npm test                     # run tests (configured to use jest-junit reporter)
+      - uses: actions/upload-artifact@v2  # upload test results
+        if: success() || failure()        # run this step even if previous step failed
+        with:
+          name: test-results
+          path: jest-junit.xml
+```
+**default branch:**  *.github/workflows/test-report.yml*
+```yaml
+name: 'Test Report'
+on:
+  workflow_run:
+    workflows: ['CI']                     # runs after CI workflow
+    types:
+      - completed
+jobs:
+  report:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: dorny/test-reporter@v1
+      with:
+        artifact: test-results            # artifact name
+        name: JEST Tests                  # Name of the check run which will be created
+        path: '*.xml'                     # Path to test results (inside artifact .zip)
+        reporter: jest-junit              # Format of test results
+```
+
+## Usage
+
+```yaml
+- uses: dorny/test-reporter@v1
+  with:
+
+    # Name or regex of artifact containing test results
+    # Regular expression must be enclosed in '/'.
+    # Values from captured groups will replace occurrences of $N in report name.
+    # Example:
+    #   artifact: /test-results-(.*)/
+    #   name: 'Test report $1'
+    #   -> Artifact 'test-result-ubuntu' would create report 'Test report ubuntu'
+    artifact: ''
+
+    # Name of the Check Run which will be created
+    name: ''
+
+    # Coma separated list of paths to test results
+    # Supports wildcards via [fast-glob](https://github.com/mrmlnc/fast-glob)
+    # All matched result files must be of the same format
+    path: ''
+
+    # The fast-glob library that is internally used interprets backslashes as escape characters.
+    # If enabled, all backslashes in provided path will be replaced by forward slashes and act as directory separators.
+    # It might be useful when path input variable is composed dynamically from existing directory paths on Windows.
+    path-replace-backslashes: 'false'
+
+    # Format of test results. Supported options:
+    #   dart-json
+    #   dotnet-trx
+    #   flutter-json
+    #   java-junit
+    #   jest-junit
+    #   mocha-json
+    reporter: ''
+
+    # Allows you to generate only the summary.
+    # If enabled, the report will contain a table listing each test results file and the number of passed, failed, and skipped tests.
+    # Detailed listing of test suites and test cases will be skipped.
+    only-summary: 'false'
+
+    # Limits which test suites are listed:
+    #   all
+    #   failed
+    list-suites: 'all'
+
+    # Limits which test cases are listed:
+    #   all
+    #   failed
+    #   none
+    list-tests: 'all'
+
+    # Limits number of created annotations with error message and stack trace captured during test execution.
+    # Must be less or equal to 50.
+    max-annotations: '10'
+
+    # Set action as failed if test report contains any failed test
+    fail-on-error: 'true'
+
+    # Relative path under $GITHUB_WORKSPACE where the repository was checked out.
+    working-directory: ''
+
+    # Personal access token used to interact with Github API
+    # Default: ${{ github.token }}
+    token: ''
+```
+
+## Output parameters
+| Name       | Description              |
+| :--        | :--                      |
+| conclusion | `success` or `failure`   |
+| passed     | Count of passed tests    |
+| failed     | Count of failed tests    |
+| skipped    | Count of skipped tests   |
+| time       | Test execution time [ms] |
+
+## Supported formats
+
+<details>
+  <summary>dart-json</summary>
+
+Test run must be configured to use [JSON](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#reporter) reporter.
+You can configure it in `dart_test.yaml`:
+
+```yml
+file_reporters:
+  json: reports/test-results.json
+```
+
+Or with CLI arguments:
+
+[`dart test --file-reporter="json:test-results.json"`](https://pub.dev/packages/test)
+
+For more information see:
+- [test package](https://pub.dev/packages/test)
+- [test configuration](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md)
+</details>
+
+<details>
+  <summary>dotnet-trx</summary>
+
+Test execution must be configured to produce *Visual Studio Test Results* files (TRX).
+To get test results in TRX format you can execute your tests with CLI arguments:
+
+`dotnet test --logger "trx;LogFileName=test-results.trx"`
+
+Or you can configure TRX test output in `*.csproj` or `Directory.Build.props`:
+```xml
+<PropertyGroup>
+  <VSTestLogger>trx%3bLogFileName=$(MSBuildProjectName).trx</VSTestLogger>
+  <VSTestResultsDirectory>$(MSBuildThisFileDirectory)/TestResults/$(TargetFramework)</VSTestResultsDirectory>
+</PropertyGroup>
+```
+
+Supported testing frameworks:
+- [xUnit](https://xunit.net/)
+- [NUnit](https://nunit.org/)
+- [MSTest](https://github.com/Microsoft/testfx-docs)
+
+For more information see [dotnet test](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test#examples)
+</details>
+
+<details>
+  <summary>flutter-json</summary>
+
+Test run must be configured to use [JSON](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#reporter) reporter.
+You can configure it in `dart_test.yaml`:
+```yml
+file_reporters:
+  json: reports/test-results.json
+```
+
+Or with (undocumented) CLI argument:
+
+`flutter test --machine > test-results.json`
+
+
+According to documentation `dart_test.yaml` should be at the root of the package, next to the package's pubspec.
+On current `stable` and `beta` channels it doesn't work, and you have to put `dart_test.yaml` inside your `test` folder.
+On `dev` channel, it's already fixed.
+
+For more information see:
+- [test package](https://pub.dev/packages/test)
+- [test configuration](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md)
+- [flutter-cli](https://flutter.dev/docs/reference/flutter-cli)
+- [unit testing introduction](https://flutter.dev/docs/cookbook/testing/unit/introduction)
+
+</details>
+
+<details>
+  <summary>java-junit (Experimental)</summary>
+
+Support for [JUnit](https://Junit.org/) XML is experimental - should work but it was not extensively tested.
+To have code annotations working properly, it's required your directory structure matches the package name.
+This is due to the fact Java stack traces don't contain a full path to the source file.
+Some heuristic was necessary to figure out the mapping between the line in the stack trace and an actual source file.
+</details>
+
+<details>
+  <summary>jest-Junit</summary>
+
+[JEST](https://jestjs.io/) testing framework support requires the usage of [jest-Junit](https://github.com/jest-community/jest-Junit) reporter.
+It will create test results in Junit XML format which can be then processed by this action.
+You can use the following example configuration in `package.json`:
+```json
+"scripts": {
+  "test": "jest --ci --reporters=default --reporters=jest-Junit"
+},
+"devDependencies": {
+  "jest": "^26.5.3",
+  "jest-junit": "^12.0.0"
+},
+"jest-junit": {
+  "outputDirectory": "reports",
+  "outputName": "jest-junit.xml",
+  "ancestorSeparator": " › ",
+  "uniqueOutputName": "false",
+  "suiteNameTemplate": "{filepath}",
+  "classNameTemplate": "{classname}",
+  "titleTemplate": "{title}"
+}
+```
+
+Configuration of `uniqueOutputName`, `suiteNameTemplate`, `classNameTemplate`, `titleTemplate` is important for proper visualization of test results.
+</details>
+
+<details>
+  <summary>mocha-json</summary>
+
+[Mocha](https://mochajs.org/) testing framework support requires:
+- Mocha version [v7.2.0](https://github.com/mochajs/mocha/releases/tag/v7.2.0) or higher
+- Usage of [json](https://mochajs.org/#json) reporter.
+
+You can use the following example configuration in `package.json`:
+```json
+"scripts": {
+  "test": "mocha --reporter json > test-results.json"
+}
+```
+
+Test processing might fail if any of your tests write anything on standard output.
+Mocha, unfortunately, doesn't have the option to store `json` output directly to the file, and we have to rely on redirecting its standard output.
+There is a work in progress to fix it: [mocha#4607](https://github.com/mochajs/mocha/pull/4607)
+</details>
+
+## GitHub limitations
+
+Unfortunately, there are some known issues and limitations caused by GitHub API:
+
+- Test report (i.e. Check Run summary) is markdown text. No custom styling or HTML is possible.
+- Maximum report size is 65535 bytes. Input parameters `list-suites` and `list-tests` will be automatically adjusted if max size is exceeded.
+- Test report can't reference any additional files (e.g. screenshots). You can use `actions/upload-artifact@v2` to upload them and inspect them manually.
+- Check Runs are created for specific commit SHA. It's not possible to specify under which workflow test report should belong if more
+  workflows are running for the same SHA. Thanks to this GitHub "feature" it's possible your test report will appear in an unexpected place in GitHub UI.
+  For more information, see [#67](https://github.com/dorny/test-reporter/issues/67).
+
+## See also
+- [paths-filter](https://github.com/dorny/paths-filter) - Conditionally run actions based on files modified by PR, feature branch, or pushed commits
+
+## License
+
+The scripts and documentation in this project are released under the [MIT License](https://github.com/dorny/test-reporter/blob/main/LICENSE)
diff --git a/test-reporter/__tests__/__outputs__/dart-json.md b/test-reporter/__tests__/__outputs__/dart-json.md
new file mode 100644
index 0000000..80a2846
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/dart-json.md
@@ -0,0 +1,28 @@
+![Tests failed](https://img.shields.io/badge/tests-1%20passed%2C%204%20failed%2C%201%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/dart-json.json</a>
+**6** tests were completed in **4s** with **1** passed, **4** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[test/main_test.dart](#r0s0)|1✔️|3❌||74ms|
+|[test/second_test.dart](#r0s1)||1❌|1✖️|51ms|
+### ❌ <a id="user-content-r0s0" href="#r0s0">test/main_test.dart</a>
+```
+Test 1
+  ✔️ Passing test
+Test 1 Test 1.1
+  ❌ Failing test
+	Expected: <2>
+	  Actual: <1>
+	
+  ❌ Exception in target unit
+	Exception: Some error
+Test 2
+  ❌ Exception in test
+	Exception: Some error
+```
+### ❌ <a id="user-content-r0s1" href="#r0s1">test/second_test.dart</a>
+```
+❌ Timeout test
+	TimeoutException after 0:00:00.000001: Test timed out after 0 seconds.
+✖️ Skipped test
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/dotnet-trx.md b/test-reporter/__tests__/__outputs__/dotnet-trx.md
new file mode 100644
index 0000000..ddaaecd
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/dotnet-trx.md
@@ -0,0 +1,31 @@
+![Tests failed](https://img.shields.io/badge/tests-5%20passed%2C%205%20failed%2C%201%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/dotnet-trx.trx</a>
+**11** tests were completed in **1s** with **5** passed, **5** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[DotnetTests.XUnitTests.CalculatorTests](#r0s0)|5✔️|5❌|1✖️|118ms|
+### ❌ <a id="user-content-r0s0" href="#r0s0">DotnetTests.XUnitTests.CalculatorTests</a>
+```
+✔️ Custom Name
+❌ Exception_In_TargetTest
+	System.DivideByZeroException : Attempted to divide by zero.
+❌ Exception_In_Test
+	System.Exception : Test
+❌ Failing_Test
+	Assert.Equal() Failure
+	Expected: 3
+	Actual:   2
+✔️ Is_Even_Number(i: 2)
+❌ Is_Even_Number(i: 3)
+	Assert.True() Failure
+	Expected: True
+	Actual:   False
+✔️ Passing_Test
+✔️ Should be even number(i: 2)
+❌ Should be even number(i: 3)
+	Assert.True() Failure
+	Expected: True
+	Actual:   False
+✖️ Skipped_Test
+✔️ Timeout_Test
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/fluent-validation-test-results.md b/test-reporter/__tests__/__outputs__/fluent-validation-test-results.md
new file mode 100644
index 0000000..9f35f00
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/fluent-validation-test-results.md
@@ -0,0 +1,1057 @@
+![Tests passed successfully](https://img.shields.io/badge/tests-803%20passed%2C%201%20skipped-success)
+## ✔️ <a id="user-content-r0" href="#r0">fixtures/external/FluentValidation.Tests.trx</a>
+**804** tests were completed in **4s** with **803** passed, **0** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[FluentValidation.Tests.AbstractValidatorTester](#r0s0)|35✔️|||12ms|
+|[FluentValidation.Tests.AccessorCacheTests](#r0s1)|4✔️||1✖️|4ms|
+|[FluentValidation.Tests.AssemblyScannerTester](#r0s2)|2✔️|||2ms|
+|[FluentValidation.Tests.CascadingFailuresTester](#r0s3)|38✔️|||23ms|
+|[FluentValidation.Tests.ChainedValidationTester](#r0s4)|13✔️|||6ms|
+|[FluentValidation.Tests.ChainingValidatorsTester](#r0s5)|3✔️|||1ms|
+|[FluentValidation.Tests.ChildRulesTests](#r0s6)|2✔️|||7ms|
+|[FluentValidation.Tests.CollectionValidatorWithParentTests](#r0s7)|16✔️|||13ms|
+|[FluentValidation.Tests.ComplexValidationTester](#r0s8)|17✔️|||26ms|
+|[FluentValidation.Tests.ConditionTests](#r0s9)|18✔️|||9ms|
+|[FluentValidation.Tests.CreditCardValidatorTests](#r0s10)|2✔️|||2ms|
+|[FluentValidation.Tests.CustomFailureActionTester](#r0s11)|3✔️|||1ms|
+|[FluentValidation.Tests.CustomMessageFormatTester](#r0s12)|6✔️|||3ms|
+|[FluentValidation.Tests.CustomValidatorTester](#r0s13)|10✔️|||6ms|
+|[FluentValidation.Tests.DefaultValidatorExtensionTester](#r0s14)|30✔️|||38ms|
+|[FluentValidation.Tests.EmailValidatorTests](#r0s15)|36✔️|||18ms|
+|[FluentValidation.Tests.EmptyTester](#r0s16)|9✔️|||5ms|
+|[FluentValidation.Tests.EnumValidatorTests](#r0s17)|12✔️|||24ms|
+|[FluentValidation.Tests.EqualValidatorTests](#r0s18)|10✔️|||3ms|
+|[FluentValidation.Tests.ExactLengthValidatorTester](#r0s19)|6✔️|||2ms|
+|[FluentValidation.Tests.ExclusiveBetweenValidatorTests](#r0s20)|19✔️|||6ms|
+|[FluentValidation.Tests.ExtensionTester](#r0s21)|4✔️|||1ms|
+|[FluentValidation.Tests.ForEachRuleTests](#r0s22)|34✔️|||47ms|
+|[FluentValidation.Tests.GreaterThanOrEqualToValidatorTester](#r0s23)|14✔️|||5ms|
+|[FluentValidation.Tests.GreaterThanValidatorTester](#r0s24)|13✔️|||4ms|
+|[FluentValidation.Tests.InclusiveBetweenValidatorTests](#r0s25)|18✔️|||4ms|
+|[FluentValidation.Tests.InheritanceValidatorTest](#r0s26)|11✔️|||18ms|
+|[FluentValidation.Tests.InlineValidatorTester](#r0s27)|1✔️|||2ms|
+|[FluentValidation.Tests.LanguageManagerTests](#r0s28)|21✔️|||28ms|
+|[FluentValidation.Tests.LengthValidatorTests](#r0s29)|16✔️|||17ms|
+|[FluentValidation.Tests.LessThanOrEqualToValidatorTester](#r0s30)|13✔️|||4ms|
+|[FluentValidation.Tests.LessThanValidatorTester](#r0s31)|16✔️|||6ms|
+|[FluentValidation.Tests.LocalisedMessagesTester](#r0s32)|6✔️|||3ms|
+|[FluentValidation.Tests.LocalisedNameTester](#r0s33)|2✔️|||1ms|
+|[FluentValidation.Tests.MemberAccessorTests](#r0s34)|9✔️|||5ms|
+|[FluentValidation.Tests.MessageFormatterTests](#r0s35)|10✔️|||2ms|
+|[FluentValidation.Tests.ModelLevelValidatorTests](#r0s36)|2✔️|||1ms|
+|[FluentValidation.Tests.NameResolutionPluggabilityTester](#r0s37)|3✔️|||2ms|
+|[FluentValidation.Tests.NotEmptyTester](#r0s38)|10✔️|||7ms|
+|[FluentValidation.Tests.NotEqualValidatorTests](#r0s39)|11✔️|||7ms|
+|[FluentValidation.Tests.NotNullTester](#r0s40)|5✔️|||1ms|
+|[FluentValidation.Tests.NullTester](#r0s41)|5✔️|||2ms|
+|[FluentValidation.Tests.OnFailureTests](#r0s42)|10✔️|||8ms|
+|[FluentValidation.Tests.PredicateValidatorTester](#r0s43)|5✔️|||2ms|
+|[FluentValidation.Tests.PropertyChainTests](#r0s44)|7✔️|||1ms|
+|[FluentValidation.Tests.RegularExpressionValidatorTests](#r0s45)|15✔️|||6ms|
+|[FluentValidation.Tests.RuleBuilderTests](#r0s46)|29✔️|||96ms|
+|[FluentValidation.Tests.RuleDependencyTests](#r0s47)|14✔️|||3s|
+|[FluentValidation.Tests.RulesetTests](#r0s48)|21✔️|||14ms|
+|[FluentValidation.Tests.ScalePrecisionValidatorTests](#r0s49)|6✔️|||4ms|
+|[FluentValidation.Tests.SharedConditionTests](#r0s50)|42✔️|||42ms|
+|[FluentValidation.Tests.StandalonePropertyValidationTester](#r0s51)|1✔️|||0ms|
+|[FluentValidation.Tests.StringEnumValidatorTests](#r0s52)|10✔️|||5ms|
+|[FluentValidation.Tests.TrackingCollectionTests](#r0s53)|3✔️|||2ms|
+|[FluentValidation.Tests.TransformTests](#r0s54)|4✔️|||3ms|
+|[FluentValidation.Tests.UserSeverityTester](#r0s55)|7✔️|||3ms|
+|[FluentValidation.Tests.UserStateTester](#r0s56)|4✔️|||3ms|
+|[FluentValidation.Tests.ValidateAndThrowTester](#r0s57)|14✔️|||25ms|
+|[FluentValidation.Tests.ValidationResultTests](#r0s58)|8✔️|||8ms|
+|[FluentValidation.Tests.ValidatorDescriptorTester](#r0s59)|5✔️|||1ms|
+|[FluentValidation.Tests.ValidatorSelectorTests](#r0s60)|10✔️|||9ms|
+|[FluentValidation.Tests.ValidatorTesterTester](#r0s61)|73✔️|||74ms|
+### ✔️ <a id="user-content-r0s0" href="#r0s0">FluentValidation.Tests.AbstractValidatorTester</a>
+```
+✔️ Can_replace_default_errorcode_resolver
+✔️ CanValidateInstancesOfType_returns_false_when_comparing_against_some_other_type
+✔️ CanValidateInstancesOfType_returns_true_when_comparing_against_same_type
+✔️ CanValidateInstancesOfType_returns_true_when_comparing_against_subclass
+✔️ Default_error_code_should_be_class_name
+✔️ OverridePropertyName_should_override_property_name
+✔️ OverridePropertyName_with_lambda_should_override_property_name
+✔️ PreValidate_bypasses_nullcheck_on_instance
+✔️ RuleForeach_with_null_instances
+✔️ Should_be_valid_when_there_are_no_failures_for_single_property
+✔️ Should_not_main_state
+✔️ Should_throw_for_non_member_expression_when_validating_single_property
+✔️ Should_throw_when_rule_is_null
+✔️ Should_validate_public_Field
+✔️ Should_validate_single_Field
+✔️ Should_validate_single_property
+✔️ Should_validate_single_property_where_invalid_property_as_string
+✔️ Should_validate_single_property_where_property_as_string
+✔️ Uses_named_parameters_to_validate_ruleset
+✔️ Validates_single_property_by_path
+✔️ Validates_type_when_using_non_generic_validate_overload
+✔️ When_the_validators_fail_then_the_errors_Should_be_accessible_via_the_errors_property
+✔️ When_the_validators_fail_then_validatorrunner_should_return_false
+✔️ When_the_Validators_pass_then_the_validatorRunner_should_return_true
+✔️ WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_Validate(preValidationResult: )
+✔️ WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_Validate(preValidationResult: AnotherInt Test Message)
+✔️ WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_ValidateAsync(preValidationResult: )
+✔️ WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_ValidateAsync(preValidationResult: AnotherInt Test Message)
+✔️ WhenPreValidationReturnsTrue_ValidatorsGetHit_Validate
+✔️ WhenPreValidationReturnsTrue_ValidatorsGetHit_ValidateAsync
+✔️ WithErrorCode_should_override_error_code
+✔️ WithMessage_and_WithErrorCode_should_override_error_message_and_error_code
+✔️ WithMessage_should_override_error_message
+✔️ WithName_should_override_field_name
+✔️ WithName_should_override_field_name_with_value_from_other_property
+```
+### ✔️ <a id="user-content-r0s1" href="#r0s1">FluentValidation.Tests.AccessorCacheTests</a>
+```
+✖️ Benchmark
+✔️ Equality_comparison_check
+✔️ Gets_accessor
+✔️ Gets_member_for_nested_property
+✔️ Identifies_if_memberexp_acts_on_model_instance
+```
+### ✔️ <a id="user-content-r0s2" href="#r0s2">FluentValidation.Tests.AssemblyScannerTester</a>
+```
+✔️ Finds_validators_for_types
+✔️ ForEach_iterates_over_types
+```
+### ✔️ <a id="user-content-r0s3" href="#r0s3">FluentValidation.Tests.CascadingFailuresTester</a>
+```
+✔️ Cascade_mode_can_be_set_after_validator_instantiated
+✔️ Cascade_mode_can_be_set_after_validator_instantiated_async
+✔️ Cascade_mode_can_be_set_after_validator_instantiated_async_legacy
+✔️ Cascade_mode_can_be_set_after_validator_instantiated_legacy
+✔️ Validation_continues_on_failure
+✔️ Validation_continues_on_failure_async
+✔️ Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level
+✔️ Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_async
+✔️ Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_async_legacy
+✔️ Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_legacy
+✔️ Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level
+✔️ Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_async
+✔️ Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_async_legacy
+✔️ Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_legacy
+✔️ Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop
+✔️ Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_async
+✔️ Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_async_legacy
+✔️ Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_legacy
+✔️ Validation_continues_when_set_to_Continue_at_validator_level
+✔️ Validation_continues_when_set_to_Continue_at_validator_level_async
+✔️ Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level
+✔️ Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_async
+✔️ Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_async_legacy
+✔️ Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_legacy
+✔️ Validation_stops_on_first_failure
+✔️ Validation_stops_on_first_failure_async
+✔️ Validation_stops_on_first_failure_async_legacy
+✔️ Validation_stops_on_first_failure_legacy
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_and_async_validator_is_invoked_synchronously
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_and_async_validator_is_invoked_synchronously_legacy
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_async
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_async_legacy
+✔️ Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_legacy
+✔️ Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level
+✔️ Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_async
+✔️ Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_async_legacy
+✔️ Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_legacy
+```
+### ✔️ <a id="user-content-r0s4" href="#r0s4">FluentValidation.Tests.ChainedValidationTester</a>
+```
+✔️ Can_validate_using_validator_for_base_type
+✔️ Chained_property_should_be_excluded
+✔️ Chained_validator_descriptor
+✔️ Chained_validator_should_not_be_invoked_on_null_property
+✔️ Condition_should_work_with_chained_property
+✔️ Explicitly_included_properties_should_be_propagated_to_nested_validators
+✔️ Explicitly_included_properties_should_be_propagated_to_nested_validators_using_strings
+✔️ Separate_validation_on_chained_property
+✔️ Separate_validation_on_chained_property_conditional
+✔️ Separate_validation_on_chained_property_valid
+✔️ Should_allow_normal_rules_and_chained_property_on_same_property
+✔️ Uses_explicit_ruleset
+✔️ Validates_chained_property
+```
+### ✔️ <a id="user-content-r0s5" href="#r0s5">FluentValidation.Tests.ChainingValidatorsTester</a>
+```
+✔️ Options_should_only_apply_to_current_validator
+✔️ Should_create_multiple_validators
+✔️ Should_execute_multiple_validators
+```
+### ✔️ <a id="user-content-r0s6" href="#r0s6">FluentValidation.Tests.ChildRulesTests</a>
+```
+✔️ Can_define_nested_rules_for_collection
+✔️ ChildRules_works_with_RuleSet
+```
+### ✔️ <a id="user-content-r0s7" href="#r0s7">FluentValidation.Tests.CollectionValidatorWithParentTests</a>
+```
+✔️ Async_condition_should_work_with_child_collection
+✔️ Can_specify_condition_for_individual_collection_elements
+✔️ Can_validate_collection_using_validator_for_base_type
+✔️ Collection_should_be_excluded
+✔️ Collection_should_be_explicitly_included_with_expression
+✔️ Collection_should_be_explicitly_included_with_string
+✔️ Condition_should_work_with_child_collection
+✔️ Creates_validator_using_context_from_property_value
+✔️ Should_override_property_name
+✔️ Should_work_with_top_level_collection_validator
+✔️ Should_work_with_top_level_collection_validator_and_overriden_name
+✔️ Skips_null_items
+✔️ Validates_collection
+✔️ Validates_collection_asynchronously
+✔️ Validates_collection_several_levels_deep
+✔️ Validates_collection_several_levels_deep_async
+```
+### ✔️ <a id="user-content-r0s8" href="#r0s8">FluentValidation.Tests.ComplexValidationTester</a>
+```
+✔️ Async_condition_should_work_with_complex_property
+✔️ Async_condition_should_work_with_complex_property_when_validator_invoked_synchronously
+✔️ Can_directly_validate_multiple_fields_of_same_type
+✔️ Can_validate_using_validator_for_base_type
+✔️ Complex_property_should_be_excluded
+✔️ Complex_validator_should_not_be_invoked_on_null_property
+✔️ Condition_should_work_with_complex_property
+✔️ Condition_should_work_with_complex_property_when_invoked_async
+✔️ Explicitly_included_properties_should_be_propagated_to_nested_validators
+✔️ Explicitly_included_properties_should_be_propagated_to_nested_validators_using_strings
+✔️ Multiple_rules_in_chain_with_childvalidator_shouldnt_reuse_accessor
+✔️ Multiple_rules_in_chain_with_childvalidator_shouldnt_reuse_accessor_async
+✔️ Should_allow_normal_rules_and_complex_property_on_same_property
+✔️ Should_override_propertyName
+✔️ Validates_child_validator_asynchronously
+✔️ Validates_child_validator_synchronously
+✔️ Validates_complex_property
+```
+### ✔️ <a id="user-content-r0s9" href="#r0s9">FluentValidation.Tests.ConditionTests</a>
+```
+✔️ Async_condition_executed_synchronosuly_with_asynchronous_collection_rule
+✔️ Async_condition_executed_synchronosuly_with_asynchronous_rule
+✔️ Async_condition_executed_synchronosuly_with_synchronous_collection_role
+✔️ Async_condition_executed_synchronosuly_with_synchronous_role
+✔️ Async_condition_is_applied_to_all_validators_in_the_chain
+✔️ Async_condition_is_applied_to_all_validators_in_the_chain_when_executed_synchronously
+✔️ Async_condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator
+✔️ Condition_is_applied_to_all_validators_in_the_chain
+✔️ Condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator
+✔️ Sync_condition_is_applied_to_async_validators
+✔️ Validation_should_fail_when_async_condition_does_not_match
+✔️ Validation_should_fail_when_async_condition_matches
+✔️ Validation_should_fail_when_condition_does_not_match
+✔️ Validation_should_fail_when_condition_matches
+✔️ Validation_should_succeed_when_async_condition_does_not_match
+✔️ Validation_should_succeed_when_async_condition_matches
+✔️ Validation_should_succeed_when_condition_does_not_match
+✔️ Validation_should_succeed_when_condition_matches
+```
+### ✔️ <a id="user-content-r0s10" href="#r0s10">FluentValidation.Tests.CreditCardValidatorTests</a>
+```
+✔️ IsValidTests
+✔️ When_validation_fails_the_default_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s11" href="#r0s11">FluentValidation.Tests.CustomFailureActionTester</a>
+```
+✔️ Does_not_invoke_action_if_validation_success
+✔️ Invokes_custom_action_on_failure
+✔️ Passes_object_being_validated_to_action
+```
+### ✔️ <a id="user-content-r0s12" href="#r0s12">FluentValidation.Tests.CustomMessageFormatTester</a>
+```
+✔️ Replaces_propertyvalue_placeholder
+✔️ Replaces_propertyvalue_with_empty_string_when_null
+✔️ Should_format_custom_message
+✔️ Uses_custom_delegate_for_building_message
+✔️ Uses_custom_delegate_for_building_message_only_for_specific_validator
+✔️ Uses_property_value_in_message
+```
+### ✔️ <a id="user-content-r0s13" href="#r0s13">FluentValidation.Tests.CustomValidatorTester</a>
+```
+✔️ New_Custom_Returns_single_failure
+✔️ New_Custom_Returns_single_failure_async
+✔️ New_custom_uses_empty_property_name_for_model_level_rule
+✔️ New_Custom_When_property_name_omitted_infers_property_name
+✔️ New_Custom_When_property_name_omitted_infers_property_name_nested
+✔️ New_Custom_within_ruleset
+✔️ New_CustomAsync_within_ruleset
+✔️ Perserves_property_chain_using_custom
+✔️ Runs_async_rule_synchronously_when_validator_invoked_synchronously
+✔️ Runs_sync_rule_asynchronously_when_validator_invoked_asynchronously
+```
+### ✔️ <a id="user-content-r0s14" href="#r0s14">FluentValidation.Tests.DefaultValidatorExtensionTester</a>
+```
+✔️ Empty_should_create_EmptyValidator
+✔️ Equal_should_create_EqualValidator_with_explicit_value
+✔️ Equal_should_create_EqualValidator_with_lambda
+✔️ GreaterThan_should_create_GreaterThanValidator_with_explicit_value
+✔️ GreaterThan_should_create_GreaterThanValidator_with_lambda
+✔️ GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_explicit_value
+✔️ GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_lambda
+✔️ GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_lambda_with_other_Nullable
+✔️ Length_should_create_ExactLengthValidator
+✔️ Length_should_create_LengthValidator
+✔️ Length_should_create_MaximumLengthValidator
+✔️ Length_should_create_MinimumLengthValidator
+✔️ LessThan_should_create_LessThanValidator_with_explicit_value
+✔️ LessThan_should_create_LessThanValidator_with_lambda
+✔️ LessThanOrEqual_should_create_LessThanOrEqualValidator_with_explicit_value
+✔️ LessThanOrEqual_should_create_LessThanOrEqualValidator_with_lambda
+✔️ LessThanOrEqual_should_create_LessThanOrEqualValidator_with_lambda_with_other_Nullable
+✔️ Must_should_create_PredicateValidator_with_context
+✔️ Must_should_create_PredicateValidator_with_PropertyValidatorContext
+✔️ Must_should_create_PredicteValidator
+✔️ MustAsync_should_create_AsyncPredicateValidator_with_context
+✔️ MustAsync_should_create_AsyncPredicateValidator_with_PropertyValidatorContext
+✔️ MustAsync_should_create_AsyncPredicteValidator
+✔️ MustAsync_should_not_throw_InvalidCastException
+✔️ NotEmpty_should_create_NotEmptyValidator
+✔️ NotEqual_should_create_NotEqualValidator_with_explicit_value
+✔️ NotEqual_should_create_NotEqualValidator_with_lambda
+✔️ NotNull_should_create_NotNullValidator
+✔️ ScalePrecision_should_create_ScalePrecisionValidator
+✔️ ScalePrecision_should_create_ScalePrecisionValidator_with_ignore_trailing_zeros
+```
+### ✔️ <a id="user-content-r0s15" href="#r0s15">FluentValidation.Tests.EmailValidatorTests</a>
+```
+✔️ Fails_email_validation_aspnetcore_compatible(email: " \r \t \n")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "@someDomain.com")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "@someDomain@abc.com")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "0")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "someName")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "someName@")
+✔️ Fails_email_validation_aspnetcore_compatible(email: "someName@a@b.com")
+✔️ Invalid_email_addressex_regex(email: "")
+✔️ Invalid_email_addressex_regex(email: "first.last@test..co.uk")
+✔️ Invalid_email_addressex_regex(email: "testperso")
+✔️ Invalid_email_addressex_regex(email: "thisisaverylongstringcodeplex.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: " @someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "!#$%&'*+-/=?^_`|~@someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "\"firstName.lastName\"@someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "1234@someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "firstName.lastName@someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "someName@1234.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "someName@some_domain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "someName@some~domain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "someName@someDomain.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: "someName@someDomain￯.com")
+✔️ Valid_email_addresses_aspnetcore_compatible(email: null)
+✔️ Valid_email_addresses_regex(email: "__somename@example.com")
+✔️ Valid_email_addresses_regex(email: "!def!xyz%abc@example.com")
+✔️ Valid_email_addresses_regex(email: "\"Abc@def\"@example.com")
+✔️ Valid_email_addresses_regex(email: "\"Abc\\@def\"@example.com")
+✔️ Valid_email_addresses_regex(email: "\"Fred Bloggs\"@example.com")
+✔️ Valid_email_addresses_regex(email: "\"Joe\\Blow\"@example.com")
+✔️ Valid_email_addresses_regex(email: "$A12345@example.com")
+✔️ Valid_email_addresses_regex(email: "customer/department=shipping@example.com")
+✔️ Valid_email_addresses_regex(email: "first.last@test.co.uk")
+✔️ Valid_email_addresses_regex(email: "testperson@gmail.com")
+✔️ Valid_email_addresses_regex(email: "TestPerson@gmail.com")
+✔️ Valid_email_addresses_regex(email: "testperson+label@gmail.com")
+✔️ Valid_email_addresses_regex(email: null)
+```
+### ✔️ <a id="user-content-r0s16" href="#r0s16">FluentValidation.Tests.EmptyTester</a>
+```
+✔️ Passes_for_ienumerable_that_doesnt_implement_ICollection
+✔️ Passes_when_collection_empty
+✔️ When_there_is_a_value_then_the_validator_should_fail
+✔️ When_validation_fails_error_should_be_set
+✔️ When_value_is_Default_for_type_validator_should_pass_datetime
+✔️ When_value_is_Default_for_type_validator_should_pass_int
+✔️ When_value_is_empty_string_validator_should_pass
+✔️ When_value_is_null_validator_should_pass
+✔️ When_value_is_whitespace_validation_should_pass
+```
+### ✔️ <a id="user-content-r0s17" href="#r0s17">FluentValidation.Tests.EnumValidatorTests</a>
+```
+✔️ Flags_enum_invalid_when_using_outofrange_negative_value
+✔️ Flags_enum_invalid_when_using_outofrange_positive_value
+✔️ Flags_enum_valid_when_using_bitwise_value
+✔️ Flags_enum_validates_correctly_when_using_zero_value
+✔️ Flags_enum_with_overlapping_flags_valid_when_using_bitwise_value
+✔️ IsValidTests
+✔️ Nullable_enum_invalid_when_bad_value_specified
+✔️ Nullable_enum_valid_when_property_value_is_null
+✔️ Nullable_enum_valid_when_value_specified
+✔️ When_the_enum_is_initialized_with_invalid_value_then_the_validator_should_fail
+✔️ When_the_enum_is_not_initialized_with_valid_value_then_the_validator_should_fail
+✔️ When_validation_fails_the_default_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s18" href="#r0s18">FluentValidation.Tests.EqualValidatorTests</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Should_store_comparison_type
+✔️ Should_store_property_to_compare
+✔️ Should_succeed_on_case_insensitive_comparison
+✔️ Should_succeed_on_case_insensitive_comparison_using_expression
+✔️ Should_use_ordinal_comparison_by_default
+✔️ Validates_against_property
+✔️ When_the_objects_are_equal_validation_should_succeed
+✔️ When_the_objects_are_not_equal_validation_should_fail
+✔️ When_validation_fails_the_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s19" href="#r0s19">FluentValidation.Tests.ExactLengthValidatorTester</a>
+```
+✔️ Min_and_max_properties_should_be_set
+✔️ When_exact_length_rule_failes_error_should_have_exact_length_error_errorcode
+✔️ When_the_text_is_an_exact_length_the_validator_should_pass
+✔️ When_the_text_length_is_larger_the_validator_should_fail
+✔️ When_the_text_length_is_smaller_the_validator_should_fail
+✔️ When_the_validator_fails_the_error_message_should_be_set
+```
+### ✔️ <a id="user-content-r0s20" href="#r0s20">FluentValidation.Tests.ExclusiveBetweenValidatorTests</a>
+```
+✔️ To_and_from_properties_should_be_set
+✔️ To_and_from_properties_should_be_set_for_dates
+✔️ To_and_from_properties_should_be_set_for_strings
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_not_null
+✔️ When_the_text_is_larger_than_the_range_then_the_validator_should_fail
+✔️ When_the_text_is_larger_than_the_range_then_the_validator_should_fail_for_strings
+✔️ When_the_to_is_smaller_than_the_from_then_the_validator_should_throw
+✔️ When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_strings
+✔️ When_the_validator_fails_the_error_message_should_be_set
+✔️ When_the_validator_fails_the_error_message_should_be_set_for_strings
+✔️ When_the_value_is_between_the_range_specified_then_the_validator_should_pass
+✔️ When_the_value_is_between_the_range_specified_then_the_validator_should_pass_for_strings
+✔️ When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_fail
+✔️ When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_fail_for_strings
+✔️ When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_fail
+✔️ When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_fail_for_strings
+✔️ When_the_value_is_smaller_than_the_range_then_the_validator_should_fail
+✔️ When_the_value_is_smaller_than_the_range_then_the_validator_should_fail_for_strings
+```
+### ✔️ <a id="user-content-r0s21" href="#r0s21">FluentValidation.Tests.ExtensionTester</a>
+```
+✔️ Should_extract_member_from_member_expression
+✔️ Should_return_null_for_non_member_expressions
+✔️ Should_split_pascal_cased_member_name
+✔️ SplitPascalCase_should_return_null_when_input_is_null
+```
+### ✔️ <a id="user-content-r0s22" href="#r0s22">FluentValidation.Tests.ForEachRuleTests</a>
+```
+✔️ Async_condition_should_work_with_child_collection
+✔️ Can_access_colletion_index
+✔️ Can_access_colletion_index_async
+✔️ Can_access_parent_index
+✔️ Can_access_parent_index_async
+✔️ Can_specify_condition_for_individual_collection_elements
+✔️ Can_use_cascade_with_RuleForEach
+✔️ Can_validate_collection_using_validator_for_base_type
+✔️ Collection_should_be_excluded
+✔️ Collection_should_be_explicitly_included_with_expression
+✔️ Collection_should_be_explicitly_included_with_string
+✔️ Condition_should_work_with_child_collection
+✔️ Correctly_gets_collection_indices
+✔️ Correctly_gets_collection_indices_async
+✔️ Executes_rule_for_each_item_in_collection
+✔️ Executes_rule_for_each_item_in_collection_async
+✔️ Nested_collection_for_null_property_should_not_throw_null_reference
+✔️ Nested_conditions_Rule_For
+✔️ Nested_conditions_Rule_For_Each
+✔️ Overrides_indexer
+✔️ Overrides_indexer_async
+✔️ Regular_rules_can_drop_into_RuleForEach
+✔️ RuleForEach_async_RunsTasksSynchronously
+✔️ Should_not_scramble_property_name_when_using_collection_validators_several_levels_deep
+✔️ Should_not_scramble_property_name_when_using_collection_validators_several_levels_deep_with_ValidateAsync
+✔️ Should_override_property_name
+✔️ Skips_null_items
+✔️ Top_level_collection
+✔️ Uses_useful_error_message_when_used_on_non_property
+✔️ Validates_child_validator_asynchronously
+✔️ Validates_child_validator_synchronously
+✔️ Validates_collection
+✔️ When_runs_outside_RuleForEach_loop
+✔️ When_runs_outside_RuleForEach_loop_async
+```
+### ✔️ <a id="user-content-r0s23" href="#r0s23">FluentValidation.Tests.GreaterThanOrEqualToValidatorTester</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Comparison_type
+✔️ Should_fail_when_less_than_input
+✔️ Should_localize_value
+✔️ Should_set_default_error_when_validation_fails
+✔️ Should_succeed_when_equal_to_input
+✔️ Should_succeed_when_greater_than_input
+✔️ Validates_nullable_with_nullable_property
+✔️ Validates_with_nullable_property
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_is_null_cross_property
+✔️ Validates_with_nullable_when_property_not_null
+✔️ Validates_with_nullable_when_property_not_null_cross_property
+✔️ Validates_with_property
+```
+### ✔️ <a id="user-content-r0s24" href="#r0s24">FluentValidation.Tests.GreaterThanValidatorTester</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Comparison_Type
+✔️ Should_fail_when_equal_to_input
+✔️ Should_fail_when_less_than_input
+✔️ Should_set_default_error_when_validation_fails
+✔️ Should_succeed_when_greater_than_input
+✔️ Validates_nullable_with_nullable_property
+✔️ Validates_with_nullable_property
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_is_null_cross_property
+✔️ Validates_with_nullable_when_property_not_null
+✔️ Validates_with_nullable_when_property_not_null_cross_property
+✔️ Validates_with_property
+```
+### ✔️ <a id="user-content-r0s25" href="#r0s25">FluentValidation.Tests.InclusiveBetweenValidatorTests</a>
+```
+✔️ To_and_from_properties_should_be_set
+✔️ To_and_from_properties_should_be_set_for_strings
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_not_null
+✔️ When_the_text_is_larger_than_the_range_then_the_validator_should_fail
+✔️ When_the_text_is_larger_than_the_range_then_the_validator_should_fail_for_strings
+✔️ When_the_to_is_smaller_than_the_from_then_the_validator_should_throw
+✔️ When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_strings
+✔️ When_the_validator_fails_the_error_message_should_be_set
+✔️ When_the_validator_fails_the_error_message_should_be_set_for_strings
+✔️ When_the_value_is_between_the_range_specified_then_the_validator_should_pass
+✔️ When_the_value_is_between_the_range_specified_then_the_validator_should_pass_for_strings
+✔️ When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass
+✔️ When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass_for_strings
+✔️ When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass
+✔️ When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass_for_strings
+✔️ When_the_value_is_smaller_than_the_range_then_the_validator_should_fail
+✔️ When_the_value_is_smaller_than_the_range_then_the_validator_should_fail_for_strings
+```
+### ✔️ <a id="user-content-r0s26" href="#r0s26">FluentValidation.Tests.InheritanceValidatorTest</a>
+```
+✔️ Can_use_custom_subclass_with_nongeneric_overload
+✔️ Validates_collection
+✔️ Validates_collection_async
+✔️ Validates_inheritance_async
+✔️ Validates_inheritance_hierarchy
+✔️ Validates_ruleset
+✔️ Validates_ruleset_async
+✔️ Validates_with_callback
+✔️ Validates_with_callback_accepting_derived
+✔️ Validates_with_callback_accepting_derived_async
+✔️ Validates_with_callback_async
+```
+### ✔️ <a id="user-content-r0s27" href="#r0s27">FluentValidation.Tests.InlineValidatorTester</a>
+```
+✔️ Uses_inline_validator_to_build_rules
+```
+### ✔️ <a id="user-content-r0s28" href="#r0s28">FluentValidation.Tests.LanguageManagerTests</a>
+```
+✔️ All_languages_should_be_loaded
+✔️ All_localizations_have_same_parameters_as_English
+✔️ Always_use_specific_language
+✔️ Always_use_specific_language_with_string_source
+✔️ Can_replace_message
+✔️ Can_replace_message_without_overriding_all_languages
+✔️ Disables_localization
+✔️ Falls_back_to_default_localization_key_when_error_code_key_not_found
+✔️ Falls_back_to_english_when_culture_not_registered
+✔️ Falls_back_to_english_when_translation_missing
+✔️ Falls_back_to_parent_culture
+✔️ Gets_translation_for_bosnian_latin_culture(cultureName: "bs-Latn-BA")
+✔️ Gets_translation_for_bosnian_latin_culture(cultureName: "bs-Latn")
+✔️ Gets_translation_for_bosnian_latin_culture(cultureName: "bs")
+✔️ Gets_translation_for_croatian_culture
+✔️ Gets_translation_for_culture
+✔️ Gets_translation_for_serbian_culture(cultureName: "sr-Latn-RS")
+✔️ Gets_translation_for_serbian_culture(cultureName: "sr-Latn")
+✔️ Gets_translation_for_serbian_culture(cultureName: "sr")
+✔️ Gets_translation_for_specific_culture
+✔️ Uses_error_code_as_localization_key
+```
+### ✔️ <a id="user-content-r0s29" href="#r0s29">FluentValidation.Tests.LengthValidatorTests</a>
+```
+✔️ Min_and_max_properties_should_be_set
+✔️ When_input_is_null_then_the_validator_should_pass
+✔️ When_the_max_is_smaller_than_the_min_then_the_validator_should_throw
+✔️ When_the_maxlength_validator_fails_the_error_message_should_be_set
+✔️ When_the_minlength_validator_fails_the_error_message_should_be_set
+✔️ When_the_text_is_between_the_lambda_range_specified_then_the_validator_should_pass
+✔️ When_the_text_is_between_the_range_specified_then_the_validator_should_pass
+✔️ When_the_text_is_exactly_the_size_of_the_lambda_lower_bound_then_the_validator_should_pass
+✔️ When_the_text_is_exactly_the_size_of_the_lambda_upper_bound_then_the_validator_should_pass
+✔️ When_the_text_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass
+✔️ When_the_text_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass
+✔️ When_the_text_is_larger_than_the_lambda_range_then_the_validator_should_fail
+✔️ When_the_text_is_larger_than_the_range_then_the_validator_should_fail
+✔️ When_the_text_is_smaller_than_the_lambda_range_then_the_validator_should_fail
+✔️ When_the_text_is_smaller_than_the_range_then_the_validator_should_fail
+✔️ When_the_validator_fails_the_error_message_should_be_set
+```
+### ✔️ <a id="user-content-r0s30" href="#r0s30">FluentValidation.Tests.LessThanOrEqualToValidatorTester</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Comparison_type
+✔️ Should_fail_when_greater_than_input
+✔️ Should_set_default_error_when_validation_fails
+✔️ Should_succeed_when_equal_to_input
+✔️ Should_succeed_when_less_than_input
+✔️ Validates_nullable_with_nullable_property
+✔️ Validates_with_nullable_property
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_is_null_cross_property
+✔️ Validates_with_nullable_when_property_not_null
+✔️ Validates_with_nullable_when_property_not_null_cross_property
+✔️ Validates_with_property
+```
+### ✔️ <a id="user-content-r0s31" href="#r0s31">FluentValidation.Tests.LessThanValidatorTester</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Comparison_type
+✔️ Extracts_property_from_constant_using_expression
+✔️ Extracts_property_from_expression
+✔️ Should_fail_when_equal_to_input
+✔️ Should_fail_when_greater_than_input
+✔️ Should_set_default_validation_message_when_validation_fails
+✔️ Should_succeed_when_less_than_input
+✔️ Should_throw_when_value_to_compare_is_null
+✔️ Validates_against_property
+✔️ Validates_nullable_with_nullable_property
+✔️ Validates_with_nullable_property
+✔️ Validates_with_nullable_when_property_is_null
+✔️ Validates_with_nullable_when_property_not_null
+✔️ Validates_with_nullable_when_property_not_null_cross_property
+✔️ Validates_with_nullable_when_property_null_cross_property
+```
+### ✔️ <a id="user-content-r0s32" href="#r0s32">FluentValidation.Tests.LocalisedMessagesTester</a>
+```
+✔️ Correctly_assigns_default_localized_error_message
+✔️ Does_not_throw_InvalidCastException_when_using_RuleForEach
+✔️ Formats_string_with_placeholders
+✔️ Formats_string_with_placeholders_when_you_cant_edit_the_string
+✔️ Uses_func_to_get_message
+✔️ Uses_string_format_with_property_value
+```
+### ✔️ <a id="user-content-r0s33" href="#r0s33">FluentValidation.Tests.LocalisedNameTester</a>
+```
+✔️ Uses_localized_name
+✔️ Uses_localized_name_expression
+```
+### ✔️ <a id="user-content-r0s34" href="#r0s34">FluentValidation.Tests.MemberAccessorTests</a>
+```
+✔️ ComplexPropertyGet
+✔️ ComplexPropertySet
+✔️ Equality
+✔️ ImplicitCast
+✔️ Name
+✔️ SimpleFieldGet
+✔️ SimpleFieldSet
+✔️ SimplePropertyGet
+✔️ SimplePropertySet
+```
+### ✔️ <a id="user-content-r0s35" href="#r0s35">FluentValidation.Tests.MessageFormatterTests</a>
+```
+✔️ Adds_argument_and_custom_arguments
+✔️ Adds_formatted_argument_and_custom_arguments
+✔️ Adds_formatted_argument_and_formatted_custom_arguments
+✔️ Adds_PropertyName_to_message
+✔️ Adds_value_to_message
+✔️ Format_property_value
+✔️ Should_ignore_unknown_numbered_parameters
+✔️ Should_ignore_unknown_parameters
+✔️ Understands_date_formats
+✔️ Understands_numeric_formats
+```
+### ✔️ <a id="user-content-r0s36" href="#r0s36">FluentValidation.Tests.ModelLevelValidatorTests</a>
+```
+✔️ Can_use_child_validator_at_model_level
+✔️ Validates_at_model_level
+```
+### ✔️ <a id="user-content-r0s37" href="#r0s37">FluentValidation.Tests.NameResolutionPluggabilityTester</a>
+```
+✔️ Resolves_nested_properties
+✔️ ShouldHaveValidationError_Should_support_custom_propertynameresolver
+✔️ Uses_custom_property_name
+```
+### ✔️ <a id="user-content-r0s38" href="#r0s38">FluentValidation.Tests.NotEmptyTester</a>
+```
+✔️ Fails_for_array
+✔️ Fails_for_ienumerable_that_doesnt_implement_ICollection
+✔️ Fails_when_collection_empty
+✔️ When_there_is_a_value_then_the_validator_should_pass
+✔️ When_validation_fails_error_should_be_set
+✔️ When_value_is_Default_for_type_validator_should_fail_datetime
+✔️ When_value_is_Default_for_type_validator_should_fail_int
+✔️ When_value_is_empty_string_validator_should_fail
+✔️ When_value_is_null_validator_should_fail
+✔️ When_value_is_whitespace_validation_should_fail
+```
+### ✔️ <a id="user-content-r0s39" href="#r0s39">FluentValidation.Tests.NotEqualValidatorTests</a>
+```
+✔️ Comparison_property_uses_custom_resolver
+✔️ Should_handle_custom_value_types_correctly
+✔️ Should_not_be_valid_for_case_insensitve_comparison
+✔️ Should_not_be_valid_for_case_insensitve_comparison_with_expression
+✔️ Should_store_comparison_type
+✔️ Should_store_property_to_compare
+✔️ Should_use_ordinal_comparison_by_default
+✔️ Validates_across_properties
+✔️ When_the_objects_are_equal_then_the_validator_should_fail
+✔️ When_the_objects_are_not_equal_then_the_validator_should_pass
+✔️ When_the_validator_fails_the_error_message_should_be_set
+```
+### ✔️ <a id="user-content-r0s40" href="#r0s40">FluentValidation.Tests.NotNullTester</a>
+```
+✔️ Fails_when_nullable_value_type_is_null
+✔️ Not_null_validator_should_not_crash_with_non_nullable_value_type
+✔️ NotNullValidator_should_fail_if_value_is_null
+✔️ NotNullValidator_should_pass_if_value_has_value
+✔️ When_the_validator_fails_the_error_message_should_be_set
+```
+### ✔️ <a id="user-content-r0s41" href="#r0s41">FluentValidation.Tests.NullTester</a>
+```
+✔️ Not_null_validator_should_not_crash_with_non_nullable_value_type
+✔️ NullValidator_should_fail_if_value_has_value
+✔️ NullValidator_should_pass_if_value_is_null
+✔️ Passes_when_nullable_value_type_is_null
+✔️ When_the_validator_passes_the_error_message_should_be_set
+```
+### ✔️ <a id="user-content-r0s42" href="#r0s42">FluentValidation.Tests.OnFailureTests</a>
+```
+✔️ OnFailure_called_for_each_failed_rule
+✔️ OnFailure_called_for_each_failed_rule_asyncAsync
+✔️ Should_be_able_to_access_error_message_in_OnFailure
+✔️ ShouldHaveChildValidator_should_be_true
+✔️ ShouldHaveChildValidator_works_with_Include
+✔️ WhenAsyncWithOnFailure_should_invoke_condition_on_async_inner_validator
+✔️ WhenAsyncWithOnFailure_should_invoke_condition_on_inner_validator
+✔️ WhenAsyncWithOnFailure_should_invoke_condition_on_inner_validator_invoked_synchronously
+✔️ WhenWithOnFailure_should_invoke_condition_on_async_inner_validator
+✔️ WhenWithOnFailure_should_invoke_condition_on_inner_validator
+```
+### ✔️ <a id="user-content-r0s43" href="#r0s43">FluentValidation.Tests.PredicateValidatorTester</a>
+```
+✔️ Should_fail_when_predicate_returns_false
+✔️ Should_succeed_when_predicate_returns_true
+✔️ Should_throw_when_predicate_is_null
+✔️ When_validation_fails_metadata_should_be_set_on_failure
+✔️ When_validation_fails_the_default_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s44" href="#r0s44">FluentValidation.Tests.PropertyChainTests</a>
+```
+✔️ AddIndexer_throws_when_nothing_added
+✔️ Calling_ToString_should_construct_string_representation_of_chain
+✔️ Calling_ToString_should_construct_string_representation_of_chain_with_indexers
+✔️ Creates_from_expression
+✔️ Should_be_subchain
+✔️ Should_ignore_blanks
+✔️ Should_not_be_subchain
+```
+### ✔️ <a id="user-content-r0s45" href="#r0s45">FluentValidation.Tests.RegularExpressionValidatorTests</a>
+```
+✔️ Can_access_expression_in_message
+✔️ Can_access_expression_in_message_lambda
+✔️ Can_access_expression_in_message_lambda_regex
+✔️ Uses_lazily_loaded_expression
+✔️ Uses_lazily_loaded_expression_with_options
+✔️ Uses_regex_object
+✔️ When_the_text_does_not_match_the_lambda_regex_regular_expression_then_the_validator_should_fail
+✔️ When_the_text_does_not_match_the_lambda_regular_expression_then_the_validator_should_fail
+✔️ When_the_text_does_not_match_the_regular_expression_then_the_validator_should_fail
+✔️ When_the_text_is_empty_then_the_validator_should_fail
+✔️ When_the_text_is_null_then_the_validator_should_pass
+✔️ When_the_text_matches_the_lambda_regex_regular_expression_then_the_validator_should_pass
+✔️ When_the_text_matches_the_lambda_regular_expression_then_the_validator_should_pass
+✔️ When_the_text_matches_the_regular_expression_then_the_validator_should_pass
+✔️ When_validation_fails_the_default_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s46" href="#r0s46">FluentValidation.Tests.RuleBuilderTests</a>
+```
+✔️ Adding_a_validator_should_return_builder
+✔️ Adding_a_validator_should_store_validator
+✔️ Calling_validate_should_delegate_to_underlying_validator
+✔️ Calling_ValidateAsync_should_delegate_to_underlying_async_validator
+✔️ Calling_ValidateAsync_should_delegate_to_underlying_sync_validator
+✔️ Conditional_child_validator_should_register_with_validator_type_not_property
+✔️ Nullable_object_with_async_condition_should_not_throw
+✔️ Nullable_object_with_condition_should_not_throw
+✔️ Property_should_return_null_when_it_is_not_a_property_being_validated
+✔️ Property_should_return_property_being_validated
+✔️ PropertyDescription_should_return_custom_property_name
+✔️ PropertyDescription_should_return_property_name_split
+✔️ Result_should_use_custom_property_name_when_no_property_name_can_be_determined
+✔️ Rule_for_a_non_memberexpression_should_not_generate_property_name
+✔️ Should_build_property_name
+✔️ Should_compile_expression
+✔️ Should_set_custom_error
+✔️ Should_set_custom_property_name
+✔️ Should_throw_if_message_is_null
+✔️ Should_throw_if_overriding_validator_is_null
+✔️ Should_throw_if_overriding_validator_provider_is_null
+✔️ Should_throw_if_property_name_is_null
+✔️ Should_throw_if_validator_is_null
+✔️ Should_throw_when_async_inverse_predicate_is_null
+✔️ Should_throw_when_async_predicate_is_null
+✔️ Should_throw_when_context_predicate_is_null
+✔️ Should_throw_when_inverse_context_predicate_is_null
+✔️ Should_throw_when_inverse_predicate_is_null
+✔️ Should_throw_when_predicate_is_null
+```
+### ✔️ <a id="user-content-r0s47" href="#r0s47">FluentValidation.Tests.RuleDependencyTests</a>
+```
+✔️ Async_inside_dependent_rules
+✔️ Async_inside_dependent_rules_when_parent_rule_not_async
+✔️ Dependent_rules_inside_ruleset
+✔️ Dependent_rules_inside_when
+✔️ Does_not_invoke_dependent_rule_if_parent_rule_does_not_pass
+✔️ Invokes_dependent_rule_if_parent_rule_passes
+✔️ Nested_dependent_rules
+✔️ Nested_dependent_rules_inside_ruleset
+✔️ Nested_dependent_rules_inside_ruleset_inside_method
+✔️ Nested_dependent_rules_inside_ruleset_no_result_when_second_level_fails
+✔️ Nested_dependent_rules_inside_ruleset_no_result_when_top_level_fails
+✔️ TestAsyncWithDependentRules_AsyncEntry
+✔️ TestAsyncWithDependentRules_SyncEntry
+✔️ Treats_root_level_RuleFor_call_as_dependent_rule_if_user_forgets_to_use_DependentRulesBuilder
+```
+### ✔️ <a id="user-content-r0s48" href="#r0s48">FluentValidation.Tests.RulesetTests</a>
+```
+✔️ Applies_multiple_rulesets_to_rule
+✔️ Combines_rulesets_and_explicit_properties
+✔️ Combines_rulesets_and_explicit_properties_async
+✔️ Executes_all_rules
+✔️ Executes_in_rule_in_default_and_none
+✔️ Executes_in_rule_in_ruleset_and_default
+✔️ Executes_multiple_rulesets
+✔️ Executes_rules_in_default_ruleset_and_specific_ruleset
+✔️ Executes_rules_in_specified_ruleset
+✔️ Executes_rules_not_specified_in_ruleset
+✔️ Includes_all_rulesets
+✔️ Includes_all_rulesets_async
+✔️ Includes_combination_of_rulesets
+✔️ Includes_combination_of_rulesets_async
+✔️ Ruleset_cascades_to_child_collection_validator
+✔️ Ruleset_cascades_to_child_validator
+✔️ Ruleset_selection_should_cascade_downwards_with_when_setting_child_validator_using_include_statement
+✔️ Ruleset_selection_should_cascade_downwards_with_when_setting_child_validator_using_include_statement_with_lambda
+✔️ Ruleset_selection_should_not_cascade_downwards_when_set_on_property
+✔️ Trims_spaces
+✔️ WithMessage_works_inside_rulesets
+```
+### ✔️ <a id="user-content-r0s49" href="#r0s49">FluentValidation.Tests.ScalePrecisionValidatorTests</a>
+```
+✔️ Scale_precision_should_be_valid
+✔️ Scale_precision_should_be_valid_when_ignoring_trailing_zeroes
+✔️ Scale_precision_should_be_valid_when_they_are_equal
+✔️ Scale_precision_should_not_be_valid
+✔️ Scale_precision_should_not_be_valid_when_ignoring_trailing_zeroes
+✔️ Scale_precision_should_not_be_valid_when_they_are_equal
+```
+### ✔️ <a id="user-content-r0s50" href="#r0s50">FluentValidation.Tests.SharedConditionTests</a>
+```
+✔️ Async_condition_can_be_used_inside_ruleset
+✔️ Condition_can_be_used_inside_ruleset
+✔️ Does_not_execute_custom_Rule_when_async_condition_false
+✔️ Does_not_execute_custom_Rule_when_condition_false
+✔️ Does_not_execute_customasync_Rule_when_async_condition_false
+✔️ Does_not_execute_customasync_Rule_when_condition_false
+✔️ Doesnt_throw_NullReferenceException_when_instance_not_null
+✔️ Doesnt_throw_NullReferenceException_when_instance_not_null_async
+✔️ Executes_custom_rule_when_async_condition_true
+✔️ Executes_custom_rule_when_condition_true
+✔️ Executes_customasync_rule_when_async_condition_true
+✔️ Executes_customasync_rule_when_condition_true
+✔️ Nested_async_conditions_with_Custom_rule
+✔️ Nested_async_conditions_with_CustomAsync_rule
+✔️ Nested_conditions_with_Custom_rule
+✔️ Nested_conditions_with_CustomAsync_rule
+✔️ Outer_async_Unless_clause_will_trump_an_inner_Unless_clause_when_inner_fails_but_the_outer_is_satisfied
+✔️ Outer_Unless_clause_will_trump_an_inner_Unless_clause_when_inner_fails_but_the_outer_is_satisfied
+✔️ Rules_invoke_when_inverse_shared_async_condition_matches
+✔️ Rules_invoke_when_inverse_shared_condition_matches
+✔️ Rules_not_invoked_when_inverse_shared_async_condition_does_not_match
+✔️ Rules_not_invoked_when_inverse_shared_condition_does_not_match
+✔️ RuleSet_can_be_used_inside_async_condition
+✔️ RuleSet_can_be_used_inside_condition
+✔️ Runs_otherwise_conditions_for_UnlessAsync
+✔️ Runs_otherwise_conditions_for_When
+✔️ Runs_otherwise_conditions_for_WhenAsync
+✔️ Runs_otherwise_conditons_for_Unless
+✔️ Shared_async_When_is_applied_to_groupd_rules_when_initial_predicate_is_true_and_all_individual_rules_are_satisfied
+✔️ Shared_async_When_is_applied_to_grouped_rules_when_initial_predicate_is_true
+✔️ Shared_async_When_is_not_applied_to_grouped_rules_when_initial_predicate_is_false
+✔️ Shared_async_When_respects_the_smaller_scope_of_a_inner_Unless_when_the_inner_Unless_predicate_fails
+✔️ Shared_async_When_respects_the_smaller_scope_of_an_inner_Unless_when_the_inner_Unless_predicate_is_satisfied
+✔️ Shared_When_is_applied_to_groupd_rules_when_initial_predicate_is_true_and_all_individual_rules_are_satisfied
+✔️ Shared_When_is_applied_to_grouped_rules_when_initial_predicate_is_true
+✔️ Shared_When_is_not_applied_to_grouped_rules_when_initial_predicate_is_false
+✔️ Shared_When_respects_the_smaller_scope_of_a_inner_Unless_when_the_inner_Unless_predicate_fails
+✔️ Shared_When_respects_the_smaller_scope_of_an_inner_Unless_when_the_inner_Unless_predicate_is_satisfied
+✔️ When_async_condition_executed_for_each_instance_of_RuleForEach_condition_should_not_be_cached
+✔️ When_condition_executed_for_each_instance_of_RuleForEach_condition_should_not_be_cached
+✔️ When_condition_only_executed_once
+✔️ WhenAsync_condition_only_executed_once
+```
+### ✔️ <a id="user-content-r0s51" href="#r0s51">FluentValidation.Tests.StandalonePropertyValidationTester</a>
+```
+✔️ Should_validate_property_value_without_instance
+```
+### ✔️ <a id="user-content-r0s52" href="#r0s52">FluentValidation.Tests.StringEnumValidatorTests</a>
+```
+✔️ IsValidTests_CaseInsensitive_CaseCorrect
+✔️ IsValidTests_CaseInsensitive_CaseIncorrect
+✔️ IsValidTests_CaseSensitive_CaseCorrect
+✔️ IsValidTests_CaseSensitive_CaseIncorrect
+✔️ When_enumType_is_not_an_enum_it_should_throw
+✔️ When_enumType_is_null_it_should_throw
+✔️ When_the_property_is_initialized_with_empty_string_then_the_validator_should_fail
+✔️ When_the_property_is_initialized_with_invalid_string_then_the_validator_should_fail
+✔️ When_the_property_is_initialized_with_null_then_the_validator_should_be_valid
+✔️ When_validation_fails_the_default_error_should_be_set
+```
+### ✔️ <a id="user-content-r0s53" href="#r0s53">FluentValidation.Tests.TrackingCollectionTests</a>
+```
+✔️ Add_AddsItem
+✔️ Should_not_raise_event_once_handler_detached
+✔️ When_Item_Added_Raises_ItemAdded
+```
+### ✔️ <a id="user-content-r0s54" href="#r0s54">FluentValidation.Tests.TransformTests</a>
+```
+✔️ Transforms_collection_element
+✔️ Transforms_collection_element_async
+✔️ Transforms_property_value
+✔️ Transforms_property_value_to_another_type
+```
+### ✔️ <a id="user-content-r0s55" href="#r0s55">FluentValidation.Tests.UserSeverityTester</a>
+```
+✔️ Can_Provide_conditional_severity
+✔️ Can_Provide_severity_for_item_in_collection
+✔️ Correctly_provides_object_being_validated
+✔️ Defaults_user_severity_to_error
+✔️ Should_use_last_supplied_severity
+✔️ Stores_user_severity_against_validation_failure
+✔️ Throws_when_provider_is_null
+```
+### ✔️ <a id="user-content-r0s56" href="#r0s56">FluentValidation.Tests.UserStateTester</a>
+```
+✔️ Can_Provide_state_for_item_in_collection
+✔️ Correctly_provides_object_being_validated
+✔️ Stores_user_state_against_validation_failure
+✔️ Throws_when_provider_is_null
+```
+### ✔️ <a id="user-content-r0s57" href="#r0s57">FluentValidation.Tests.ValidateAndThrowTester</a>
+```
+✔️ Does_not_throw_when_valid
+✔️ Does_not_throw_when_valid_and_a_ruleset
+✔️ Does_not_throw_when_valid_and_a_ruleset_async
+✔️ Does_not_throw_when_valid_async
+✔️ Only_root_validator_throws
+✔️ Populates_errors
+✔️ Serializes_exception
+✔️ Throws_exception
+✔️ Throws_exception_async
+✔️ Throws_exception_with_a_ruleset
+✔️ Throws_exception_with_a_ruleset_async
+✔️ ToString_provides_error_details
+✔️ ValidationException_provides_correct_message_when_appendDefaultMessage_false
+✔️ ValidationException_provides_correct_message_when_appendDefaultMessage_true
+```
+### ✔️ <a id="user-content-r0s58" href="#r0s58">FluentValidation.Tests.ValidationResultTests</a>
+```
+✔️ Can_serialize_failure
+✔️ Can_serialize_result
+✔️ Should_add_errors
+✔️ Should_be_valid_when_there_are_no_errors
+✔️ Should_not_be_valid_when_there_are_errors
+✔️ ToString_return_empty_string_when_there_is_no_error
+✔️ ToString_return_error_messages_with_given_separator
+✔️ ToString_return_error_messages_with_newline_as_separator
+```
+### ✔️ <a id="user-content-r0s59" href="#r0s59">FluentValidation.Tests.ValidatorDescriptorTester</a>
+```
+✔️ Does_not_throw_when_rule_declared_without_property
+✔️ Gets_validators_for_property
+✔️ GetValidatorsForMember_and_GetRulesForMember_can_both_retrieve_for_model_level_rule
+✔️ Returns_empty_collection_for_property_with_no_validators
+✔️ Should_retrieve_name_given_to_it_pass_property_as_string
+```
+### ✔️ <a id="user-content-r0s60" href="#r0s60">FluentValidation.Tests.ValidatorSelectorTests</a>
+```
+✔️ Can_use_property_with_include
+✔️ Does_not_validate_other_property
+✔️ Does_not_validate_other_property_using_expression
+✔️ Executes_correct_rule_when_using_property_with_include
+✔️ Executes_correct_rule_when_using_property_with_include_async
+✔️ Includes_nested_property
+✔️ Includes_nested_property_using_expression
+✔️ MemberNameValidatorSelector_returns_true_when_property_name_matches
+✔️ Validates_nullable_property_with_overriden_name_when_selected
+✔️ Validates_property_using_expression
+```
+### ✔️ <a id="user-content-r0s61" href="#r0s61">FluentValidation.Tests.ValidatorTesterTester</a>
+```
+✔️ Allows_only_one_failure_to_match
+✔️ Can_use_indexer_in_string_message
+✔️ Can_use_indexer_in_string_message_inverse
+✔️ Expected_error_code_check
+✔️ Expected_message_argument_check
+✔️ Expected_message_check
+✔️ Expected_severity_check
+✔️ Expected_state_check
+✔️ Matches_any_failure
+✔️ Matches_model_level_rule
+✔️ Model_level_check_fails_if_no_model_level_failures
+✔️ ShouldHaveChildValidator_should_not_throw_when_property_Does_have_child_validator
+✔️ ShouldHaveChildValidator_should_not_throw_when_property_Does_have_child_validator_and_expecting_a_basetype
+✔️ ShouldHaveChildValidator_should_not_throw_when_property_has_collection_validators
+✔️ ShouldHaveChildValidator_should_throw_when_property_has_a_different_child_validator
+✔️ ShouldHaveChildValidator_should_work_with_DependentRules
+✔️ ShouldHaveChildvalidator_throws_when_collection_property_Does_not_have_child_validator
+✔️ ShouldHaveChildValidator_throws_when_property_does_not_have_child_validator
+✔️ ShouldHaveChildValidator_works_on_model_level_rules
+✔️ ShouldHaveValidationError_async
+✔️ ShouldHaveValidationError_async_throws
+✔️ ShouldHaveValidationError_model_async
+✔️ ShouldHaveValidationError_model_async_throws
+✔️ ShouldHaveValidationError_preconstructed_object_does_not_throw_for_unwritable_property
+✔️ ShouldHaveValidationError_should_not_throw_when_there_are_errors_with_preconstructed_object
+✔️ ShouldHaveValidationError_should_not_throw_when_there_are_validation_errors
+✔️ ShouldHaveValidationError_should_not_throw_when_there_are_validation_errors__WhenAsyn_is_used(age: 42, cardNumber: "")
+✔️ ShouldHaveValidationError_should_not_throw_when_there_are_validation_errors__WhenAsyn_is_used(age: 42, cardNumber: null)
+✔️ ShouldHaveValidationError_should_not_throw_when_there_are_validation_errors_ruleforeach
+✔️ ShouldHaveValidationError_Should_support_nested_properties
+✔️ ShouldHaveValidationError_Should_throw_when_there_are_no_validation_errors
+✔️ ShouldHaveValidationError_should_throw_when_there_are_no_validation_errors_with_preconstructed_object
+✔️ ShouldHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_Is_Used(age: 17, cardNumber: "")
+✔️ ShouldHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_Is_Used(age: 17, cardNumber: "cardNumber")
+✔️ ShouldHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_Is_Used(age: 17, cardNumber: null)
+✔️ ShouldHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_Is_Used(age: 42, cardNumber: "cardNumber")
+✔️ ShouldHaveValidationError_should_throw_when_there_are_not_validation_errors_ruleforeach
+✔️ ShouldHaveValidationError_with_an_unmatched_rule_and_a_single_error_should_throw_an_exception
+✔️ ShouldHaveValidationError_with_an_unmatched_rule_and_multiple_errors_should_throw_an_exception
+✔️ ShouldHaveValidationErrorFor_takes_account_of_rulesets
+✔️ ShouldHaveValidationErrorFor_takes_account_of_rulesets_fluent_approach
+✔️ ShouldNotHaveValidationError_async
+✔️ ShouldNotHaveValidationError_async_model_throws
+✔️ ShouldNotHaveValidationError_async_throws
+✔️ ShouldNotHaveValidationError_model_async
+✔️ ShouldNotHaveValidationError_should_correctly_handle_explicitly_providing_object_to_validate
+✔️ ShouldNotHaveValidationError_should_correctly_handle_explicitly_providing_object_to_validate_and_other_property_fails_validation
+✔️ ShouldNotHaveValidationError_should_have_validation_error_details_when_thrown_ruleforeach
+✔️ ShouldNotHaveValidationError_should_not_throw_when_there_are_no_errors
+✔️ ShouldNotHAveValidationError_should_not_throw_When_there_are_no_errors_with_preconstructed_object
+✔️ ShouldNotHaveValidationError_should_not_throw_when_there_are_not_validation_errors_ruleforeach
+✔️ ShouldNotHaveValidationError_Should_support_nested_properties
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_errors
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_errors_with_preconstructed_object
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_is_used(age: 17, cardNumber: "")
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_is_used(age: 17, cardNumber: "cardNumber")
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_is_used(age: 17, cardNumber: null)
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_not_validation_errors__WhenAsyn_is_used(age: 42, cardNumber: "cardNumber")
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_validation_errors__WhenAsyn_is_used(age: 42, cardNumber: "")
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_validation_errors__WhenAsyn_is_used(age: 42, cardNumber: null)
+✔️ ShouldNotHaveValidationError_should_throw_when_there_are_validation_errors_ruleforeach
+✔️ Tests_nested_property
+✔️ Tests_nested_property_reverse
+✔️ Tests_nested_property_using_obsolete_method
+✔️ TestValidate_runs_async
+✔️ TestValidate_runs_async_throws
+✔️ Unexpected_error_code_check
+✔️ Unexpected_message_check(withoutErrMsg: "bar", errMessages: ["bar"])
+✔️ Unexpected_message_check(withoutErrMsg: "bar", errMessages: ["foo", "bar"])
+✔️ Unexpected_message_check(withoutErrMsg: "bar", errMessages: ["foo"])
+✔️ Unexpected_message_check(withoutErrMsg: "bar", errMessages: [])
+✔️ Unexpected_severity_check
+✔️ Unexpected_state_check
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/jest-junit.md b/test-reporter/__tests__/__outputs__/jest-junit.md
new file mode 100644
index 0000000..5aecf5d
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/jest-junit.md
@@ -0,0 +1,26 @@
+![Tests failed](https://img.shields.io/badge/tests-1%20passed%2C%204%20failed%2C%201%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/jest-junit.xml</a>
+**6** tests were completed in **1s** with **1** passed, **4** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[__tests__\main.test.js](#r0s0)|1✔️|3❌||486ms|
+|[__tests__\second.test.js](#r0s1)||1❌|1✖️|82ms|
+### ❌ <a id="user-content-r0s0" href="#r0s0">__tests__\main.test.js</a>
+```
+Test 1
+  ✔️ Passing test
+Test 1 › Test 1.1
+  ❌ Failing test
+	Error: expect(received).toBeTruthy()
+  ❌ Exception in target unit
+	Error: Some error
+Test 2
+  ❌ Exception in test
+	Error: Some error
+```
+### ❌ <a id="user-content-r0s1" href="#r0s1">__tests__\second.test.js</a>
+```
+❌ Timeout test
+	: Timeout - Async callback was not invoked within the 1 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 1 ms timeout specified by jest.setTimeout.Error:
+✖️ Skipped test
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/jest-test-results.md b/test-reporter/__tests__/__outputs__/jest-test-results.md
new file mode 100644
index 0000000..ed545bf
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/jest-test-results.md
@@ -0,0 +1,432 @@
+![Tests failed](https://img.shields.io/badge/tests-4207%20passed%2C%202%20failed%2C%2030%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/external/jest/jest-test-results.xml</a>
+**4239** tests were completed in **166s** with **4207** passed, **2** failed and **30** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|e2e/__tests__/asyncAndCallback.test.ts|1✔️|||746ms|
+|e2e/__tests__/asyncRegenerator.test.ts|1✔️|||4s|
+|e2e/__tests__/autoClearMocks.test.ts|2✔️|||2s|
+|e2e/__tests__/autoResetMocks.test.ts|2✔️|||2s|
+|e2e/__tests__/autoRestoreMocks.test.ts|2✔️|||2s|
+|e2e/__tests__/babelPluginJestHoist.test.ts|1✔️|||6s|
+|e2e/__tests__/badSourceMap.test.ts|1✔️|||858ms|
+|e2e/__tests__/beforeAllFiltered.ts|1✔️|||958ms|
+|e2e/__tests__/beforeEachQueue.ts|1✔️||1✖️|55ms|
+|e2e/__tests__/callDoneTwice.test.ts|1✔️|||882ms|
+|e2e/__tests__/chaiAssertionLibrary.ts|1✔️|||2s|
+|e2e/__tests__/circularInequality.test.ts|1✔️|||1s|
+|e2e/__tests__/circusConcurrentEach.test.ts|2✔️|||2s|
+|e2e/__tests__/circusDeclarationErrors.test.ts|1✔️|||869ms|
+|e2e/__tests__/clearCache.test.ts|2✔️|||1s|
+|e2e/__tests__/cliHandlesExactFilenames.test.ts|2✔️|||1s|
+|e2e/__tests__/compareDomNodes.test.ts|1✔️|||1s|
+|e2e/__tests__/config.test.ts|6✔️|||4s|
+|e2e/__tests__/console.test.ts|7✔️|||8s|
+|e2e/__tests__/consoleAfterTeardown.test.ts|1✔️|||1s|
+|e2e/__tests__/consoleLogOutputWhenRunInBand.test.ts|1✔️|||793ms|
+|e2e/__tests__/coverageHandlebars.test.ts|1✔️|||2s|
+|e2e/__tests__/coverageRemapping.test.ts|1✔️|||13s|
+|e2e/__tests__/coverageReport.test.ts|12✔️|||22s|
+|e2e/__tests__/coverageThreshold.test.ts|5✔️|||5s|
+|e2e/__tests__/coverageTransformInstrumented.test.ts|1✔️|||5s|
+|e2e/__tests__/coverageWithoutTransform.test.ts|1✔️|||1s|
+|e2e/__tests__/createProcessObject.test.ts|1✔️|||908ms|
+|e2e/__tests__/customInlineSnapshotMatchers.test.ts|1✔️|||2s|
+|e2e/__tests__/customMatcherStackTrace.test.ts|2✔️|||2s|
+|e2e/__tests__/customReporters.test.ts|9✔️|||7s|
+|e2e/__tests__/customResolver.test.ts|1✔️|||826ms|
+|e2e/__tests__/customTestSequencers.test.ts|3✔️|||3s|
+|e2e/__tests__/debug.test.ts|1✔️|||899ms|
+|e2e/__tests__/declarationErrors.test.ts|3✔️|||2s|
+|e2e/__tests__/dependencyClash.test.ts|1✔️|||833ms|
+|e2e/__tests__/detectOpenHandles.ts|8✔️|||8s|
+|e2e/__tests__/domDiffing.test.ts|1✔️|||1s|
+|e2e/__tests__/doneInHooks.test.ts|1✔️|||855ms|
+|e2e/__tests__/dynamicRequireDependencies.ts|1✔️|||847ms|
+|e2e/__tests__/each.test.ts|7✔️|||5s|
+|e2e/__tests__/emptyDescribeWithHooks.test.ts|4✔️|||3s|
+|e2e/__tests__/emptySuiteError.test.ts|1✔️|||885ms|
+|e2e/__tests__/env.test.ts|6✔️|||5s|
+|e2e/__tests__/environmentAfterTeardown.test.ts|1✔️|||892ms|
+|e2e/__tests__/errorOnDeprecated.test.ts|1✔️||24✖️|56ms|
+|e2e/__tests__/esmConfigFile.test.ts|3✔️|||526ms|
+|e2e/__tests__/executeTestsOnceInMpr.ts|1✔️|||976ms|
+|e2e/__tests__/existentRoots.test.ts|4✔️|||627ms|
+|e2e/__tests__/expectAsyncMatcher.test.ts|2✔️|||3s|
+|e2e/__tests__/expectInVm.test.ts|1✔️|||2s|
+|e2e/__tests__/extraGlobals.test.ts|1✔️|||1s|
+|e2e/__tests__/failureDetailsProperty.test.ts|1✔️|||907ms|
+|e2e/__tests__/failures.test.ts|7✔️|||10s|
+|e2e/__tests__/fakePromises.test.ts|2✔️|||2s|
+|e2e/__tests__/fatalWorkerError.test.ts|1✔️|||3s|
+|e2e/__tests__/filter.test.ts|7✔️|||5s|
+|e2e/__tests__/findRelatedFiles.test.ts|5✔️|||6s|
+|e2e/__tests__/focusedTests.test.ts|1✔️|||888ms|
+|e2e/__tests__/forceExit.test.ts|1✔️|||2s|
+|e2e/__tests__/generatorMock.test.ts|1✔️|||1s|
+|e2e/__tests__/global-mutation.test.ts|1✔️|||40ms|
+|e2e/__tests__/global.test.ts|1✔️|||31ms|
+|e2e/__tests__/globals.test.ts|10✔️|||8s|
+|e2e/__tests__/globalSetup.test.ts|10✔️|||14s|
+|e2e/__tests__/globalTeardown.test.ts|7✔️|||12s|
+|e2e/__tests__/hasteMapMockChanged.test.ts|1✔️|||379ms|
+|e2e/__tests__/hasteMapSha1.test.ts|1✔️|||298ms|
+|e2e/__tests__/hasteMapSize.test.ts|2✔️|||397ms|
+|e2e/__tests__/importedGlobals.test.ts|1✔️|||1s|
+|e2e/__tests__/injectGlobals.test.ts|2✔️|||2s|
+|e2e/__tests__/jasmineAsync.test.ts|15✔️|||28s|
+|e2e/__tests__/jasmineAsyncWithPendingDuringTest.ts|1✔️||1✖️|72ms|
+|e2e/__tests__/jest.config.js.test.ts|3✔️|||2s|
+|e2e/__tests__/jest.config.ts.test.ts|5✔️|||14s|
+|[e2e/__tests__/jestChangedFiles.test.ts](#r0s75)|9✔️|1❌||9s|
+|e2e/__tests__/jestEnvironmentJsdom.test.ts|1✔️|||2s|
+|e2e/__tests__/jestRequireActual.test.ts|1✔️|||2s|
+|e2e/__tests__/jestRequireMock.test.ts|1✔️|||2s|
+|e2e/__tests__/json.test.ts|2✔️|||29ms|
+|e2e/__tests__/jsonReporter.test.ts|2✔️|||2s|
+|e2e/__tests__/lifecycles.ts|1✔️|||861ms|
+|e2e/__tests__/listTests.test.ts|2✔️|||945ms|
+|e2e/__tests__/locationInResults.test.ts|2✔️|||2s|
+|e2e/__tests__/logHeapUsage.test.ts|1✔️|||884ms|
+|e2e/__tests__/mockNames.test.ts|8✔️|||7s|
+|e2e/__tests__/modernFakeTimers.test.ts|2✔️|||2s|
+|e2e/__tests__/moduleNameMapper.test.ts|5✔️|||5s|
+|e2e/__tests__/moduleParentNullInTest.ts|1✔️|||886ms|
+|e2e/__tests__/multiProjectRunner.test.ts|14✔️|||16s|
+|e2e/__tests__/nativeAsyncMock.test.ts|1✔️|||55ms|
+|e2e/__tests__/nativeEsm.test.ts|2✔️||1✖️|905ms|
+|e2e/__tests__/nativeEsmTypescript.test.ts|1✔️|||956ms|
+|e2e/__tests__/nestedEventLoop.test.ts|1✔️|||1s|
+|e2e/__tests__/nestedTestDefinitions.test.ts|4✔️|||5s|
+|e2e/__tests__/nodePath.test.ts|1✔️|||866ms|
+|e2e/__tests__/noTestFound.test.ts|2✔️|||1s|
+|e2e/__tests__/noTestsFound.test.ts|5✔️|||3s|
+|[e2e/__tests__/onlyChanged.test.ts](#r0s98)|8✔️|1❌||22s|
+|e2e/__tests__/onlyFailuresNonWatch.test.ts|1✔️|||3s|
+|e2e/__tests__/overrideGlobals.test.ts|2✔️|||2s|
+|e2e/__tests__/pnp.test.ts|1✔️|||3s|
+|e2e/__tests__/presets.test.ts|2✔️|||2s|
+|e2e/__tests__/processExit.test.ts|1✔️|||1s|
+|e2e/__tests__/promiseReject.test.ts|1✔️|||967ms|
+|e2e/__tests__/regexCharInPath.test.ts|1✔️|||962ms|
+|e2e/__tests__/requireAfterTeardown.test.ts|1✔️|||921ms|
+|e2e/__tests__/requireMain.test.ts|1✔️|||1s|
+|e2e/__tests__/requireMainAfterCreateRequire.test.ts|1✔️|||966ms|
+|e2e/__tests__/requireMainIsolateModules.test.ts|1✔️|||976ms|
+|e2e/__tests__/requireMainResetModules.test.ts|2✔️|||2s|
+|e2e/__tests__/requireV8Module.test.ts|1✔️|||30ms|
+|e2e/__tests__/resetModules.test.ts|1✔️|||926ms|
+|e2e/__tests__/resolve.test.ts|1✔️|||2s|
+|e2e/__tests__/resolveGetPaths.test.ts|1✔️|||1s|
+|e2e/__tests__/resolveNodeModule.test.ts|1✔️|||943ms|
+|e2e/__tests__/resolveNoFileExtensions.test.ts|2✔️|||1s|
+|e2e/__tests__/resolveWithPaths.test.ts|1✔️|||1s|
+|e2e/__tests__/runProgrammatically.test.ts|2✔️|||575ms|
+|e2e/__tests__/runTestsByPath.test.ts|1✔️|||2s|
+|e2e/__tests__/runtimeInternalModuleRegistry.test.ts|1✔️|||1s|
+|e2e/__tests__/selectProjects.test.ts|18✔️|||5s|
+|e2e/__tests__/setImmediate.test.ts|1✔️|||904ms|
+|e2e/__tests__/setupFilesAfterEnvConfig.test.ts|2✔️|||2s|
+|e2e/__tests__/showConfig.test.ts|1✔️|||195ms|
+|e2e/__tests__/skipBeforeAfterAll.test.ts|1✔️|||1s|
+|e2e/__tests__/snapshot-unknown.test.ts|1✔️|||838ms|
+|e2e/__tests__/snapshot.test.ts|9✔️|||14s|
+|e2e/__tests__/snapshotMockFs.test.ts|1✔️|||883ms|
+|e2e/__tests__/snapshotResolver.test.ts|1✔️|||823ms|
+|e2e/__tests__/snapshotSerializers.test.ts|2✔️|||2s|
+|e2e/__tests__/stackTrace.test.ts|7✔️|||5s|
+|e2e/__tests__/stackTraceNoCaptureStackTrace.test.ts|1✔️|||899ms|
+|e2e/__tests__/stackTraceSourceMaps.test.ts|1✔️|||2s|
+|e2e/__tests__/stackTraceSourceMapsWithCoverage.test.ts|1✔️|||2s|
+|e2e/__tests__/supportsDashedArgs.ts|2✔️|||968ms|
+|e2e/__tests__/symbol.test.ts|1✔️|||49ms|
+|e2e/__tests__/testEnvironment.test.ts|1✔️|||2s|
+|e2e/__tests__/testEnvironmentAsync.test.ts|1✔️|||1s|
+|e2e/__tests__/testEnvironmentCircus.test.ts|1✔️|||2s|
+|e2e/__tests__/testEnvironmentCircusAsync.test.ts|1✔️|||2s|
+|e2e/__tests__/testFailureExitCode.test.ts|2✔️|||4s|
+|e2e/__tests__/testInRoot.test.ts|1✔️|||1s|
+|e2e/__tests__/testNamePattern.test.ts|1✔️|||859ms|
+|e2e/__tests__/testNamePatternSkipped.test.ts|1✔️|||991ms|
+|e2e/__tests__/testPathPatternReporterMessage.test.ts|1✔️|||3s|
+|e2e/__tests__/testResultsProcessor.test.ts|1✔️|||910ms|
+|e2e/__tests__/testRetries.test.ts|4✔️|||3s|
+|e2e/__tests__/testTodo.test.ts|5✔️|||4s|
+|e2e/__tests__/timeouts.test.ts|4✔️|||4s|
+|e2e/__tests__/timeoutsLegacy.test.ts|1✔️||3✖️|71ms|
+|e2e/__tests__/timerResetMocks.test.ts|2✔️|||2s|
+|e2e/__tests__/timerUseRealTimers.test.ts|1✔️|||1s|
+|e2e/__tests__/toMatchInlineSnapshot.test.ts|12✔️|||24s|
+|e2e/__tests__/toMatchInlineSnapshotWithRetries.test.ts|3✔️|||5s|
+|e2e/__tests__/toMatchSnapshot.test.ts|9✔️|||17s|
+|e2e/__tests__/toMatchSnapshotWithRetries.test.ts|2✔️|||4s|
+|e2e/__tests__/toMatchSnapshotWithStringSerializer.test.ts|3✔️|||4s|
+|e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts|4✔️|||4s|
+|e2e/__tests__/toThrowErrorMatchingSnapshot.test.ts|5✔️|||4s|
+|e2e/__tests__/transform.test.ts|16✔️|||27s|
+|e2e/__tests__/transformLinkedModules.test.ts|1✔️|||783ms|
+|e2e/__tests__/typescriptCoverage.test.ts|1✔️|||3s|
+|e2e/__tests__/unexpectedToken.test.ts|3✔️|||3s|
+|e2e/__tests__/useStderr.test.ts|1✔️|||1s|
+|e2e/__tests__/v8Coverage.test.ts|2✔️|||2s|
+|e2e/__tests__/verbose.test.ts|1✔️|||683ms|
+|e2e/__tests__/version.test.ts|1✔️|||138ms|
+|e2e/__tests__/watchModeNoAccess.test.ts|1✔️|||4s|
+|e2e/__tests__/watchModeOnlyFailed.test.ts|1✔️|||1s|
+|e2e/__tests__/watchModePatterns.test.ts|2✔️|||4s|
+|e2e/__tests__/watchModeUpdateSnapshot.test.ts|1✔️|||1s|
+|e2e/__tests__/workerForceExit.test.ts|2✔️|||5s|
+|e2e/__tests__/wrongEnv.test.ts|5✔️|||4s|
+|e2e/custom-test-sequencer/a.test.js|1✔️|||29ms|
+|e2e/custom-test-sequencer/b.test.js|1✔️|||21ms|
+|e2e/custom-test-sequencer/c.test.js|1✔️|||42ms|
+|e2e/custom-test-sequencer/d.test.js|1✔️|||21ms|
+|e2e/custom-test-sequencer/e.test.js|1✔️|||27ms|
+|e2e/test-in-root/spec.js|1✔️|||19ms|
+|e2e/test-in-root/test.js|1✔️|||37ms|
+|e2e/timer-reset-mocks/after-reset-all-mocks/timerAndMock.test.js|2✔️|||30ms|
+|e2e/timer-reset-mocks/with-reset-mocks/timerWithMock.test.js|1✔️|||34ms|
+|e2e/v8-coverage/empty-sourcemap/test.ts|1✔️|||31ms|
+|examples/angular/app.component.spec.ts|3✔️|||654ms|
+|examples/angular/shared/data.service.spec.ts|2✔️|||431ms|
+|examples/angular/shared/sub.service.spec.ts|1✔️|||109ms|
+|examples/async/__tests__/user.test.js|8✔️|||96ms|
+|examples/automatic-mocks/__tests__/automock.test.js|2✔️|||74ms|
+|examples/automatic-mocks/__tests__/createMockFromModule.test.js|2✔️|||115ms|
+|examples/automatic-mocks/__tests__/disableAutomocking.test.js|1✔️|||24ms|
+|examples/enzyme/__tests__/CheckboxWithLabel-test.js|1✔️|||434ms|
+|examples/getting-started/sum.test.js|1✔️|||78ms|
+|examples/jquery/__tests__/display_user.test.js|1✔️|||196ms|
+|examples/jquery/__tests__/fetch_current_user.test.js|2✔️|||196ms|
+|examples/manual-mocks/__tests__/file_summarizer.test.js|1✔️|||87ms|
+|examples/manual-mocks/__tests__/lodashMocking.test.js|1✔️|||109ms|
+|examples/manual-mocks/__tests__/user.test.js|1✔️|||41ms|
+|examples/manual-mocks/__tests__/userMocked.test.js|1✔️|||105ms|
+|examples/module-mock/__tests__/full_mock.js|1✔️|||60ms|
+|examples/module-mock/__tests__/mock_per_test.js|2✔️|||116ms|
+|examples/module-mock/__tests__/partial_mock.js|1✔️|||215ms|
+|examples/mongodb/__test__/db.test.js|1✔️|||236ms|
+|examples/react-native/__tests__/intro.test.js|4✔️|||9s|
+|examples/react-testing-library/__tests__/CheckboxWithLabel-test.js|1✔️|||469ms|
+|examples/react/__tests__/CheckboxWithLabel-test.js|1✔️|||256ms|
+|examples/snapshot/__tests__/clock.react.test.js|1✔️|||62ms|
+|examples/snapshot/__tests__/link.react.test.js|4✔️|||181ms|
+|examples/timer/__tests__/infinite_timer_game.test.js|1✔️|||94ms|
+|examples/timer/__tests__/timer_game.test.js|3✔️|||74ms|
+|examples/typescript/__tests__/calc.test.ts|6✔️|||276ms|
+|examples/typescript/__tests__/CheckboxWithLabel-test.tsx|1✔️|||227ms|
+|examples/typescript/__tests__/sub-test.ts|1✔️|||43ms|
+|examples/typescript/__tests__/sum-test.ts|2✔️|||69ms|
+|examples/typescript/__tests__/sum.test.js|2✔️|||100ms|
+|packages/babel-jest/src/__tests__/index.ts|6✔️|||371ms|
+|packages/babel-plugin-jest-hoist/src/__tests__/hoistPlugin.test.ts|4✔️|||347ms|
+|packages/diff-sequences/src/__tests__/index.property.test.ts|7✔️|||357ms|
+|packages/diff-sequences/src/__tests__/index.test.ts|48✔️|||195ms|
+|packages/expect/src/__tests__/assertionCounts.test.ts|6✔️|||60ms|
+|packages/expect/src/__tests__/asymmetricMatchers.test.ts|38✔️|||207ms|
+|packages/expect/src/__tests__/extend.test.ts|10✔️|||99ms|
+|packages/expect/src/__tests__/isError.test.ts|4✔️|||43ms|
+|packages/expect/src/__tests__/matchers-toContain.property.test.ts|2✔️|||236ms|
+|packages/expect/src/__tests__/matchers-toContainEqual.property.test.ts|2✔️|||287ms|
+|packages/expect/src/__tests__/matchers-toEqual.property.test.ts|2✔️|||1s|
+|packages/expect/src/__tests__/matchers-toStrictEqual.property.test.ts|3✔️|||394ms|
+|packages/expect/src/__tests__/matchers.test.js|592✔️|||862ms|
+|packages/expect/src/__tests__/spyMatchers.test.ts|248✔️|||395ms|
+|packages/expect/src/__tests__/stacktrace.test.ts|3✔️|||69ms|
+|packages/expect/src/__tests__/symbolInObjects.test.ts|3✔️|||33ms|
+|packages/expect/src/__tests__/toEqual-dom.test.ts|12✔️|||99ms|
+|packages/expect/src/__tests__/toThrowMatchers.test.ts|98✔️|||257ms|
+|packages/expect/src/__tests__/utils.test.ts|41✔️|||147ms|
+|packages/jest-circus/src/__tests__/afterAll.test.ts|6✔️|||6s|
+|packages/jest-circus/src/__tests__/baseTest.test.ts|2✔️|||3s|
+|packages/jest-circus/src/__tests__/circusItTestError.test.ts|8✔️|||300ms|
+|packages/jest-circus/src/__tests__/circusItTodoTestError.test.ts|3✔️|||81ms|
+|packages/jest-circus/src/__tests__/hooks.test.ts|3✔️|||4s|
+|packages/jest-circus/src/__tests__/hooksError.test.ts|32✔️|||127ms|
+|packages/jest-cli/src/__tests__/cli/args.test.ts|17✔️|||345ms|
+|packages/jest-cli/src/init/__tests__/init.test.js|24✔️|||119ms|
+|packages/jest-cli/src/init/__tests__/modifyPackageJson.test.ts|4✔️|||30ms|
+|packages/jest-config/src/__tests__/Defaults.test.ts|1✔️|||672ms|
+|packages/jest-config/src/__tests__/getMaxWorkers.test.ts|7✔️|||67ms|
+|packages/jest-config/src/__tests__/normalize.test.js|118✔️|||798ms|
+|packages/jest-config/src/__tests__/readConfig.test.ts|1✔️|||76ms|
+|packages/jest-config/src/__tests__/readConfigs.test.ts|3✔️|||135ms|
+|packages/jest-config/src/__tests__/resolveConfigPath.test.ts|10✔️|||183ms|
+|packages/jest-config/src/__tests__/setFromArgv.test.ts|4✔️|||53ms|
+|packages/jest-config/src/__tests__/validatePattern.test.ts|4✔️|||52ms|
+|packages/jest-console/src/__tests__/bufferedConsole.test.ts|20✔️|||171ms|
+|packages/jest-console/src/__tests__/CustomConsole.test.ts|23✔️|||115ms|
+|packages/jest-console/src/__tests__/getConsoleOutput.test.ts|12✔️|||56ms|
+|packages/jest-core/src/__tests__/FailedTestsCache.test.js|1✔️|||25ms|
+|packages/jest-core/src/__tests__/getNoTestsFoundMessage.test.js|5✔️|||61ms|
+|packages/jest-core/src/__tests__/globals.test.ts|1✔️|||22ms|
+|packages/jest-core/src/__tests__/runJest.test.js|2✔️|||261ms|
+|packages/jest-core/src/__tests__/SearchSource.test.ts|27✔️|||3s|
+|packages/jest-core/src/__tests__/SnapshotInteractiveMode.test.js|13✔️|||89ms|
+|packages/jest-core/src/__tests__/TestScheduler.test.js|8✔️|||520ms|
+|packages/jest-core/src/__tests__/testSchedulerHelper.test.js|12✔️|||48ms|
+|packages/jest-core/src/__tests__/watch.test.js|80✔️|||7s|
+|packages/jest-core/src/__tests__/watchFileChanges.test.ts|1✔️|||2s|
+|packages/jest-core/src/__tests__/watchFilenamePatternMode.test.js|2✔️|||165ms|
+|packages/jest-core/src/__tests__/watchTestNamePatternMode.test.js|1✔️|||246ms|
+|packages/jest-core/src/lib/__tests__/isValidPath.test.ts|3✔️|||166ms|
+|packages/jest-core/src/lib/__tests__/logDebugMessages.test.ts|3✔️|||48ms|
+|packages/jest-create-cache-key-function/src/__tests__/index.test.ts|1✔️|||75ms|
+|packages/jest-diff/src/__tests__/diff.test.ts|107✔️|||625ms|
+|packages/jest-diff/src/__tests__/diffStringsRaw.test.ts|2✔️|||55ms|
+|packages/jest-diff/src/__tests__/getAlignedDiffs.test.ts|24✔️|||72ms|
+|packages/jest-diff/src/__tests__/joinAlignedDiffs.test.ts|6✔️|||44ms|
+|packages/jest-docblock/src/__tests__/index.test.ts|36✔️|||177ms|
+|packages/jest-each/src/__tests__/array.test.ts|159✔️|||192ms|
+|packages/jest-each/src/__tests__/index.test.ts|10✔️|||44ms|
+|packages/jest-each/src/__tests__/template.test.ts|242✔️|||483ms|
+|packages/jest-environment-jsdom/src/__tests__/jsdom_environment.test.ts|2✔️|||783ms|
+|packages/jest-environment-node/src/__tests__/node_environment.test.ts|6✔️|||184ms|
+|packages/jest-fake-timers/src/__tests__/legacyFakeTimers.test.ts|50✔️|||302ms|
+|packages/jest-fake-timers/src/__tests__/modernFakeTimers.test.ts|40✔️|||317ms|
+|packages/jest-get-type/src/__tests__/getType.test.ts|14✔️|||45ms|
+|packages/jest-get-type/src/__tests__/isPrimitive.test.ts|18✔️|||36ms|
+|packages/jest-globals/src/__tests__/index.ts|1✔️|||533ms|
+|packages/jest-haste-map/src/__tests__/get_mock_name.test.js|1✔️|||22ms|
+|packages/jest-haste-map/src/__tests__/includes_dotfiles.test.ts|1✔️|||337ms|
+|packages/jest-haste-map/src/__tests__/index.test.js|44✔️|||1s|
+|packages/jest-haste-map/src/__tests__/worker.test.js|7✔️|||100ms|
+|packages/jest-haste-map/src/crawlers/__tests__/node.test.js|10✔️|||170ms|
+|packages/jest-haste-map/src/crawlers/__tests__/watchman.test.js|8✔️|||153ms|
+|packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js|15✔️|||56ms|
+|packages/jest-haste-map/src/lib/__tests__/fast_path.test.js|5✔️|||29ms|
+|packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js|1✔️|||35ms|
+|packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js|2✔️|||31ms|
+|packages/jest-haste-map/src/lib/__tests__/normalizePathSep.test.js|2✔️|||35ms|
+|packages/jest-jasmine2/src/__tests__/concurrent.test.ts|3✔️|||24ms|
+|packages/jest-jasmine2/src/__tests__/expectationResultFactory.test.ts|7✔️|||70ms|
+|packages/jest-jasmine2/src/__tests__/hooksError.test.ts|32✔️|||51ms|
+|packages/jest-jasmine2/src/__tests__/iterators.test.ts|4✔️|||43ms|
+|packages/jest-jasmine2/src/__tests__/itTestError.test.ts|6✔️|||32ms|
+|packages/jest-jasmine2/src/__tests__/itToTestAlias.test.ts|1✔️|||23ms|
+|packages/jest-jasmine2/src/__tests__/pTimeout.test.ts|3✔️|||44ms|
+|packages/jest-jasmine2/src/__tests__/queueRunner.test.ts|6✔️|||93ms|
+|packages/jest-jasmine2/src/__tests__/reporter.test.ts|1✔️|||107ms|
+|packages/jest-jasmine2/src/__tests__/Suite.test.ts|1✔️|||84ms|
+|packages/jest-jasmine2/src/__tests__/todoError.test.ts|3✔️|||27ms|
+|packages/jest-leak-detector/src/__tests__/index.test.ts|6✔️|||986ms|
+|packages/jest-matcher-utils/src/__tests__/deepCyclicCopyReplaceable.test.ts|11✔️|||49ms|
+|packages/jest-matcher-utils/src/__tests__/deepCyclicCopyReplaceableDom.test.ts|2✔️|||48ms|
+|packages/jest-matcher-utils/src/__tests__/index.test.ts|48✔️|||391ms|
+|packages/jest-matcher-utils/src/__tests__/printDiffOrStringify.test.ts|21✔️|||114ms|
+|packages/jest-matcher-utils/src/__tests__/Replaceable.test.ts|17✔️|||111ms|
+|packages/jest-message-util/src/__tests__/messages.test.ts|11✔️|||205ms|
+|packages/jest-mock/src/__tests__/index.test.ts|84✔️|||509ms|
+|packages/jest-regex-util/src/__tests__/index.test.ts|8✔️|||56ms|
+|packages/jest-repl/src/__tests__/jest_repl.test.js|1✔️|||1s|
+|packages/jest-repl/src/__tests__/runtime_cli.test.js|4✔️|||4s|
+|packages/jest-reporters/src/__tests__/CoverageReporter.test.js|12✔️|||397ms|
+|packages/jest-reporters/src/__tests__/CoverageWorker.test.js|2✔️|||199ms|
+|packages/jest-reporters/src/__tests__/DefaultReporter.test.js|2✔️|||148ms|
+|packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js|3✔️|||1s|
+|packages/jest-reporters/src/__tests__/getResultHeader.test.js|4✔️|||30ms|
+|packages/jest-reporters/src/__tests__/getSnapshotStatus.test.js|3✔️|||28ms|
+|packages/jest-reporters/src/__tests__/getSnapshotSummary.test.js|4✔️|||49ms|
+|packages/jest-reporters/src/__tests__/getWatermarks.test.ts|2✔️|||37ms|
+|packages/jest-reporters/src/__tests__/NotifyReporter.test.ts|18✔️|||166ms|
+|packages/jest-reporters/src/__tests__/SummaryReporter.test.js|4✔️|||366ms|
+|packages/jest-reporters/src/__tests__/utils.test.ts|10✔️|||85ms|
+|packages/jest-reporters/src/__tests__/VerboseReporter.test.js|11✔️|||425ms|
+|packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts|11✔️|||666ms|
+|packages/jest-resolve/src/__tests__/isBuiltinModule.test.ts|4✔️|||36ms|
+|packages/jest-resolve/src/__tests__/resolve.test.ts|16✔️|||1s|
+|packages/jest-runner/src/__tests__/testRunner.test.ts|2✔️|||905ms|
+|packages/jest-runtime/src/__tests__/instrumentation.test.ts|1✔️|||275ms|
+|packages/jest-runtime/src/__tests__/runtime_create_mock_from_module.test.js|3✔️|||606ms|
+|packages/jest-runtime/src/__tests__/runtime_environment.test.js|2✔️|||497ms|
+|packages/jest-runtime/src/__tests__/runtime_internal_module.test.js|4✔️|||727ms|
+|packages/jest-runtime/src/__tests__/runtime_jest_fn.js|4✔️|||479ms|
+|packages/jest-runtime/src/__tests__/runtime_jest_spy_on.test.js|2✔️|||521ms|
+|packages/jest-runtime/src/__tests__/runtime_mock.test.js|4✔️|||743ms|
+|packages/jest-runtime/src/__tests__/runtime_module_directories.test.js|4✔️|||525ms|
+|packages/jest-runtime/src/__tests__/runtime_node_path.test.js|4✔️|||1s|
+|packages/jest-runtime/src/__tests__/runtime_require_actual.test.js|2✔️|||478ms|
+|packages/jest-runtime/src/__tests__/runtime_require_cache.test.js|2✔️|||454ms|
+|packages/jest-runtime/src/__tests__/runtime_require_mock.test.js|13✔️|||962ms|
+|packages/jest-runtime/src/__tests__/runtime_require_module_no_ext.test.js|1✔️|||261ms|
+|packages/jest-runtime/src/__tests__/runtime_require_module_or_mock_transitive_deps.test.js|6✔️|||2s|
+|packages/jest-runtime/src/__tests__/runtime_require_module_or_mock.test.js|17✔️|||1s|
+|packages/jest-runtime/src/__tests__/runtime_require_module.test.js|27✔️|||2s|
+|packages/jest-runtime/src/__tests__/runtime_require_resolve.test.ts|5✔️|||707ms|
+|packages/jest-runtime/src/__tests__/runtime_wrap.js|2✔️|||263ms|
+|packages/jest-runtime/src/__tests__/Runtime-sourceMaps.test.js|1✔️|||584ms|
+|packages/jest-runtime/src/__tests__/Runtime-statics.test.js|2✔️|||162ms|
+|packages/jest-serializer/src/__tests__/index.test.ts|17✔️|||158ms|
+|packages/jest-snapshot/src/__tests__/dedentLines.test.ts|17✔️|||94ms|
+|packages/jest-snapshot/src/__tests__/InlineSnapshots.test.ts|22✔️|||1s|
+|packages/jest-snapshot/src/__tests__/matcher.test.ts|1✔️|||131ms|
+|packages/jest-snapshot/src/__tests__/mockSerializer.test.ts|10✔️|||45ms|
+|packages/jest-snapshot/src/__tests__/printSnapshot.test.ts|71✔️|||1s|
+|packages/jest-snapshot/src/__tests__/SnapshotResolver.test.ts|10✔️|||98ms|
+|packages/jest-snapshot/src/__tests__/throwMatcher.test.ts|3✔️|||481ms|
+|packages/jest-snapshot/src/__tests__/utils.test.ts|26✔️|||214ms|
+|packages/jest-source-map/src/__tests__/getCallsite.test.ts|3✔️|||86ms|
+|packages/jest-test-result/src/__tests__/formatTestResults.test.ts|1✔️|||53ms|
+|packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js|8✔️|||251ms|
+|packages/jest-transform/src/__tests__/ScriptTransformer.test.ts|22✔️|||2s|
+|packages/jest-transform/src/__tests__/shouldInstrument.test.ts|25✔️|||155ms|
+|packages/jest-util/src/__tests__/createProcessObject.test.ts|4✔️|||81ms|
+|packages/jest-util/src/__tests__/deepCyclicCopy.test.ts|12✔️|||86ms|
+|packages/jest-util/src/__tests__/errorWithStack.test.ts|1✔️|||41ms|
+|packages/jest-util/src/__tests__/formatTime.test.ts|11✔️|||82ms|
+|packages/jest-util/src/__tests__/globsToMatcher.test.ts|4✔️|||56ms|
+|packages/jest-util/src/__tests__/installCommonGlobals.test.ts|2✔️|||68ms|
+|packages/jest-util/src/__tests__/isInteractive.test.ts|2✔️|||35ms|
+|packages/jest-util/src/__tests__/isPromise.test.ts|10✔️|||30ms|
+|packages/jest-validate/src/__tests__/validate.test.ts|23✔️|||283ms|
+|packages/jest-validate/src/__tests__/validateCLIOptions.test.js|6✔️|||83ms|
+|packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.ts|11✔️|||129ms|
+|packages/jest-watcher/src/lib/__tests__/prompt.test.ts|3✔️|||91ms|
+|packages/jest-watcher/src/lib/__tests__/scroll.test.ts|5✔️|||57ms|
+|packages/jest-worker/src/__tests__/Farm.test.js|10✔️|||158ms|
+|packages/jest-worker/src/__tests__/FifoQueue.test.js|3✔️|||48ms|
+|packages/jest-worker/src/__tests__/index.test.js|8✔️|||230ms|
+|packages/jest-worker/src/__tests__/PriorityQueue.test.js|5✔️|||63ms|
+|packages/jest-worker/src/__tests__/process-integration.test.js|5✔️|||62ms|
+|packages/jest-worker/src/__tests__/thread-integration.test.js|6✔️|||114ms|
+|packages/jest-worker/src/__tests__/WorkerPool.test.js|3✔️|||51ms|
+|packages/jest-worker/src/base/__tests__/BaseWorkerPool.test.js|11✔️|||653ms|
+|packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js|17✔️|||184ms|
+|packages/jest-worker/src/workers/__tests__/NodeThreadsWorker.test.js|15✔️|||258ms|
+|packages/jest-worker/src/workers/__tests__/processChild.test.js|10✔️|||135ms|
+|packages/jest-worker/src/workers/__tests__/threadChild.test.js|10✔️|||120ms|
+|packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts|38✔️|||137ms|
+|packages/pretty-format/src/__tests__/ConvertAnsi.test.ts|6✔️|||43ms|
+|packages/pretty-format/src/__tests__/DOMCollection.test.ts|10✔️|||64ms|
+|packages/pretty-format/src/__tests__/DOMElement.test.ts|28✔️|||148ms|
+|packages/pretty-format/src/__tests__/Immutable.test.ts|111✔️|||443ms|
+|packages/pretty-format/src/__tests__/prettyFormat.test.ts|86✔️|||219ms|
+|packages/pretty-format/src/__tests__/react.test.tsx|55✔️|||325ms|
+|packages/pretty-format/src/__tests__/ReactElement.test.ts|3✔️|||64ms|
+### ❌ <a id="user-content-r0s75" href="#r0s75">e2e/__tests__/jestChangedFiles.test.ts</a>
+```
+✔️ gets hg SCM roots and dedupes them
+✔️ gets git SCM roots and dedupes them
+✔️ gets mixed git and hg SCM roots and dedupes them
+✔️ gets changed files for git
+✔️ monitors only root paths for git
+✔️ does not find changes in files with no diff, for git
+✔️ handles a bad revision for "changedSince", for git
+❌ gets changed files for hg
+	Error: abort: empty revision range
+✔️ monitors only root paths for hg
+✔️ handles a bad revision for "changedSince", for hg
+```
+### ❌ <a id="user-content-r0s98" href="#r0s98">e2e/__tests__/onlyChanged.test.ts</a>
+```
+✔️ run for "onlyChanged" and "changedSince"
+✔️ run only changed files
+✔️ report test coverage for only changed files
+✔️ report test coverage of source on test file change under only changed files
+✔️ do not pickup non-tested files when reporting coverage on only changed files
+✔️ collect test coverage when using onlyChanged
+✔️ onlyChanged in config is overwritten by --all or testPathPattern
+❌ gets changed files for hg
+	Error: expect(received).toMatch(expected)
+✔️ path on Windows is case-insensitive
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/mocha-json.md b/test-reporter/__tests__/__outputs__/mocha-json.md
new file mode 100644
index 0000000..9eadcae
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/mocha-json.md
@@ -0,0 +1,29 @@
+![Tests failed](https://img.shields.io/badge/tests-1%20passed%2C%204%20failed%2C%201%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/mocha-json.json</a>
+**6** tests were completed in **12ms** with **1** passed, **4** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[test/main.test.js](#r0s0)|1✔️|3❌||1ms|
+|[test/second.test.js](#r0s1)||1❌|1✖️|8ms|
+### ❌ <a id="user-content-r0s0" href="#r0s0">test/main.test.js</a>
+```
+Test 1
+  ✔️ Passing test
+Test 1 Test 1.1
+  ❌ Exception in target unit
+	Some error
+  ❌ Failing test
+	Expected values to be strictly equal:
+	
+	false !== true
+	
+Test 2
+  ❌ Exception in test
+	Some error
+```
+### ❌ <a id="user-content-r0s1" href="#r0s1">test/second.test.js</a>
+```
+✖️ Skipped test
+❌ Timeout test
+	Timeout of 1ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (C:\Users\Michal\Workspace\dorny\test-reporter\reports\mocha\test\second.test.js)
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/mocha-test-results.md b/test-reporter/__tests__/__outputs__/mocha-test-results.md
new file mode 100644
index 0000000..fd85859
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/mocha-test-results.md
@@ -0,0 +1,41 @@
+![Tests passed successfully](https://img.shields.io/badge/tests-833%20passed%2C%206%20skipped-success)
+## ✔️ <a id="user-content-r0" href="#r0">fixtures/external/mocha/mocha-test-results.json</a>
+**839** tests were completed in **6s** with **833** passed, **0** failed and **6** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|test/node-unit/buffered-worker-pool.spec.js|14✔️|||8ms|
+|test/node-unit/cli/config.spec.js|10✔️|||8ms|
+|test/node-unit/cli/node-flags.spec.js|105✔️|||9ms|
+|test/node-unit/cli/options.spec.js|36✔️|||250ms|
+|test/node-unit/cli/run-helpers.spec.js|9✔️|||8ms|
+|test/node-unit/cli/run.spec.js|40✔️|||4ms|
+|test/node-unit/mocha.spec.js|24✔️|||33ms|
+|test/node-unit/parallel-buffered-runner.spec.js|19✔️|||23ms|
+|test/node-unit/reporters/parallel-buffered.spec.js|6✔️|||16ms|
+|test/node-unit/serializer.spec.js|40✔️|||31ms|
+|test/node-unit/stack-trace-filter.spec.js|2✔️||4✖️|1ms|
+|test/node-unit/utils.spec.js|5✔️|||1ms|
+|test/node-unit/worker.spec.js|15✔️|||92ms|
+|test/unit/context.spec.js|8✔️|||5ms|
+|test/unit/duration.spec.js|3✔️|||166ms|
+|test/unit/errors.spec.js|13✔️|||5ms|
+|test/unit/globals.spec.js|4✔️|||0ms|
+|test/unit/grep.spec.js|8✔️|||2ms|
+|test/unit/hook-async.spec.js|3✔️|||1ms|
+|test/unit/hook-sync-nested.spec.js|4✔️|||1ms|
+|test/unit/hook-sync.spec.js|3✔️|||0ms|
+|test/unit/hook-timeout.spec.js|1✔️|||0ms|
+|test/unit/hook.spec.js|4✔️|||0ms|
+|test/unit/mocha.spec.js|115✔️||1✖️|128ms|
+|test/unit/overspecified-async.spec.js|1✔️|||3ms|
+|test/unit/parse-query.spec.js|2✔️|||1ms|
+|test/unit/plugin-loader.spec.js|41✔️||1✖️|16ms|
+|test/unit/required-tokens.spec.js|1✔️|||0ms|
+|test/unit/root.spec.js|1✔️|||0ms|
+|test/unit/runnable.spec.js|55✔️|||122ms|
+|test/unit/runner.spec.js|77✔️|||43ms|
+|test/unit/suite.spec.js|57✔️|||14ms|
+|test/unit/test.spec.js|15✔️|||0ms|
+|test/unit/throw.spec.js|9✔️|||9ms|
+|test/unit/timeout.spec.js|8✔️|||109ms|
+|test/unit/utils.spec.js|75✔️|||24ms|
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/provider-test-results.md b/test-reporter/__tests__/__outputs__/provider-test-results.md
new file mode 100644
index 0000000..79b13aa
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/provider-test-results.md
@@ -0,0 +1,374 @@
+![Tests failed](https://img.shields.io/badge/tests-268%20passed%2C%201%20failed-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/external/flutter/provider-test-results.json</a>
+**269** tests were completed in **0ms** with **268** passed, **1** failed and **0** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[test/builder_test.dart](#r0s0)|24✔️|||402ms|
+|[test/change_notifier_provider_test.dart](#r0s1)|10✔️|||306ms|
+|[test/consumer_test.dart](#r0s2)|18✔️|||340ms|
+|[test/context_test.dart](#r0s3)|31✔️|||698ms|
+|[test/future_provider_test.dart](#r0s4)|10✔️|||305ms|
+|[test/inherited_provider_test.dart](#r0s5)|81✔️|||1s|
+|[test/listenable_provider_test.dart](#r0s6)|16✔️|||353ms|
+|[test/listenable_proxy_provider_test.dart](#r0s7)|12✔️|||373ms|
+|[test/multi_provider_test.dart](#r0s8)|3✔️|||198ms|
+|[test/provider_test.dart](#r0s9)|11✔️|||306ms|
+|[test/proxy_provider_test.dart](#r0s10)|16✔️|||438ms|
+|[test/reassemble_test.dart](#r0s11)|3✔️|||221ms|
+|[test/selector_test.dart](#r0s12)|17✔️|||364ms|
+|[test/stateful_provider_test.dart](#r0s13)|4✔️|||254ms|
+|[test/stream_provider_test.dart](#r0s14)|8✔️|||282ms|
+|[test/value_listenable_provider_test.dart](#r0s15)|4✔️|1❌||327ms|
+### ✔️ <a id="user-content-r0s0" href="#r0s0">test/builder_test.dart</a>
+```
+ChangeNotifierProvider
+  ✔️ default
+  ✔️ .value
+ListenableProvider
+  ✔️ default
+  ✔️ .value
+Provider
+  ✔️ default
+  ✔️ .value
+ProxyProvider
+  ✔️ 0
+  ✔️ 1
+  ✔️ 2
+  ✔️ 3
+  ✔️ 4
+  ✔️ 5
+  ✔️ 6
+MultiProvider
+  ✔️ with 1 ChangeNotifierProvider default
+  ✔️ with 2 ChangeNotifierProvider default
+  ✔️ with ListenableProvider default
+  ✔️ with Provider default
+  ✔️ with ProxyProvider0
+  ✔️ with ProxyProvider1
+  ✔️ with ProxyProvider2
+  ✔️ with ProxyProvider3
+  ✔️ with ProxyProvider4
+  ✔️ with ProxyProvider5
+  ✔️ with ProxyProvider6
+```
+### ✔️ <a id="user-content-r0s1" href="#r0s1">test/change_notifier_provider_test.dart</a>
+```
+✔️ Use builder property, not child
+ChangeNotifierProvider
+  ✔️ value
+  ✔️ builder
+  ✔️ builder1
+  ✔️ builder2
+  ✔️ builder3
+  ✔️ builder4
+  ✔️ builder5
+  ✔️ builder6
+  ✔️ builder0
+```
+### ✔️ <a id="user-content-r0s2" href="#r0s2">test/consumer_test.dart</a>
+```
+consumer
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+consumer2
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+consumer3
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+consumer4
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+consumer5
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+consumer6
+  ✔️ obtains value from Provider<T>
+  ✔️ crashed with no builder
+  ✔️ can be used inside MultiProvider
+```
+### ✔️ <a id="user-content-r0s3" href="#r0s3">test/context_test.dart</a>
+```
+✔️ watch in layoutbuilder
+✔️ select in layoutbuilder
+✔️ cannot select in listView
+✔️ watch in listView
+✔️ watch in gridView
+✔️ clears select dependencies for all dependents
+BuildContext
+  ✔️ internal selected value is updated
+  ✔️ create can use read without being lazy
+  ✔️ watch can be used inside InheritedProvider.update
+  ✔️ select doesn't fail if it loads a provider that depends on other providers
+  ✔️ don't call old selectors if the child rebuilds individually
+  ✔️ selects throws inside click handlers
+  ✔️ select throws if try to read dynamic
+  ✔️ select throws ProviderNotFoundException
+  ✔️ select throws if watch called inside the callback from build
+  ✔️ select throws if read called inside the callback from build
+  ✔️ select throws if select called inside the callback from build
+  ✔️ select throws if read called inside the callback on dependency change
+  ✔️ select throws if watch called inside the callback on dependency change
+  ✔️ select throws if select called inside the callback on dependency change
+  ✔️ can call read inside didChangeDependencies
+  ✔️ select cannot be called inside didChangeDependencies
+  ✔️ select in initState throws
+  ✔️ watch in initState throws
+  ✔️ read in initState works
+  ✔️ consumer can be removed and selector stops to be called
+  ✔️ context.select deeply compares maps
+  ✔️ context.select deeply compares lists
+  ✔️ context.select deeply compares iterables
+  ✔️ context.select deeply compares sets
+  ✔️ context.watch listens to value changes
+```
+### ✔️ <a id="user-content-r0s4" href="#r0s4">test/future_provider_test.dart</a>
+```
+✔️ works with MultiProvider
+✔️ (catchError) previous future completes after transition is no-op
+✔️ previous future completes after transition is no-op
+✔️ transition from future to future preserve state
+✔️ throws if future has error and catchError is missing
+✔️ calls catchError if present and future has error
+✔️ works with null
+✔️ create and dispose future with builder
+✔️ FutureProvider() crashes if builder is null
+FutureProvider()
+  ✔️ crashes if builder is null
+```
+### ✔️ <a id="user-content-r0s5" href="#r0s5">test/inherited_provider_test.dart</a>
+```
+✔️ regression test #377
+✔️ rebuild on dependency flags update
+✔️ properly update debug flags if a create triggers another deferred create
+✔️ properly update debug flags if a create triggers another deferred create
+✔️ properly update debug flags if an update triggers another create/update
+✔️ properly update debug flags if a create triggers another create/update
+✔️ Provider.of(listen: false) outside of build works when it loads a provider
+✔️ new value is available in didChangeDependencies
+✔️ builder receives the current value and updates independently from `update`
+✔️ builder can _not_ rebuild when provider updates
+✔️ builder rebuilds if provider is recreated
+✔️ provider.of throws if listen:true outside of the widget tree
+✔️ InheritedProvider throws if no child is provided with default constructor
+✔️ InheritedProvider throws if no child is provided with value constructor
+✔️ DeferredInheritedProvider throws if no child is provided with default constructor
+✔️ DeferredInheritedProvider throws if no child is provided with value constructor
+✔️ startListening markNeedsNotifyDependents
+✔️ InheritedProvider can be subclassed
+✔️ DeferredInheritedProvider can be subclassed
+✔️ can be used with MultiProvider
+✔️ throw if the widget ctor changes
+✔️ InheritedProvider lazy loading can be disabled
+✔️ InheritedProvider.value lazy loading can be disabled
+✔️ InheritedProvider subclass don't have to specify default lazy value
+✔️ DeferredInheritedProvider lazy loading can be disabled
+✔️ DeferredInheritedProvider.value lazy loading can be disabled
+✔️ selector
+✔️ can select multiple types from same provider
+✔️ can select same type on two different providers
+✔️ can select same type twice on same provider
+✔️ Provider.of has a proper error message if context is null
+diagnostics
+  ✔️ InheritedProvider.value
+  ✔️ InheritedProvider doesn't break lazy loading
+  ✔️ InheritedProvider show if listening
+  ✔️ DeferredInheritedProvider.value
+  ✔️ DeferredInheritedProvider
+InheritedProvider.value()
+  ✔️ markNeedsNotifyDependents during startListening is noop
+  ✔️ startListening called again when create returns new value
+  ✔️ startListening
+  ✔️ stopListening not called twice if rebuild doesn't have listeners
+  ✔️ removeListener cannot be null
+  ✔️ pass down current value
+  ✔️ default updateShouldNotify
+  ✔️ custom updateShouldNotify
+InheritedProvider()
+  ✔️ hasValue
+  ✔️ provider calls update if rebuilding only due to didChangeDependencies
+  ✔️ provider notifying dependents doesn't call update
+  ✔️ update can call Provider.of with listen:true
+  ✔️ update lazy loaded can call Provider.of with listen:true
+  ✔️ markNeedsNotifyDependents during startListening is noop
+  ✔️ update can obtain parent of the same type than self
+  ✔️ _debugCheckInvalidValueType
+  ✔️ startListening
+  ✔️ startListening called again when create returns new value
+  ✔️ stopListening not called twice if rebuild doesn't have listeners
+  ✔️ removeListener cannot be null
+  ✔️ fails if initialValueBuilder calls inheritFromElement/inheritFromWiggetOfExactType
+  ✔️ builder is called on every rebuild and after a dependency change
+  ✔️ builder with no updateShouldNotify use ==
+  ✔️ builder calls updateShouldNotify callback
+  ✔️ initialValue is transmitted to valueBuilder
+  ✔️ calls builder again if dependencies change
+  ✔️ exposes initialValue if valueBuilder is null
+  ✔️ call dispose on unmount
+  ✔️ builder unmount, dispose not called if value never read
+  ✔️ call dispose after new value
+  ✔️ valueBuilder works without initialBuilder
+  ✔️ calls initialValueBuilder lazily once
+  ✔️ throws if both builder and initialBuilder are missing
+DeferredInheritedProvider.value()
+  ✔️ hasValue
+  ✔️ startListening
+  ✔️ stopListening cannot be null
+  ✔️ startListening doesn't need setState if already initialized
+  ✔️ setState without updateShouldNotify
+  ✔️ setState with updateShouldNotify
+  ✔️ startListening never leave the widget uninitialized
+  ✔️ startListening called again on controller change
+DeferredInheritedProvider()
+  ✔️ create can't call inherited widgets
+  ✔️ creates the value lazily
+  ✔️ dispose
+  ✔️ dispose no-op if never built
+```
+### ✔️ <a id="user-content-r0s6" href="#r0s6">test/listenable_provider_test.dart</a>
+```
+ListenableProvider
+  ✔️ works with MultiProvider
+  ✔️ asserts that the created notifier can have listeners
+  ✔️ don't listen again if listenable instance doesn't change
+  ✔️ works with null (default)
+  ✔️ works with null (create)
+  ✔️ stateful create called once
+  ✔️ dispose called on unmount
+  ✔️ dispose can be null
+  ✔️ changing listenable rebuilds descendants
+  ✔️ rebuilding with the same provider don't rebuilds descendants
+  ✔️ notifylistener rebuilds descendants
+ListenableProvider value constructor
+  ✔️ pass down key
+  ✔️ changing the Listenable instance rebuilds dependents
+ListenableProvider stateful constructor
+  ✔️ called with context
+  ✔️ pass down key
+  ✔️ throws if create is null
+```
+### ✔️ <a id="user-content-r0s7" href="#r0s7">test/listenable_proxy_provider_test.dart</a>
+```
+ListenableProxyProvider
+  ✔️ throws if update is missing
+  ✔️ asserts that the created notifier has no listener
+  ✔️ asserts that the created notifier has no listener after rebuild
+  ✔️ rebuilds dependendents when listeners are called
+  ✔️ update returning a new Listenable disposes the previously created value and update dependents
+  ✔️ disposes of created value
+ListenableProxyProvider variants
+  ✔️ ListenableProxyProvider
+  ✔️ ListenableProxyProvider2
+  ✔️ ListenableProxyProvider3
+  ✔️ ListenableProxyProvider4
+  ✔️ ListenableProxyProvider5
+  ✔️ ListenableProxyProvider6
+```
+### ✔️ <a id="user-content-r0s8" href="#r0s8">test/multi_provider_test.dart</a>
+```
+MultiProvider
+  ✔️ throw if providers is null
+  ✔️ MultiProvider children can only access parent providers
+  ✔️ MultiProvider.providers with ignored child
+```
+### ✔️ <a id="user-content-r0s9" href="#r0s9">test/provider_test.dart</a>
+```
+✔️ works with MultiProvider
+Provider.of
+  ✔️ throws if T is dynamic
+  ✔️ listen defaults to true when building widgets
+  ✔️ listen defaults to false outside of the widget tree
+  ✔️ listen:false doesn't trigger rebuild
+  ✔️ listen:true outside of the widget tree throws
+Provider
+  ✔️ throws if the provided value is a Listenable/Stream
+  ✔️ debugCheckInvalidValueType can be disabled
+  ✔️ simple usage
+  ✔️ throws an error if no provider found
+  ✔️ update should notify
+```
+### ✔️ <a id="user-content-r0s10" href="#r0s10">test/proxy_provider_test.dart</a>
+```
+ProxyProvider
+  ✔️ throws if the provided value is a Listenable/Stream
+  ✔️ debugCheckInvalidValueType can be disabled
+  ✔️ create creates initial value
+  ✔️ consume another providers
+  ✔️ rebuild descendants if value change
+  ✔️ call dispose when unmounted with the latest result
+  ✔️ don't rebuild descendants if value doesn't change
+  ✔️ pass down updateShouldNotify
+  ✔️ works with MultiProvider
+  ✔️ update callback can trigger descendants setState synchronously
+  ✔️ throws if update is null
+ProxyProvider variants
+  ✔️ ProxyProvider2
+  ✔️ ProxyProvider3
+  ✔️ ProxyProvider4
+  ✔️ ProxyProvider5
+  ✔️ ProxyProvider6
+```
+### ✔️ <a id="user-content-r0s11" href="#r0s11">test/reassemble_test.dart</a>
+```
+✔️ ReassembleHandler
+✔️ unevaluated create
+✔️ unevaluated create
+```
+### ✔️ <a id="user-content-r0s12" href="#r0s12">test/selector_test.dart</a>
+```
+✔️ asserts that builder/selector are not null
+✔️ Deep compare maps by default
+✔️ Deep compare iterables by default
+✔️ Deep compare sets by default
+✔️ Deep compare lists by default
+✔️ custom shouldRebuid
+✔️ passes `child` and `key`
+✔️ calls builder if the callback changes
+✔️ works with MultiProvider
+✔️ don't call builder again if it rebuilds but selector returns the same thing
+✔️ call builder again if it rebuilds abd selector returns the a different variable
+✔️ Selector
+✔️ Selector2
+✔️ Selector3
+✔️ Selector4
+✔️ Selector5
+✔️ Selector6
+```
+### ✔️ <a id="user-content-r0s13" href="#r0s13">test/stateful_provider_test.dart</a>
+```
+✔️ asserts
+✔️ works with MultiProvider
+✔️ calls create only once
+✔️ dispose
+```
+### ✔️ <a id="user-content-r0s14" href="#r0s14">test/stream_provider_test.dart</a>
+```
+✔️ works with MultiProvider
+✔️ transition from stream to stream preserve state
+✔️ throws if stream has error and catchError is missing
+✔️ calls catchError if present and stream has error
+✔️ works with null
+✔️ StreamProvider() crashes if builder is null
+StreamProvider()
+  ✔️ create and dispose stream with builder
+  ✔️ crashes if builder is null
+```
+### ❌ <a id="user-content-r0s15" href="#r0s15">test/value_listenable_provider_test.dart</a>
+```
+valueListenableProvider
+  ✔️ rebuilds when value change
+  ✔️ don't rebuild dependents by default
+  ✔️ pass keys
+  ✔️ don't listen again if stream instance doesn't change
+  ❌ pass updateShouldNotify
+	The following TestFailure object was thrown running a test:
+	  Expected: <2>
+	  Actual: <1>
+	Unexpected number of calls
+	
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/pulsar-test-results-no-merge.md b/test-reporter/__tests__/__outputs__/pulsar-test-results-no-merge.md
new file mode 100644
index 0000000..dbf7601
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/pulsar-test-results-no-merge.md
@@ -0,0 +1,12 @@
+![Tests failed](https://img.shields.io/badge/tests-1%20failed%2C%201%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/external/java/TEST-org.apache.pulsar.AddMissingPatchVersionTest.xml</a>
+**2** tests were completed in **116ms** with **0** passed, **1** failed and **1** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[org.apache.pulsar.AddMissingPatchVersionTest](#r0s0)||1❌|1✖️|116ms|
+### ❌ <a id="user-content-r0s0" href="#r0s0">org.apache.pulsar.AddMissingPatchVersionTest</a>
+```
+✖️ testVersionStrings
+❌ testVersionStrings
+	java.lang.AssertionError: expected [1.2.1] but found [1.2.0]
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/pulsar-test-results.md b/test-reporter/__tests__/__outputs__/pulsar-test-results.md
new file mode 100644
index 0000000..88e8bea
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/pulsar-test-results.md
@@ -0,0 +1,1518 @@
+![Tests failed](https://img.shields.io/badge/tests-793%20passed%2C%201%20failed%2C%2014%20skipped-critical)
+## ❌ <a id="user-content-r0" href="#r0">fixtures/external/java/pulsar-test-report.xml</a>
+**808** tests were completed in **2127s** with **793** passed, **1** failed and **14** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[org.apache.pulsar.AddMissingPatchVersionTest](#r0s0)||1❌|1✖️|116ms|
+|[org.apache.pulsar.broker.admin.AdminApiOffloadTest](#r0s1)|7✔️|||19s|
+|[org.apache.pulsar.broker.auth.AuthenticationServiceTest](#r0s2)|2✔️|||185ms|
+|[org.apache.pulsar.broker.auth.AuthLogsTest](#r0s3)|2✔️|||1s|
+|[org.apache.pulsar.broker.auth.AuthorizationTest](#r0s4)|1✔️|||2s|
+|[org.apache.pulsar.broker.lookup.http.HttpTopicLookupv2Test](#r0s5)|4✔️|||2s|
+|[org.apache.pulsar.broker.namespace.NamespaceCreateBundlesTest](#r0s6)|2✔️|||33s|
+|[org.apache.pulsar.broker.namespace.NamespaceOwnershipListenerTests](#r0s7)|2✔️|||32s|
+|[org.apache.pulsar.broker.namespace.NamespaceServiceTest](#r0s8)|10✔️|||75s|
+|[org.apache.pulsar.broker.namespace.NamespaceUnloadingTest](#r0s9)|2✔️|||14s|
+|[org.apache.pulsar.broker.namespace.OwnerShipCacheForCurrentServerTest](#r0s10)|1✔️|||16s|
+|[org.apache.pulsar.broker.namespace.OwnershipCacheTest](#r0s11)|8✔️|||16s|
+|[org.apache.pulsar.broker.protocol.ProtocolHandlersTest](#r0s12)|6✔️|||946ms|
+|[org.apache.pulsar.broker.protocol.ProtocolHandlerUtilsTest](#r0s13)|3✔️|||7s|
+|[org.apache.pulsar.broker.protocol.ProtocolHandlerWithClassLoaderTest](#r0s14)|1✔️|||15ms|
+|[org.apache.pulsar.broker.PulsarServiceTest](#r0s15)|2✔️|||96ms|
+|[org.apache.pulsar.broker.service.MessagePublishBufferThrottleTest](#r0s16)|3✔️|||14s|
+|[org.apache.pulsar.broker.service.ReplicatorTest](#r0s17)|22✔️|||40s|
+|[org.apache.pulsar.broker.service.TopicOwnerTest](#r0s18)|8✔️|||114s|
+|[org.apache.pulsar.broker.SLAMonitoringTest](#r0s19)|4✔️|||9s|
+|[org.apache.pulsar.broker.stats.BookieClientsStatsGeneratorTest](#r0s20)|2✔️|||49ms|
+|[org.apache.pulsar.broker.stats.ConsumerStatsTest](#r0s21)|3✔️|||21s|
+|[org.apache.pulsar.broker.stats.ManagedCursorMetricsTest](#r0s22)|1✔️|||281ms|
+|[org.apache.pulsar.broker.stats.ManagedLedgerMetricsTest](#r0s23)|1✔️|||285ms|
+|[org.apache.pulsar.broker.stats.prometheus.AggregatedNamespaceStatsTest](#r0s24)|1✔️|||40ms|
+|[org.apache.pulsar.broker.stats.PrometheusMetricsTest](#r0s25)|15✔️|||83s|
+|[org.apache.pulsar.broker.stats.SubscriptionStatsTest](#r0s26)|2✔️|||2s|
+|[org.apache.pulsar.broker.systopic.NamespaceEventsSystemTopicServiceTest](#r0s27)|1✔️|||1s|
+|[org.apache.pulsar.broker.transaction.buffer.InMemTransactionBufferReaderTest](#r0s28)|3✔️|||28ms|
+|[org.apache.pulsar.broker.transaction.buffer.TransactionBufferClientTest](#r0s29)|4✔️|||93ms|
+|[org.apache.pulsar.broker.transaction.buffer.TransactionBufferTest](#r0s30)|7✔️|||81ms|
+|[org.apache.pulsar.broker.transaction.buffer.TransactionEntryImplTest](#r0s31)|1✔️|||14ms|
+|[org.apache.pulsar.broker.transaction.buffer.TransactionLowWaterMarkTest](#r0s32)|2✔️|||38s|
+|[org.apache.pulsar.broker.transaction.buffer.TransactionStablePositionTest](#r0s33)|2✔️||1✖️|49s|
+|[org.apache.pulsar.broker.transaction.coordinator.TransactionCoordinatorClientTest](#r0s34)|3✔️|||95ms|
+|[org.apache.pulsar.broker.transaction.coordinator.TransactionMetaStoreAssignmentTest](#r0s35)|1✔️|||1s|
+|[org.apache.pulsar.broker.transaction.pendingack.PendingAckInMemoryDeleteTest](#r0s36)|2✔️||1✖️|57s|
+|[org.apache.pulsar.broker.transaction.TransactionConsumeTest](#r0s37)|2✔️|||30s|
+|[org.apache.pulsar.broker.web.RestExceptionTest](#r0s38)|3✔️|||37ms|
+|[org.apache.pulsar.broker.web.WebServiceTest](#r0s39)|9✔️|||27s|
+|[org.apache.pulsar.client.impl.AdminApiKeyStoreTlsAuthTest](#r0s40)|4✔️|||8s|
+|[org.apache.pulsar.client.impl.BatchMessageIdImplSerializationTest](#r0s41)|4✔️|||30ms|
+|[org.apache.pulsar.client.impl.BatchMessageIndexAckDisableTest](#r0s42)|4✔️|||14s|
+|[org.apache.pulsar.client.impl.BatchMessageIndexAckTest](#r0s43)|5✔️|||44s|
+|[org.apache.pulsar.client.impl.BrokerClientIntegrationTest](#r0s44)|15✔️|||148s|
+|[org.apache.pulsar.client.impl.CompactedOutBatchMessageTest](#r0s45)|1✔️|||1s|
+|[org.apache.pulsar.client.impl.ConsumerAckResponseTest](#r0s46)|1✔️|||549ms|
+|[org.apache.pulsar.client.impl.ConsumerConfigurationTest](#r0s47)|4✔️|||12s|
+|[org.apache.pulsar.client.impl.ConsumerDedupPermitsUpdate](#r0s48)|7✔️|||4s|
+|[org.apache.pulsar.client.impl.ConsumerUnsubscribeTest](#r0s49)|1✔️|||129ms|
+|[org.apache.pulsar.client.impl.KeyStoreTlsProducerConsumerTestWithAuth](#r0s50)|3✔️|||23s|
+|[org.apache.pulsar.client.impl.KeyStoreTlsProducerConsumerTestWithoutAuth](#r0s51)|3✔️|||8s|
+|[org.apache.pulsar.client.impl.KeyStoreTlsTest](#r0s52)|1✔️|||183ms|
+|[org.apache.pulsar.client.impl.MessageChecksumTest](#r0s53)|3✔️|||47s|
+|[org.apache.pulsar.client.impl.MessageChunkingTest](#r0s54)|8✔️||1✖️|73s|
+|[org.apache.pulsar.client.impl.MessageParserTest](#r0s55)|2✔️|||5s|
+|[org.apache.pulsar.client.impl.MultiTopicsReaderTest](#r0s56)|8✔️|||35s|
+|[org.apache.pulsar.client.impl.NegativeAcksTest](#r0s57)|32✔️|||11s|
+|[org.apache.pulsar.client.impl.PatternTopicsConsumerImplTest](#r0s58)|11✔️|||63s|
+|[org.apache.pulsar.client.impl.PerMessageUnAcknowledgedRedeliveryTest](#r0s59)|5✔️|||34s|
+|[org.apache.pulsar.client.impl.PulsarMultiHostClientTest](#r0s60)|3✔️|||15s|
+|[org.apache.pulsar.client.impl.RawMessageSerDeserTest](#r0s61)|1✔️|||10ms|
+|[org.apache.pulsar.client.impl.SchemaDeleteTest](#r0s62)|1✔️|||2s|
+|[org.apache.pulsar.client.impl.SequenceIdWithErrorTest](#r0s63)|3✔️||2✖️|18s|
+|[org.apache.pulsar.client.impl.TopicDoesNotExistsTest](#r0s64)|2✔️|||4s|
+|[org.apache.pulsar.client.impl.TopicFromMessageTest](#r0s65)|5✔️|||14s|
+|[org.apache.pulsar.client.impl.TopicsConsumerImplTest](#r0s66)|17✔️|||133s|
+|[org.apache.pulsar.client.impl.UnAcknowledgedMessagesTimeoutTest](#r0s67)|7✔️|||44s|
+|[org.apache.pulsar.client.impl.ZeroQueueSizeTest](#r0s68)|14✔️|||16s|
+|[org.apache.pulsar.common.api.raw.RawMessageImplTest](#r0s69)|1✔️|||316ms|
+|[org.apache.pulsar.common.compression.CommandsTest](#r0s70)|1✔️|||30ms|
+|[org.apache.pulsar.common.compression.CompressorCodecBackwardCompatTest](#r0s71)|6✔️|||223ms|
+|[org.apache.pulsar.common.compression.CompressorCodecTest](#r0s72)|45✔️|||737ms|
+|[org.apache.pulsar.common.compression.Crc32cChecksumTest](#r0s73)|6✔️|||5s|
+|[org.apache.pulsar.common.lookup.data.LookupDataTest](#r0s74)|4✔️|||2s|
+|[org.apache.pulsar.common.naming.MetadataTests](#r0s75)|2✔️|||161ms|
+|[org.apache.pulsar.common.naming.NamespaceBundlesTest](#r0s76)|5✔️|||99ms|
+|[org.apache.pulsar.common.naming.NamespaceBundleTest](#r0s77)|6✔️|||64ms|
+|[org.apache.pulsar.common.naming.NamespaceNameTest](#r0s78)|2✔️|||207ms|
+|[org.apache.pulsar.common.naming.ServiceConfigurationTest](#r0s79)|4✔️|||48ms|
+|[org.apache.pulsar.common.naming.TopicNameTest](#r0s80)|4✔️|||529ms|
+|[org.apache.pulsar.common.net.ServiceURITest](#r0s81)|21✔️|||237ms|
+|[org.apache.pulsar.common.policies.data.AutoFailoverPolicyDataTest](#r0s82)|1✔️|||15ms|
+|[org.apache.pulsar.common.policies.data.AutoFailoverPolicyTypeTest](#r0s83)|1✔️|||19ms|
+|[org.apache.pulsar.common.policies.data.AutoTopicCreationOverrideTest](#r0s84)|6✔️|||64ms|
+|[org.apache.pulsar.common.policies.data.BacklogQuotaTest](#r0s85)|1✔️|||12ms|
+|[org.apache.pulsar.common.policies.data.ClusterDataTest](#r0s86)|1✔️|||9ms|
+|[org.apache.pulsar.common.policies.data.ConsumerStatsTest](#r0s87)|1✔️|||8ms|
+|[org.apache.pulsar.common.policies.data.EnsemblePlacementPolicyConfigTest](#r0s88)|2✔️|||948ms|
+|[org.apache.pulsar.common.policies.data.LocalPolicesTest](#r0s89)|1✔️|||48ms|
+|[org.apache.pulsar.common.policies.data.NamespaceIsolationDataTest](#r0s90)|1✔️|||76ms|
+|[org.apache.pulsar.common.policies.data.NamespaceOwnershipStatusTest](#r0s91)|1✔️|||45ms|
+|[org.apache.pulsar.common.policies.data.OffloadPoliciesTest](#r0s92)|6✔️|||216ms|
+|[org.apache.pulsar.common.policies.data.PartitionedTopicStatsTest](#r0s93)|1✔️|||12ms|
+|[org.apache.pulsar.common.policies.data.PersistencePoliciesTest](#r0s94)|1✔️|||19ms|
+|[org.apache.pulsar.common.policies.data.PersistentOfflineTopicStatsTest](#r0s95)|1✔️|||29ms|
+|[org.apache.pulsar.common.policies.data.PersistentTopicStatsTest](#r0s96)|2✔️|||51ms|
+|[org.apache.pulsar.common.policies.data.PoliciesDataTest](#r0s97)|4✔️|||1s|
+|[org.apache.pulsar.common.policies.data.PublisherStatsTest](#r0s98)|2✔️|||37ms|
+|[org.apache.pulsar.common.policies.data.ReplicatorStatsTest](#r0s99)|2✔️|||30ms|
+|[org.apache.pulsar.common.policies.data.ResourceQuotaTest](#r0s100)|2✔️|||45ms|
+|[org.apache.pulsar.common.policies.data.RetentionPolicesTest](#r0s101)|1✔️|||8ms|
+|[org.apache.pulsar.common.policies.impl.AutoFailoverPolicyFactoryTest](#r0s102)|1✔️|||22ms|
+|[org.apache.pulsar.common.policies.impl.MinAvailablePolicyTest](#r0s103)|1✔️|||1ms|
+|[org.apache.pulsar.common.policies.impl.NamespaceIsolationPoliciesTest](#r0s104)|7✔️|||265ms|
+|[org.apache.pulsar.common.policies.impl.NamespaceIsolationPolicyImplTest](#r0s105)|7✔️|||309ms|
+|[org.apache.pulsar.common.protocol.ByteBufPairTest](#r0s106)|2✔️|||5s|
+|[org.apache.pulsar.common.protocol.CommandUtilsTests](#r0s107)|7✔️|||3s|
+|[org.apache.pulsar.common.protocol.MarkersTest](#r0s108)|6✔️|||3s|
+|[org.apache.pulsar.common.protocol.PulsarDecoderTest](#r0s109)|1✔️|||4s|
+|[org.apache.pulsar.common.stats.JvmDefaultGCMetricsLoggerTest](#r0s110)|1✔️|||82ms|
+|[org.apache.pulsar.common.util.collections.BitSetRecyclableRecyclableTest](#r0s111)|2✔️|||13ms|
+|[org.apache.pulsar.common.util.collections.ConcurrentBitSetRecyclableTest](#r0s112)|2✔️|||63ms|
+|[org.apache.pulsar.common.util.collections.ConcurrentLongHashMapTest](#r0s113)|13✔️|||28s|
+|[org.apache.pulsar.common.util.collections.ConcurrentLongPairSetTest](#r0s114)|15✔️|||2s|
+|[org.apache.pulsar.common.util.collections.ConcurrentOpenHashMapTest](#r0s115)|12✔️|||9s|
+|[org.apache.pulsar.common.util.collections.ConcurrentOpenHashSetTest](#r0s116)|11✔️|||7s|
+|[org.apache.pulsar.common.util.collections.ConcurrentOpenLongPairRangeSetTest](#r0s117)|13✔️|||1s|
+|[org.apache.pulsar.common.util.collections.ConcurrentSortedLongPairSetTest](#r0s118)|9✔️|||342ms|
+|[org.apache.pulsar.common.util.collections.FieldParserTest](#r0s119)|2✔️|||64ms|
+|[org.apache.pulsar.common.util.collections.GrowableArrayBlockingQueueTest](#r0s120)|6✔️|||350ms|
+|[org.apache.pulsar.common.util.collections.GrowablePriorityLongPairQueueTest](#r0s121)|15✔️|||3s|
+|[org.apache.pulsar.common.util.collections.TripleLongPriorityQueueTest](#r0s122)|3✔️|||238ms|
+|[org.apache.pulsar.common.util.FieldParserTest](#r0s123)|1✔️|||242ms|
+|[org.apache.pulsar.common.util.FileModifiedTimeUpdaterTest](#r0s124)|6✔️|||6s|
+|[org.apache.pulsar.common.util.netty.ChannelFuturesTest](#r0s125)|5✔️|||2s|
+|[org.apache.pulsar.common.util.RateLimiterTest](#r0s126)|11✔️|||7s|
+|[org.apache.pulsar.common.util.ReflectionsTest](#r0s127)|12✔️|||172ms|
+|[org.apache.pulsar.common.util.RelativeTimeUtilTest](#r0s128)|1✔️|||39ms|
+|[org.apache.pulsar.discovery.service.web.DiscoveryServiceWebTest](#r0s129)|1✔️|||5s|
+|[org.apache.pulsar.functions.worker.PulsarFunctionE2ESecurityTest](#r0s130)|2✔️|||28s|
+|[org.apache.pulsar.functions.worker.PulsarFunctionPublishTest](#r0s131)|3✔️|||42s|
+|[org.apache.pulsar.functions.worker.PulsarFunctionTlsTest](#r0s132)|1✔️|||12s|
+|[org.apache.pulsar.io.PulsarFunctionTlsTest](#r0s133)|1✔️|||30s|
+|[org.apache.pulsar.proxy.server.AdminProxyHandlerTest](#r0s134)|1✔️|||474ms|
+|[org.apache.pulsar.proxy.server.AuthedAdminProxyHandlerTest](#r0s135)|1✔️|||2s|
+|[org.apache.pulsar.proxy.server.FunctionWorkerRoutingTest](#r0s136)|1✔️|||10ms|
+|[org.apache.pulsar.proxy.server.ProxyAdditionalServletTest](#r0s137)|1✔️|||125ms|
+|[org.apache.pulsar.proxy.server.ProxyAuthenticatedProducerConsumerTest](#r0s138)|1✔️|||2s|
+|[org.apache.pulsar.proxy.server.ProxyAuthenticationTest](#r0s139)|1✔️|||17s|
+|[org.apache.pulsar.proxy.server.ProxyConnectionThrottlingTest](#r0s140)|1✔️|||2s|
+|[org.apache.pulsar.proxy.server.ProxyEnableHAProxyProtocolTest](#r0s141)|1✔️|||511ms|
+|[org.apache.pulsar.proxy.server.ProxyForwardAuthDataTest](#r0s142)|1✔️|||32s|
+|[org.apache.pulsar.proxy.server.ProxyIsAHttpProxyTest](#r0s143)|10✔️|||2s|
+|[org.apache.pulsar.proxy.server.ProxyKeyStoreTlsTestWithAuth](#r0s144)|3✔️|||7s|
+|[org.apache.pulsar.proxy.server.ProxyKeyStoreTlsTestWithoutAuth](#r0s145)|3✔️|||7s|
+|[org.apache.pulsar.proxy.server.ProxyLookupThrottlingTest](#r0s146)|1✔️|||3s|
+|[org.apache.pulsar.proxy.server.ProxyParserTest](#r0s147)|5✔️|||1s|
+|[org.apache.pulsar.proxy.server.ProxyRolesEnforcementTest](#r0s148)|1✔️|||10s|
+|[org.apache.pulsar.proxy.server.ProxyStatsTest](#r0s149)|3✔️|||533ms|
+|[org.apache.pulsar.proxy.server.ProxyTest](#r0s150)|6✔️|||3s|
+|[org.apache.pulsar.proxy.server.ProxyTlsTest](#r0s151)|2✔️|||414ms|
+|[org.apache.pulsar.proxy.server.ProxyTlsTestWithAuth](#r0s152)|1✔️|||4ms|
+|[org.apache.pulsar.proxy.server.ProxyWithAuthorizationNegTest](#r0s153)|1✔️|||2s|
+|[org.apache.pulsar.proxy.server.ProxyWithAuthorizationTest](#r0s154)|13✔️|||33s|
+|[org.apache.pulsar.proxy.server.ProxyWithoutServiceDiscoveryTest](#r0s155)|1✔️|||2s|
+|[org.apache.pulsar.proxy.server.SuperUserAuthedAdminProxyHandlerTest](#r0s156)|3✔️|||8s|
+|[org.apache.pulsar.proxy.server.UnauthedAdminProxyHandlerTest](#r0s157)|2✔️|||114ms|
+|[org.apache.pulsar.PulsarBrokerStarterTest](#r0s158)|9✔️|||591ms|
+|[org.apache.pulsar.schema.compatibility.SchemaCompatibilityCheckTest](#r0s159)|23✔️|||107s|
+|[org.apache.pulsar.schema.PartitionedTopicSchemaTest](#r0s160)|1✔️|||29s|
+|[org.apache.pulsar.schema.SchemaTest](#r0s161)|3✔️|||31s|
+|[org.apache.pulsar.stats.client.PulsarBrokerStatsClientTest](#r0s162)|2✔️|||41s|
+|[org.apache.pulsar.tests.EnumValuesDataProviderTest](#r0s163)|6✔️|||23ms|
+|[org.apache.pulsar.tests.TestRetrySupportBeforeMethodRetryTest](#r0s164)|1✔️||4✖️|36ms|
+|[org.apache.pulsar.tests.TestRetrySupportRetryTest](#r0s165)|1✔️||4✖️|27ms|
+|[org.apache.pulsar.tests.TestRetrySupportSuccessTest](#r0s166)|3✔️|||1ms|
+|[org.apache.pulsar.tests.ThreadDumpUtilTest](#r0s167)|2✔️|||17ms|
+|[org.apache.pulsar.utils.SimpleTextOutputStreamTest](#r0s168)|4✔️|||50ms|
+|[org.apache.pulsar.utils.StatsOutputStreamTest](#r0s169)|6✔️|||59ms|
+|[org.apache.pulsar.websocket.proxy.ProxyAuthenticationTest](#r0s170)|4✔️|||29s|
+|[org.apache.pulsar.websocket.proxy.ProxyAuthorizationTest](#r0s171)|1✔️|||1s|
+|[org.apache.pulsar.websocket.proxy.ProxyConfigurationTest](#r0s172)|2✔️|||9s|
+|[org.apache.pulsar.websocket.proxy.ProxyPublishConsumeTlsTest](#r0s173)|1✔️|||11s|
+|[org.apache.pulsar.websocket.proxy.ProxyPublishConsumeWithoutZKTest](#r0s174)|1✔️|||7s|
+|[org.apache.pulsar.websocket.proxy.v1.V1_ProxyAuthenticationTest](#r0s175)|4✔️|||30s|
+### ❌ <a id="user-content-r0s0" href="#r0s0">org.apache.pulsar.AddMissingPatchVersionTest</a>
+```
+✖️ testVersionStrings
+❌ testVersionStrings
+	java.lang.AssertionError: expected [1.2.1] but found [1.2.0]
+```
+### ✔️ <a id="user-content-r0s1" href="#r0s1">org.apache.pulsar.broker.admin.AdminApiOffloadTest</a>
+```
+✔️ testOffloadPoliciesAppliedApi
+✔️ testOffloadV2
+✔️ testTopicLevelOffloadNonPartitioned
+✔️ testTopicLevelOffloadPartitioned
+✔️ testOffloadV1
+✔️ testOffloadPolicies
+✔️ testOffloadPoliciesApi
+```
+### ✔️ <a id="user-content-r0s2" href="#r0s2">org.apache.pulsar.broker.auth.AuthenticationServiceTest</a>
+```
+✔️ testAuthentication
+✔️ testAuthenticationHttp
+```
+### ✔️ <a id="user-content-r0s3" href="#r0s3">org.apache.pulsar.broker.auth.AuthLogsTest</a>
+```
+✔️ httpEndpoint
+✔️ binaryEndpoint
+```
+### ✔️ <a id="user-content-r0s4" href="#r0s4">org.apache.pulsar.broker.auth.AuthorizationTest</a>
+```
+✔️ simple
+```
+### ✔️ <a id="user-content-r0s5" href="#r0s5">org.apache.pulsar.broker.lookup.http.HttpTopicLookupv2Test</a>
+```
+✔️ crossColoLookup
+✔️ testNotEnoughLookupPermits
+✔️ testValidateReplicationSettingsOnNamespace
+✔️ testDataPojo
+```
+### ✔️ <a id="user-content-r0s6" href="#r0s6">org.apache.pulsar.broker.namespace.NamespaceCreateBundlesTest</a>
+```
+✔️ testCreateNamespaceWithDefaultBundles
+✔️ testSplitBundleUpdatesLocalPoliciesWithoutOverwriting
+```
+### ✔️ <a id="user-content-r0s7" href="#r0s7">org.apache.pulsar.broker.namespace.NamespaceOwnershipListenerTests</a>
+```
+✔️ testGetAllPartitions
+✔️ testNamespaceBundleOwnershipListener
+```
+### ✔️ <a id="user-content-r0s8" href="#r0s8">org.apache.pulsar.broker.namespace.NamespaceServiceTest</a>
+```
+✔️ testSplitMapWithRefreshedStatMap
+✔️ testRemoveOwnershipNamespaceBundle
+✔️ testIsServiceUnitDisabled
+✔️ testLoadReportDeserialize
+✔️ testCreateLookupResult
+✔️ testUnloadNamespaceBundleWithStuckTopic
+✔️ testUnloadNamespaceBundleFailure
+✔️ testSplitAndOwnBundles
+✔️ testCreateNamespaceWithDefaultNumberOfBundles
+✔️ testRemoveOwnershipAndSplitBundle
+```
+### ✔️ <a id="user-content-r0s9" href="#r0s9">org.apache.pulsar.broker.namespace.NamespaceUnloadingTest</a>
+```
+✔️ testUnloadNotLoadedNamespace
+✔️ testUnloadPartiallyLoadedNamespace
+```
+### ✔️ <a id="user-content-r0s10" href="#r0s10">org.apache.pulsar.broker.namespace.OwnerShipCacheForCurrentServerTest</a>
+```
+✔️ testOwnershipForCurrentServer
+```
+### ✔️ <a id="user-content-r0s11" href="#r0s11">org.apache.pulsar.broker.namespace.OwnershipCacheTest</a>
+```
+✔️ testGetOwnedServiceUnits
+✔️ testRemoveOwnership
+✔️ testGetOwnedServiceUnit
+✔️ testGetOrSetOwner
+✔️ testConstructor
+✔️ testGetOwner
+✔️ testDisableOwnership
+✔️ testReestablishOwnership
+```
+### ✔️ <a id="user-content-r0s12" href="#r0s12">org.apache.pulsar.broker.protocol.ProtocolHandlersTest</a>
+```
+✔️ testStart
+✔️ testGetProtocol
+✔️ testNewChannelInitializersSuccess
+✔️ testInitialize
+✔️ testNewChannelInitializersOverlapped
+✔️ testGetProtocolDataToAdvertise
+```
+### ✔️ <a id="user-content-r0s13" href="#r0s13">org.apache.pulsar.broker.protocol.ProtocolHandlerUtilsTest</a>
+```
+✔️ testLoadProtocolHandler
+✔️ testLoadProtocolHandlerBlankHandlerClass
+✔️ testLoadProtocolHandlerWrongHandlerClass
+```
+### ✔️ <a id="user-content-r0s14" href="#r0s14">org.apache.pulsar.broker.protocol.ProtocolHandlerWithClassLoaderTest</a>
+```
+✔️ testWrapper
+```
+### ✔️ <a id="user-content-r0s15" href="#r0s15">org.apache.pulsar.broker.PulsarServiceTest</a>
+```
+✔️ testGetWorkerService
+✔️ testGetWorkerServiceException
+```
+### ✔️ <a id="user-content-r0s16" href="#r0s16">org.apache.pulsar.broker.service.MessagePublishBufferThrottleTest</a>
+```
+✔️ testMessagePublishBufferThrottleEnable
+✔️ testBlockByPublishRateLimiting
+✔️ testMessagePublishBufferThrottleDisabled
+```
+### ✔️ <a id="user-content-r0s17" href="#r0s17">org.apache.pulsar.broker.service.ReplicatorTest</a>
+```
+✔️ testResumptionAfterBacklogRelaxed
+✔️ testReplicationOverrides
+✔️ testResetCursorNotFail
+✔️ testUpdateGlobalTopicPartition
+✔️ testReplication
+✔️ testReplicatorOnPartitionedTopic
+✔️ testConcurrentReplicator
+✔️ testTopicReplicatedAndProducerCreate
+✔️ testDeleteReplicatorFailure
+✔️ testReplicatorOnPartitionedTopic
+✔️ testReplicationForBatchMessages
+✔️ testReplicatorClearBacklog
+✔️ verifyChecksumAfterReplication
+✔️ testCloseReplicatorStartProducer
+✔️ activeBrokerParse
+✔️ testReplicatePeekAndSkip
+✔️ testReplication
+✔️ testReplicatedCluster
+✔️ testTopicReplicatedAndProducerCreate
+✔️ testConfigChange
+✔️ testFailures
+✔️ testReplicatorProducerClosing
+```
+### ✔️ <a id="user-content-r0s18" href="#r0s18">org.apache.pulsar.broker.service.TopicOwnerTest</a>
+```
+✔️ testReleaseOwnershipWithZookeeperDisconnectedBeforeOwnershipNodeDeleted
+✔️ testAcquireOwnershipWithZookeeperDisconnectedAfterOwnershipNodeCreated
+✔️ testConnectToInvalidateBundleCacheBroker
+✔️ testAcquireOwnershipWithZookeeperDisconnectedBeforeOwnershipNodeCreated
+✔️ testLookupPartitionedTopic
+✔️ testListNonPersistentTopic
+✔️ testReleaseOwnershipWithZookeeperDisconnectedAfterOwnershipNodeDeleted
+✔️ testReestablishOwnershipAfterInvalidateCache
+```
+### ✔️ <a id="user-content-r0s19" href="#r0s19">org.apache.pulsar.broker.SLAMonitoringTest</a>
+```
+✔️ testOwnedNamespaces
+✔️ testOwnershipAfterSetup
+✔️ testUnloadIfBrokerCrashes
+✔️ testOwnershipViaAdminAfterSetup
+```
+### ✔️ <a id="user-content-r0s20" href="#r0s20">org.apache.pulsar.broker.stats.BookieClientsStatsGeneratorTest</a>
+```
+✔️ testJvmDirectMemoryUsedMetric
+✔️ testBookieClientStatsGenerator
+```
+### ✔️ <a id="user-content-r0s21" href="#r0s21">org.apache.pulsar.broker.stats.ConsumerStatsTest</a>
+```
+✔️ testAckStatsOnPartitionedTopicForExclusiveSubscription
+✔️ testConsumerStatsOnZeroMaxUnackedMessagesPerConsumer
+✔️ testUpdateStatsForActiveConsumerAndSubscription
+```
+### ✔️ <a id="user-content-r0s22" href="#r0s22">org.apache.pulsar.broker.stats.ManagedCursorMetricsTest</a>
+```
+✔️ testManagedCursorMetrics
+```
+### ✔️ <a id="user-content-r0s23" href="#r0s23">org.apache.pulsar.broker.stats.ManagedLedgerMetricsTest</a>
+```
+✔️ testManagedLedgerMetrics
+```
+### ✔️ <a id="user-content-r0s24" href="#r0s24">org.apache.pulsar.broker.stats.prometheus.AggregatedNamespaceStatsTest</a>
+```
+✔️ testSimpleAggregation
+```
+### ✔️ <a id="user-content-r0s25" href="#r0s25">org.apache.pulsar.broker.stats.PrometheusMetricsTest</a>
+```
+✔️ testPerTopicStats
+✔️ testAuthMetrics
+✔️ testPerTopicExpiredStat
+✔️ testPerProducerStats
+✔️ testMetricsTopicCount
+✔️ testManagedLedgerBookieClientStats
+✔️ testDuplicateMetricTypeDefinitions
+✔️ testExpiringTokenMetrics
+✔️ testPerConsumerStats
+✔️ testPerNamespaceStats
+✔️ testManagedCursorPersistStats
+✔️ testDuplicateMetricTypeDefinitions
+✔️ testExpiredTokenMetrics
+✔️ testManagedLedgerCacheStats
+✔️ testManagedLedgerStats
+```
+### ✔️ <a id="user-content-r0s26" href="#r0s26">org.apache.pulsar.broker.stats.SubscriptionStatsTest</a>
+```
+✔️ testConsumersAfterMarkDelete
+✔️ testNonContiguousDeletedMessagesRanges
+```
+### ✔️ <a id="user-content-r0s27" href="#r0s27">org.apache.pulsar.broker.systopic.NamespaceEventsSystemTopicServiceTest</a>
+```
+✔️ testSendAndReceiveNamespaceEvents
+```
+### ✔️ <a id="user-content-r0s28" href="#r0s28">org.apache.pulsar.broker.transaction.buffer.InMemTransactionBufferReaderTest</a>
+```
+✔️ testCloseReleaseAllEntries
+✔️ testInvalidNumEntriesArgument
+✔️ testEndOfTransactionException
+```
+### ✔️ <a id="user-content-r0s29" href="#r0s29">org.apache.pulsar.broker.transaction.buffer.TransactionBufferClientTest</a>
+```
+✔️ testAbortOnTopic
+✔️ testAbortOnSubscription
+✔️ testCommitOnTopic
+✔️ testCommitOnSubscription
+```
+### ✔️ <a id="user-content-r0s30" href="#r0s30">org.apache.pulsar.broker.transaction.buffer.TransactionBufferTest</a>
+```
+✔️ testOpenReaderOnNonExistentTxn
+✔️ testAbortCommittedTxn
+✔️ testAbortTxn
+✔️ testAbortNonExistentTxn
+✔️ testCommitNonExistentTxn
+✔️ testCommitTxn
+✔️ testOpenReaderOnAnOpenTxn
+```
+### ✔️ <a id="user-content-r0s31" href="#r0s31">org.apache.pulsar.broker.transaction.buffer.TransactionEntryImplTest</a>
+```
+✔️ testCloseShouldReleaseBuffer
+```
+### ✔️ <a id="user-content-r0s32" href="#r0s32">org.apache.pulsar.broker.transaction.buffer.TransactionLowWaterMarkTest</a>
+```
+✔️ testTransactionBufferLowWaterMark
+✔️ testPendingAckLowWaterMark
+```
+### ✔️ <a id="user-content-r0s33" href="#r0s33">org.apache.pulsar.broker.transaction.buffer.TransactionStablePositionTest</a>
+```
+✔️ commitTxnTest
+✔️ abortTxnTest
+✖️ commitTxnTest
+```
+### ✔️ <a id="user-content-r0s34" href="#r0s34">org.apache.pulsar.broker.transaction.coordinator.TransactionCoordinatorClientTest</a>
+```
+✔️ testClientStart
+✔️ testCommitAndAbort
+✔️ testNewTxn
+```
+### ✔️ <a id="user-content-r0s35" href="#r0s35">org.apache.pulsar.broker.transaction.coordinator.TransactionMetaStoreAssignmentTest</a>
+```
+✔️ testTransactionMetaStoreAssignAndFailover
+```
+### ✔️ <a id="user-content-r0s36" href="#r0s36">org.apache.pulsar.broker.transaction.pendingack.PendingAckInMemoryDeleteTest</a>
+```
+✖️ txnAckTestNoBatchAndSharedSubMemoryDeleteTest
+✔️ txnAckTestNoBatchAndSharedSubMemoryDeleteTest
+✔️ txnAckTestBatchAndSharedSubMemoryDeleteTest
+```
+### ✔️ <a id="user-content-r0s37" href="#r0s37">org.apache.pulsar.broker.transaction.TransactionConsumeTest</a>
+```
+✔️ noSortedTest
+✔️ sortedTest
+```
+### ✔️ <a id="user-content-r0s38" href="#r0s38">org.apache.pulsar.broker.web.RestExceptionTest</a>
+```
+✔️ testRestException
+✔️ testWebApplicationException
+✔️ testOtherException
+```
+### ✔️ <a id="user-content-r0s39" href="#r0s39">org.apache.pulsar.broker.web.WebServiceTest</a>
+```
+✔️ testTlsAuthDisallowInsecure
+✔️ testBrokerReady
+✔️ testDefaultClientVersion
+✔️ testTlsEnabled
+✔️ testTlsAuthAllowInsecure
+✔️ testSplitPath
+✔️ testMaxRequestSize
+✔️ testTlsDisabled
+✔️ testRateLimiting
+```
+### ✔️ <a id="user-content-r0s40" href="#r0s40">org.apache.pulsar.client.impl.AdminApiKeyStoreTlsAuthTest</a>
+```
+✔️ testAuthorizedUserAsOriginalPrincipal
+✔️ testSuperUserCantListNamespaces
+✔️ testPersistentList
+✔️ testSuperUserCanListTenants
+```
+### ✔️ <a id="user-content-r0s41" href="#r0s41">org.apache.pulsar.client.impl.BatchMessageIdImplSerializationTest</a>
+```
+✔️ testSerializationEmpty
+✔️ testSerialization1
+✔️ testSerializationNull
+✔️ testSerialization2
+```
+### ✔️ <a id="user-content-r0s42" href="#r0s42">org.apache.pulsar.client.impl.BatchMessageIndexAckDisableTest</a>
+```
+✔️ testBatchMessageIndexAckForExclusiveSubscription
+✔️ testBatchMessageIndexAckForSharedSubscription
+✔️ testBatchMessageIndexAckForExclusiveSubscription
+✔️ testBatchMessageIndexAckForSharedSubscription
+```
+### ✔️ <a id="user-content-r0s43" href="#r0s43">org.apache.pulsar.client.impl.BatchMessageIndexAckTest</a>
+```
+✔️ testBatchMessageIndexAckForSharedSubscription
+✔️ testBatchMessageIndexAckForSharedSubscription
+✔️ testDoNotRecycleAckSetMultipleTimes
+✔️ testBatchMessageIndexAckForExclusiveSubscription
+✔️ testBatchMessageIndexAckForExclusiveSubscription
+```
+### ✔️ <a id="user-content-r0s44" href="#r0s44">org.apache.pulsar.client.impl.BrokerClientIntegrationTest</a>
+```
+✔️ testDisconnectClientWithoutClosingConnection
+✔️ testResetCursor
+✔️ testResetCursor
+✔️ testCloseBrokerService
+✔️ testUnsupportedBatchMessageConsumer
+✔️ testAvroSchemaProducerConsumerWithSpecifiedReaderAndWriter
+✔️ testJsonSchemaProducerConsumerWithSpecifiedReaderAndWriter
+✔️ testOperationTimeout
+✔️ testCleanProducer
+✔️ testUnsupportedBatchMessageConsumer
+✔️ testCloseConnectionOnBrokerRejectedRequest
+✔️ testAddEntryOperationTimeout
+✔️ testInvalidDynamicConfiguration
+✔️ testMaxConcurrentTopicLoading
+✔️ testCloseConnectionOnInternalServerError
+```
+### ✔️ <a id="user-content-r0s45" href="#r0s45">org.apache.pulsar.client.impl.CompactedOutBatchMessageTest</a>
+```
+✔️ testCompactedOutMessages
+```
+### ✔️ <a id="user-content-r0s46" href="#r0s46">org.apache.pulsar.client.impl.ConsumerAckResponseTest</a>
+```
+✔️ testAckResponse
+```
+### ✔️ <a id="user-content-r0s47" href="#r0s47">org.apache.pulsar.client.impl.ConsumerConfigurationTest</a>
+```
+✔️ testReadCompactNonPersistentExclusive
+✔️ testReadCompactPersistentExclusive
+✔️ testReadCompactPersistentFailover
+✔️ testReadCompactPersistentShared
+```
+### ✔️ <a id="user-content-r0s48" href="#r0s48">org.apache.pulsar.client.impl.ConsumerDedupPermitsUpdate</a>
+```
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+✔️ testConsumerDedup
+```
+### ✔️ <a id="user-content-r0s49" href="#r0s49">org.apache.pulsar.client.impl.ConsumerUnsubscribeTest</a>
+```
+✔️ testConsumerUnsubscribeReference
+```
+### ✔️ <a id="user-content-r0s50" href="#r0s50">org.apache.pulsar.client.impl.KeyStoreTlsProducerConsumerTestWithAuth</a>
+```
+✔️ testTlsClientAuthOverHTTPProtocol
+✔️ testTlsClientAuthOverBinaryProtocol
+✔️ testTlsLargeSizeMessage
+```
+### ✔️ <a id="user-content-r0s51" href="#r0s51">org.apache.pulsar.client.impl.KeyStoreTlsProducerConsumerTestWithoutAuth</a>
+```
+✔️ testTlsClientAuthOverHTTPProtocol
+✔️ testTlsClientAuthOverBinaryProtocol
+✔️ testTlsLargeSizeMessage
+```
+### ✔️ <a id="user-content-r0s52" href="#r0s52">org.apache.pulsar.client.impl.KeyStoreTlsTest</a>
+```
+✔️ testValidate
+```
+### ✔️ <a id="user-content-r0s53" href="#r0s53">org.apache.pulsar.client.impl.MessageChecksumTest</a>
+```
+✔️ testChecksumCompatibilityInMixedVersionBrokerCluster
+✔️ testTamperingMessageIsDetected
+✔️ testChecksumCompatibilityInMixedVersionBrokerCluster
+```
+### ✔️ <a id="user-content-r0s54" href="#r0s54">org.apache.pulsar.client.impl.MessageChunkingTest</a>
+```
+✔️ testPublishWithFailure
+✔️ testInvalidUseCaseForChunking
+✔️ testLargeMessage
+✔️ testExpireIncompleteChunkMessage
+✔️ testInvalidConfig
+✔️ testLargeMessageAckTimeOut
+✔️ testLargeMessageAckTimeOut
+✔️ testLargeMessage
+✖️ testMaxPendingChunkMessages
+```
+### ✔️ <a id="user-content-r0s55" href="#r0s55">org.apache.pulsar.client.impl.MessageParserTest</a>
+```
+✔️ testWithoutBatches
+✔️ testWithBatches
+```
+### ✔️ <a id="user-content-r0s56" href="#r0s56">org.apache.pulsar.client.impl.MultiTopicsReaderTest</a>
+```
+✔️ testReadMessageWithBatchingWithMessageInclusive
+✔️ testKeyHashRangeReader
+✔️ testRemoveSubscriptionForReaderNeedRemoveCursor
+✔️ testReadMessageWithBatching
+✔️ testReadMessageWithoutBatchingWithMessageInclusive
+✔️ testMultiReaderSeek
+✔️ testReadMessageWithoutBatching
+✔️ testReaderWithTimeLong
+```
+### ✔️ <a id="user-content-r0s57" href="#r0s57">org.apache.pulsar.client.impl.NegativeAcksTest</a>
+```
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+✔️ testNegativeAcks
+```
+### ✔️ <a id="user-content-r0s58" href="#r0s58">org.apache.pulsar.client.impl.PatternTopicsConsumerImplTest</a>
+```
+✔️ testStartEmptyPatternConsumer
+✔️ testBinaryProtoToGetTopicsOfNamespaceAll
+✔️ testPatternTopicsSubscribeWithBuilderFail
+✔️ testPubRateOnNonPersistent
+✔️ testTopicDeletion
+✔️ testAutoUnbubscribePatternConsumer
+✔️ testTopicsPatternFilter
+✔️ testBinaryProtoToGetTopicsOfNamespaceNonPersistent
+✔️ testBinaryProtoToGetTopicsOfNamespacePersistent
+✔️ testTopicsListMinus
+✔️ testAutoSubscribePatternConsumer
+```
+### ✔️ <a id="user-content-r0s59" href="#r0s59">org.apache.pulsar.client.impl.PerMessageUnAcknowledgedRedeliveryTest</a>
+```
+✔️ testSharedAckedNormalTopic
+✔️ testUnAckedMessageTrackerSize
+✔️ testSharedAckedPartitionedTopic
+✔️ testExclusiveAckedNormalTopic
+✔️ testFailoverAckedNormalTopic
+```
+### ✔️ <a id="user-content-r0s60" href="#r0s60">org.apache.pulsar.client.impl.PulsarMultiHostClientTest</a>
+```
+✔️ testMultiHostUrlRetrySuccess
+✔️ testGetPartitionedTopicDataTimeout
+✔️ testGetPartitionedTopicMetaData
+```
+### ✔️ <a id="user-content-r0s61" href="#r0s61">org.apache.pulsar.client.impl.RawMessageSerDeserTest</a>
+```
+✔️ testSerializationAndDeserialization
+```
+### ✔️ <a id="user-content-r0s62" href="#r0s62">org.apache.pulsar.client.impl.SchemaDeleteTest</a>
+```
+✔️ createTopicDeleteTopicCreateTopic
+```
+### ✔️ <a id="user-content-r0s63" href="#r0s63">org.apache.pulsar.client.impl.SequenceIdWithErrorTest</a>
+```
+✔️ testCheckSequenceId
+✔️ testDeleteTopicWithMissingData
+✔️ testTopicWithWildCardChar
+✖️ testCrashBrokerWithoutCursorLedgerLeak
+✖️ testSkipCorruptDataLedger
+```
+### ✔️ <a id="user-content-r0s64" href="#r0s64">org.apache.pulsar.client.impl.TopicDoesNotExistsTest</a>
+```
+✔️ testCreateConsumerOnNotExistsTopic
+✔️ testCreateProducerOnNotExistsTopic
+```
+### ✔️ <a id="user-content-r0s65" href="#r0s65">org.apache.pulsar.client.impl.TopicFromMessageTest</a>
+```
+✔️ testSingleTopicConsumerNoBatchFullName
+✔️ testMultiTopicConsumerBatchShortName
+✔️ testSingleTopicConsumerNoBatchShortName
+✔️ testMultiTopicConsumerNoBatchShortName
+✔️ testSingleTopicConsumerBatchShortName
+```
+### ✔️ <a id="user-content-r0s66" href="#r0s66">org.apache.pulsar.client.impl.TopicsConsumerImplTest</a>
+```
+✔️ testTopicAutoUpdatePartitions
+✔️ testDifferentTopicsNameSubscribe
+✔️ testGetLastMessageId
+✔️ testConsumerUnackedRedelivery
+✔️ testSubscriptionMustCompleteWhenOperationTimeoutOnMultipleTopics
+✔️ testConsumerDistributionInFailoverSubscriptionWhenUpdatePartitions
+✔️ multiTopicsInDifferentNameSpace
+✔️ testDefaultBacklogTTL
+✔️ testGetConsumersAndGetTopics
+✔️ testSubscribeUnsubscribeSingleTopic
+✔️ testResubscribeSameTopic
+✔️ testSyncProducerAndConsumer
+✔️ testPartitionsUpdatesForMultipleTopics
+✔️ testTopicsNameSubscribeWithBuilderFail
+✔️ testMultiTopicsMessageListener
+✔️ testTopicNameValid
+✔️ testAsyncConsumer
+```
+### ✔️ <a id="user-content-r0s67" href="#r0s67">org.apache.pulsar.client.impl.UnAcknowledgedMessagesTimeoutTest</a>
+```
+✔️ testCheckUnAcknowledgedMessageTimer
+✔️ testExclusiveSingleAckedNormalTopic
+✔️ testFailoverSingleAckedPartitionedTopic
+✔️ testSharedSingleAckedPartitionedTopic
+✔️ testAckTimeoutMinValue
+✔️ testExclusiveCumulativeAckedNormalTopic
+✔️ testSingleMessageBatch
+```
+### ✔️ <a id="user-content-r0s68" href="#r0s68">org.apache.pulsar.client.impl.ZeroQueueSizeTest</a>
+```
+✔️ zeroQueueSizeSharedSubscription
+✔️ testPauseAndResume
+✔️ testZeroQueueSizeMessageRedeliveryForAsyncReceive
+✔️ zeroQueueSizeConsumerListener
+✔️ zeroQueueSizeFailoverSubscription
+✔️ validQueueSizeConfig
+✔️ zeroQueueSizeNormalConsumer
+✔️ zeroQueueSizeReceieveAsyncInCompatibility
+✔️ InvalidQueueSizeConfig
+✔️ testZeroQueueSizeMessageRedeliveryForListener
+✔️ testZeroQueueSizeMessageRedelivery
+✔️ zeroQueueSizePartitionedTopicInCompatibility
+✔️ testFailedZeroQueueSizeBatchMessage
+✔️ testPauseAndResumeWithUnloading
+```
+### ✔️ <a id="user-content-r0s69" href="#r0s69">org.apache.pulsar.common.api.raw.RawMessageImplTest</a>
+```
+✔️ testGetProperties
+```
+### ✔️ <a id="user-content-r0s70" href="#r0s70">org.apache.pulsar.common.compression.CommandsTest</a>
+```
+✔️ testChecksumSendCommand
+```
+### ✔️ <a id="user-content-r0s71" href="#r0s71">org.apache.pulsar.common.compression.CompressorCodecBackwardCompatTest</a>
+```
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+```
+### ✔️ <a id="user-content-r0s72" href="#r0s72">org.apache.pulsar.common.compression.CompressorCodecTest</a>
+```
+✔️ testCompressDecompress
+✔️ testMultpileUsages
+✔️ testMultpileUsages
+✔️ testCompressDecompress
+✔️ testMultpileUsages
+✔️ testCompressDecompress
+✔️ testMultpileUsages
+✔️ testCompressDecompress
+✔️ testDecompressFromSampleBuffer
+✔️ testDecompressReadonlyByteBuf
+✔️ testDecompressReadonlyByteBuf
+✔️ testCodecProvider
+✔️ testEmptyInput
+✔️ testEmptyInput
+✔️ testCompressDecompress
+✔️ testCodecProvider
+✔️ testDecompressFromSampleBuffer
+✔️ testMultpileUsages
+✔️ testCodecProvider
+✔️ testEmptyInput
+✔️ testDecompressReadonlyByteBuf
+✔️ testCompressDecompress
+✔️ testDecompressReadonlyByteBuf
+✔️ testCompressDecompress
+✔️ testCompressDecompress
+✔️ testMultpileUsages
+✔️ testEmptyInput
+✔️ testDecompressReadonlyByteBuf
+✔️ testDecompressFromSampleBuffer
+✔️ testDecompressFromSampleBuffer
+✔️ testDecompressFromSampleBuffer
+✔️ testDecompressReadonlyByteBuf
+✔️ testDecompressReadonlyByteBuf
+✔️ testMultpileUsages
+✔️ testCompressDecompress
+✔️ testCodecProvider
+✔️ testMultpileUsages
+✔️ testCompressDecompress
+✔️ testMultpileUsages
+✔️ testDecompressReadonlyByteBuf
+✔️ testEmptyInput
+✔️ testCodecProvider
+✔️ testDecompressReadonlyByteBuf
+✔️ testDecompressReadonlyByteBuf
+✔️ testMultpileUsages
+```
+### ✔️ <a id="user-content-r0s73" href="#r0s73">org.apache.pulsar.common.compression.Crc32cChecksumTest</a>
+```
+✔️ testCrc32cHardware
+✔️ testCrc32cDirectMemoryHardware
+✔️ testCrc32c
+✔️ testCrc32cSoftware
+✔️ testCrc32cIncremental
+✔️ testCrc32cIncrementalUsingProvider
+```
+### ✔️ <a id="user-content-r0s74" href="#r0s74">org.apache.pulsar.common.lookup.data.LookupDataTest</a>
+```
+✔️ testLoadReportSerialization
+✔️ testUrlEncoder
+✔️ serializeToJsonTest
+✔️ withConstructor
+```
+### ✔️ <a id="user-content-r0s75" href="#r0s75">org.apache.pulsar.common.naming.MetadataTests</a>
+```
+✔️ testInvalidMetadata
+✔️ testValidMetadata
+```
+### ✔️ <a id="user-content-r0s76" href="#r0s76">org.apache.pulsar.common.naming.NamespaceBundlesTest</a>
+```
+✔️ testConstructor
+✔️ testSplitBundleInTwo
+✔️ testsplitBundles
+✔️ testFindBundle
+✔️ testSplitBundleByFixBoundary
+```
+### ✔️ <a id="user-content-r0s77" href="#r0s77">org.apache.pulsar.common.naming.NamespaceBundleTest</a>
+```
+✔️ testIncludes
+✔️ testGetBundle
+✔️ testCompareTo
+✔️ testConstructor
+✔️ testToString
+✔️ testEquals
+```
+### ✔️ <a id="user-content-r0s78" href="#r0s78">org.apache.pulsar.common.naming.NamespaceNameTest</a>
+```
+✔️ namespace
+✔️ testNewScheme
+```
+### ✔️ <a id="user-content-r0s79" href="#r0s79">org.apache.pulsar.common.naming.ServiceConfigurationTest</a>
+```
+✔️ testOptionalSettingPresent
+✔️ testOptionalSettingEmpty
+✔️ testInit
+✔️ testInitFailure
+```
+### ✔️ <a id="user-content-r0s80" href="#r0s80">org.apache.pulsar.common.naming.TopicNameTest</a>
+```
+✔️ testShortTopicName
+✔️ topic
+✔️ testTopicNameWithoutCluster
+✔️ testDecodeEncode
+```
+### ✔️ <a id="user-content-r0s81" href="#r0s81">org.apache.pulsar.common.net.ServiceURITest</a>
+```
+✔️ testEmptyServiceUriString
+✔️ testMultipleHostsSemiColon
+✔️ testInvalidServiceUris
+✔️ testMultipleHostsWithoutHttpPorts
+✔️ testRootPath
+✔️ testMultipleHostsMixedPorts
+✔️ testMultipleHostsWithoutPulsarTlsPorts
+✔️ testUserInfoWithMultipleHosts
+✔️ testMultipleHostsComma
+✔️ testMultipleHostsMixed
+✔️ testUserInfo
+✔️ testIpv6UriWithoutPulsarPort
+✔️ testMultiIpv6Uri
+✔️ testMultiIpv6UriWithoutPulsarPort
+✔️ testEmptyPath
+✔️ testNullServiceUriString
+✔️ testNullServiceUriInstance
+✔️ testMissingServiceName
+✔️ testMultipleHostsWithoutHttpsPorts
+✔️ testMultipleHostsWithoutPulsarPorts
+✔️ testIpv6Uri
+```
+### ✔️ <a id="user-content-r0s82" href="#r0s82">org.apache.pulsar.common.policies.data.AutoFailoverPolicyDataTest</a>
+```
+✔️ testAutoFailoverPolicyData
+```
+### ✔️ <a id="user-content-r0s83" href="#r0s83">org.apache.pulsar.common.policies.data.AutoFailoverPolicyTypeTest</a>
+```
+✔️ testAutoFailoverPolicyType
+```
+### ✔️ <a id="user-content-r0s84" href="#r0s84">org.apache.pulsar.common.policies.data.AutoTopicCreationOverrideTest</a>
+```
+✔️ testInvalidTopicType
+✔️ testNumPartitionsTooLow
+✔️ testNumPartitionsNotSet
+✔️ testValidOverrideNonPartitioned
+✔️ testNumPartitionsOnNonPartitioned
+✔️ testValidOverridePartitioned
+```
+### ✔️ <a id="user-content-r0s85" href="#r0s85">org.apache.pulsar.common.policies.data.BacklogQuotaTest</a>
+```
+✔️ testBacklogQuotaIdentity
+```
+### ✔️ <a id="user-content-r0s86" href="#r0s86">org.apache.pulsar.common.policies.data.ClusterDataTest</a>
+```
+✔️ simple
+```
+### ✔️ <a id="user-content-r0s87" href="#r0s87">org.apache.pulsar.common.policies.data.ConsumerStatsTest</a>
+```
+✔️ testConsumerStats
+```
+### ✔️ <a id="user-content-r0s88" href="#r0s88">org.apache.pulsar.common.policies.data.EnsemblePlacementPolicyConfigTest</a>
+```
+✔️ testDecodeFailed
+✔️ testEncodeDecodeSuccessfully
+```
+### ✔️ <a id="user-content-r0s89" href="#r0s89">org.apache.pulsar.common.policies.data.LocalPolicesTest</a>
+```
+✔️ testLocalPolices
+```
+### ✔️ <a id="user-content-r0s90" href="#r0s90">org.apache.pulsar.common.policies.data.NamespaceIsolationDataTest</a>
+```
+✔️ testNamespaceIsolationData
+```
+### ✔️ <a id="user-content-r0s91" href="#r0s91">org.apache.pulsar.common.policies.data.NamespaceOwnershipStatusTest</a>
+```
+✔️ testSerialization
+```
+### ✔️ <a id="user-content-r0s92" href="#r0s92">org.apache.pulsar.common.policies.data.OffloadPoliciesTest</a>
+```
+✔️ testGcsConfiguration
+✔️ mergeTest
+✔️ compatibleWithConfigFileTest
+✔️ testCreateByProperties
+✔️ testS3Configuration
+✔️ oldPoliciesCompatibleTest
+```
+### ✔️ <a id="user-content-r0s93" href="#r0s93">org.apache.pulsar.common.policies.data.PartitionedTopicStatsTest</a>
+```
+✔️ testPartitionedTopicStats
+```
+### ✔️ <a id="user-content-r0s94" href="#r0s94">org.apache.pulsar.common.policies.data.PersistencePoliciesTest</a>
+```
+✔️ testPersistencePolicies
+```
+### ✔️ <a id="user-content-r0s95" href="#r0s95">org.apache.pulsar.common.policies.data.PersistentOfflineTopicStatsTest</a>
+```
+✔️ testPersistentOfflineTopicStats
+```
+### ✔️ <a id="user-content-r0s96" href="#r0s96">org.apache.pulsar.common.policies.data.PersistentTopicStatsTest</a>
+```
+✔️ testPersistentTopicStatsAggregation
+✔️ testPersistentTopicStats
+```
+### ✔️ <a id="user-content-r0s97" href="#r0s97">org.apache.pulsar.common.policies.data.PoliciesDataTest</a>
+```
+✔️ propertyAdmin
+✔️ policies
+✔️ bundlesData
+✔️ bundlesPolicies
+```
+### ✔️ <a id="user-content-r0s98" href="#r0s98">org.apache.pulsar.common.policies.data.PublisherStatsTest</a>
+```
+✔️ testPublisherStats
+✔️ testPublisherStatsAggregation
+```
+### ✔️ <a id="user-content-r0s99" href="#r0s99">org.apache.pulsar.common.policies.data.ReplicatorStatsTest</a>
+```
+✔️ testReplicatorStatsAdd
+✔️ testReplicatorStatsNull
+```
+### ✔️ <a id="user-content-r0s100" href="#r0s100">org.apache.pulsar.common.policies.data.ResourceQuotaTest</a>
+```
+✔️ testResourceQuotaDefault
+✔️ testResourceQuotaEqual
+```
+### ✔️ <a id="user-content-r0s101" href="#r0s101">org.apache.pulsar.common.policies.data.RetentionPolicesTest</a>
+```
+✔️ testRetentionPolices
+```
+### ✔️ <a id="user-content-r0s102" href="#r0s102">org.apache.pulsar.common.policies.impl.AutoFailoverPolicyFactoryTest</a>
+```
+✔️ testAutoFailoverPolicyFactory
+```
+### ✔️ <a id="user-content-r0s103" href="#r0s103">org.apache.pulsar.common.policies.impl.MinAvailablePolicyTest</a>
+```
+✔️ testMinAvailablePolicty
+```
+### ✔️ <a id="user-content-r0s104" href="#r0s104">org.apache.pulsar.common.policies.impl.NamespaceIsolationPoliciesTest</a>
+```
+✔️ testBrokerAssignment
+✔️ testGetNamespaceIsolationPolicyByName
+✔️ testDeletePolicy
+✔️ testSetPolicy
+✔️ testJsonSerialization
+✔️ testDefaultConstructor
+✔️ testGetNamespaceIsolationPolicyByNamespace
+```
+### ✔️ <a id="user-content-r0s105" href="#r0s105">org.apache.pulsar.common.policies.impl.NamespaceIsolationPolicyImplTest</a>
+```
+✔️ testFindBrokers
+✔️ testGetSecondaryBrokers
+✔️ testShouldFailover
+✔️ testGetPrimaryBrokers
+✔️ testGetAvailablePrimaryBrokers
+✔️ testConstructor
+✔️ testIsPrimaryOrSecondaryBroker
+```
+### ✔️ <a id="user-content-r0s106" href="#r0s106">org.apache.pulsar.common.protocol.ByteBufPairTest</a>
+```
+✔️ testEncoder
+✔️ testDoubleByteBuf
+```
+### ✔️ <a id="user-content-r0s107" href="#r0s107">org.apache.pulsar.common.protocol.CommandUtilsTests</a>
+```
+✔️ testSkipBrokerEntryMetadata
+✔️ testPeekBrokerEntryMetadata
+✔️ testParseBrokerEntryMetadata
+✔️ testMetadataFromCommandSubscribe
+✔️ testMetadataFromCommandProducer
+✔️ testAddBrokerEntryMetadata
+✔️ testByteBufComposite
+```
+### ✔️ <a id="user-content-r0s108" href="#r0s108">org.apache.pulsar.common.protocol.MarkersTest</a>
+```
+✔️ testSnapshot
+✔️ testTxnAbortMarker
+✔️ testUpdate
+✔️ testTxnCommitMarker
+✔️ testSnapshotRequest
+✔️ testSnapshotResponse
+```
+### ✔️ <a id="user-content-r0s109" href="#r0s109">org.apache.pulsar.common.protocol.PulsarDecoderTest</a>
+```
+✔️ testChannelRead
+```
+### ✔️ <a id="user-content-r0s110" href="#r0s110">org.apache.pulsar.common.stats.JvmDefaultGCMetricsLoggerTest</a>
+```
+✔️ testInvokeJVMInternals
+```
+### ✔️ <a id="user-content-r0s111" href="#r0s111">org.apache.pulsar.common.util.collections.BitSetRecyclableRecyclableTest</a>
+```
+✔️ testResetWords
+✔️ testRecycle
+```
+### ✔️ <a id="user-content-r0s112" href="#r0s112">org.apache.pulsar.common.util.collections.ConcurrentBitSetRecyclableTest</a>
+```
+✔️ testRecycle
+✔️ testGenerateByBitSet
+```
+### ✔️ <a id="user-content-r0s113" href="#r0s113">org.apache.pulsar.common.util.collections.ConcurrentLongHashMapTest</a>
+```
+✔️ testRehashingWithDeletes
+✔️ concurrentInsertionsAndReads
+✔️ testRemove
+✔️ testRehashing
+✔️ simpleInsertions
+✔️ testComputeIfAbsent
+✔️ testConstructor
+✔️ testPutIfAbsent
+✔️ testIteration
+✔️ testHashConflictWithDeletion
+✔️ concurrentInsertions
+✔️ stressConcurrentInsertionsAndReads
+✔️ testNegativeUsedBucketCount
+```
+### ✔️ <a id="user-content-r0s114" href="#r0s114">org.apache.pulsar.common.util.collections.ConcurrentLongPairSetTest</a>
+```
+✔️ concurrentInsertionsAndReads
+✔️ testEqualsObjects
+✔️ testIfRemoval
+✔️ testRehashing
+✔️ testToString
+✔️ testRemove
+✔️ testItems
+✔️ testRehashingWithDeletes
+✔️ testHashConflictWithDeletion
+✔️ testIteration
+✔️ simpleInsertions
+✔️ testRehashingRemoval
+✔️ testRemoval
+✔️ testConstructor
+✔️ concurrentInsertions
+```
+### ✔️ <a id="user-content-r0s115" href="#r0s115">org.apache.pulsar.common.util.collections.ConcurrentOpenHashMapTest</a>
+```
+✔️ testRemove
+✔️ simpleInsertions
+✔️ testPutIfAbsent
+✔️ concurrentInsertions
+✔️ testHashConflictWithDeletion
+✔️ testRehashingWithDeletes
+✔️ testComputeIfAbsent
+✔️ testRehashing
+✔️ testIteration
+✔️ testEqualsKeys
+✔️ concurrentInsertionsAndReads
+✔️ testConstructor
+```
+### ✔️ <a id="user-content-r0s116" href="#r0s116">org.apache.pulsar.common.util.collections.ConcurrentOpenHashSetTest</a>
+```
+✔️ concurrentInsertions
+✔️ testRehashing
+✔️ testRemoval
+✔️ testEqualsObjects
+✔️ testHashConflictWithDeletion
+✔️ testConstructor
+✔️ concurrentInsertionsAndReads
+✔️ testIteration
+✔️ simpleInsertions
+✔️ testRehashingWithDeletes
+✔️ testRemove
+```
+### ✔️ <a id="user-content-r0s117" href="#r0s117">org.apache.pulsar.common.util.collections.ConcurrentOpenLongPairRangeSetTest</a>
+```
+✔️ testAddForDifferentKey
+✔️ testToString
+✔️ testCacheFlagConflict
+✔️ testDeleteWithLeastMost
+✔️ testDeleteForDifferentKey
+✔️ testLastRange
+✔️ testAddCompareCompareWithGuava
+✔️ testSpanWithGuava
+✔️ testDeleteCompareWithGuava
+✔️ testFirstRange
+✔️ testAddForSameKey
+✔️ testDeleteWithAtMost
+✔️ testRangeContaining
+```
+### ✔️ <a id="user-content-r0s118" href="#r0s118">org.apache.pulsar.common.util.collections.ConcurrentSortedLongPairSetTest</a>
+```
+✔️ concurrentInsertions
+✔️ testIfRemoval
+✔️ testRemoval
+✔️ testRemove
+✔️ testItems
+✔️ testEqualsObjects
+✔️ simpleInsertions
+✔️ testIteration
+✔️ testToString
+```
+### ✔️ <a id="user-content-r0s119" href="#r0s119">org.apache.pulsar.common.util.collections.FieldParserTest</a>
+```
+✔️ testUpdateObject
+✔️ testConversion
+```
+### ✔️ <a id="user-content-r0s120" href="#r0s120">org.apache.pulsar.common.util.collections.GrowableArrayBlockingQueueTest</a>
+```
+✔️ removeTest
+✔️ growArray
+✔️ pollTimeout
+✔️ simple
+✔️ pollTimeout2
+✔️ blockingTake
+```
+### ✔️ <a id="user-content-r0s121" href="#r0s121">org.apache.pulsar.common.util.collections.GrowablePriorityLongPairQueueTest</a>
+```
+✔️ testItems
+✔️ testRemove
+✔️ testExpandQueue
+✔️ testInsertAndRemove
+✔️ testEqualsObjects
+✔️ testExpandRemoval
+✔️ testIteration
+✔️ simpleInsertions
+✔️ concurrentInsertions
+✔️ testConstructor
+✔️ testSetWithDuplicateInsert
+✔️ testExpandWithDeletes
+✔️ concurrentInsertionsAndReads
+✔️ testRemoval
+✔️ testIfRemoval
+```
+### ✔️ <a id="user-content-r0s122" href="#r0s122">org.apache.pulsar.common.util.collections.TripleLongPriorityQueueTest</a>
+```
+✔️ testQueue
+✔️ testCheckForEmpty
+✔️ testCompareWithSamePrefix
+```
+### ✔️ <a id="user-content-r0s123" href="#r0s123">org.apache.pulsar.common.util.FieldParserTest</a>
+```
+✔️ testMap
+```
+### ✔️ <a id="user-content-r0s124" href="#r0s124">org.apache.pulsar.common.util.FileModifiedTimeUpdaterTest</a>
+```
+✔️ testFileNotModified
+✔️ testFileModified
+✔️ testFileModified
+✔️ testFileNotModified
+✔️ testFileModified
+✔️ testFileNotModified
+```
+### ✔️ <a id="user-content-r0s125" href="#r0s125">org.apache.pulsar.common.util.netty.ChannelFuturesTest</a>
+```
+✔️ toCompletableFuture_shouldCompleteExceptionally_channelFutureCompletedAfter
+✔️ toCompletableFuture_shouldCompleteSuccessfully_channelFutureCompletedAfter
+✔️ toCompletableFuture_shouldCompleteSuccessfully_channelFutureCompletedBefore
+✔️ toCompletableFuture_shouldCompleteExceptionally_channelFutureCompletedBefore
+✔️ toCompletableFuture_shouldRequireNonNullArgument
+```
+### ✔️ <a id="user-content-r0s126" href="#r0s126">org.apache.pulsar.common.util.RateLimiterTest</a>
+```
+✔️ testMultipleTryAcquire
+✔️ testRateLimiterWithPermitUpdater
+✔️ testTryAcquire
+✔️ testTryAcquireNoPermits
+✔️ testClose
+✔️ testResetRate
+✔️ testMultipleAcquire
+✔️ testAcquire
+✔️ testInvalidRenewTime
+✔️ testRateLimiterWithFunction
+✔️ testAcquireBlock
+```
+### ✔️ <a id="user-content-r0s127" href="#r0s127">org.apache.pulsar.common.util.ReflectionsTest</a>
+```
+✔️ testCreateInstanceNoNoArgConstructor
+✔️ testCreateInstanceConstructorThrowsException
+✔️ testCreateInstanceAbstractClass
+✔️ testCreateTypedInstanceUnassignableClass
+✔️ testCreateInstanceClassNotFound
+✔️ testCreateTypedInstanceConstructorThrowsException
+✔️ testClassExists
+✔️ testCreateTypedInstanceAbstractClass
+✔️ testCreateTypedInstanceClassNotFound
+✔️ testCreateTypedInstanceNoNoArgConstructor
+✔️ testLoadClass
+✔️ testClassInJarImplementsIface
+```
+### ✔️ <a id="user-content-r0s128" href="#r0s128">org.apache.pulsar.common.util.RelativeTimeUtilTest</a>
+```
+✔️ testParseRelativeTime
+```
+### ✔️ <a id="user-content-r0s129" href="#r0s129">org.apache.pulsar.discovery.service.web.DiscoveryServiceWebTest</a>
+```
+✔️ testRedirectUrlWithServerStarted
+```
+### ✔️ <a id="user-content-r0s130" href="#r0s130">org.apache.pulsar.functions.worker.PulsarFunctionE2ESecurityTest</a>
+```
+✔️ testAuthorizationWithAnonymousUser
+✔️ testAuthorization
+```
+### ✔️ <a id="user-content-r0s131" href="#r0s131">org.apache.pulsar.functions.worker.PulsarFunctionPublishTest</a>
+```
+✔️ testPulsarFunctionState
+✔️ testMultipleAddress
+✔️ testPulsarFunctionBKCleanup
+```
+### ✔️ <a id="user-content-r0s132" href="#r0s132">org.apache.pulsar.functions.worker.PulsarFunctionTlsTest</a>
+```
+✔️ testFunctionsCreation
+```
+### ✔️ <a id="user-content-r0s133" href="#r0s133">org.apache.pulsar.io.PulsarFunctionTlsTest</a>
+```
+✔️ testAuthorization
+```
+### ✔️ <a id="user-content-r0s134" href="#r0s134">org.apache.pulsar.proxy.server.AdminProxyHandlerTest</a>
+```
+✔️ replayableProxyContentProviderTest
+```
+### ✔️ <a id="user-content-r0s135" href="#r0s135">org.apache.pulsar.proxy.server.AuthedAdminProxyHandlerTest</a>
+```
+✔️ testAuthenticatedProxyAsNonAdmin
+```
+### ✔️ <a id="user-content-r0s136" href="#r0s136">org.apache.pulsar.proxy.server.FunctionWorkerRoutingTest</a>
+```
+✔️ testFunctionWorkerRedirect
+```
+### ✔️ <a id="user-content-r0s137" href="#r0s137">org.apache.pulsar.proxy.server.ProxyAdditionalServletTest</a>
+```
+✔️ test
+```
+### ✔️ <a id="user-content-r0s138" href="#r0s138">org.apache.pulsar.proxy.server.ProxyAuthenticatedProducerConsumerTest</a>
+```
+✔️ testTlsSyncProducerAndConsumer
+```
+### ✔️ <a id="user-content-r0s139" href="#r0s139">org.apache.pulsar.proxy.server.ProxyAuthenticationTest</a>
+```
+✔️ testAuthentication
+```
+### ✔️ <a id="user-content-r0s140" href="#r0s140">org.apache.pulsar.proxy.server.ProxyConnectionThrottlingTest</a>
+```
+✔️ testInboundConnection
+```
+### ✔️ <a id="user-content-r0s141" href="#r0s141">org.apache.pulsar.proxy.server.ProxyEnableHAProxyProtocolTest</a>
+```
+✔️ testSimpleProduceAndConsume
+```
+### ✔️ <a id="user-content-r0s142" href="#r0s142">org.apache.pulsar.proxy.server.ProxyForwardAuthDataTest</a>
+```
+✔️ testForwardAuthData
+```
+### ✔️ <a id="user-content-r0s143" href="#r0s143">org.apache.pulsar.proxy.server.ProxyIsAHttpProxyTest</a>
+```
+✔️ testProxyToEndsInSlash
+✔️ testStreaming
+✔️ testLongPath
+✔️ testLongPathInProxyTo
+✔️ testPathEndsInSlash
+✔️ testPathNotSpecified
+✔️ testTryingToUseExistingPath
+✔️ testMultipleRedirect
+✔️ testSingleRedirect
+✔️ testRedirectNotSpecified
+```
+### ✔️ <a id="user-content-r0s144" href="#r0s144">org.apache.pulsar.proxy.server.ProxyKeyStoreTlsTestWithAuth</a>
+```
+✔️ testProducerFailed
+✔️ testPartitions
+✔️ testProducer
+```
+### ✔️ <a id="user-content-r0s145" href="#r0s145">org.apache.pulsar.proxy.server.ProxyKeyStoreTlsTestWithoutAuth</a>
+```
+✔️ testPartitions
+✔️ testProducerFailed
+✔️ testProducer
+```
+### ✔️ <a id="user-content-r0s146" href="#r0s146">org.apache.pulsar.proxy.server.ProxyLookupThrottlingTest</a>
+```
+✔️ testLookup
+```
+### ✔️ <a id="user-content-r0s147" href="#r0s147">org.apache.pulsar.proxy.server.ProxyParserTest</a>
+```
+✔️ testRegexSubscription
+✔️ testProducerConsumer
+✔️ testProducer
+✔️ testPartitions
+✔️ testProtocolVersionAdvertisement
+```
+### ✔️ <a id="user-content-r0s148" href="#r0s148">org.apache.pulsar.proxy.server.ProxyRolesEnforcementTest</a>
+```
+✔️ testIncorrectRoles
+```
+### ✔️ <a id="user-content-r0s149" href="#r0s149">org.apache.pulsar.proxy.server.ProxyStatsTest</a>
+```
+✔️ testChangeLogLevel
+✔️ testConnectionsStats
+✔️ testTopicStats
+```
+### ✔️ <a id="user-content-r0s150" href="#r0s150">org.apache.pulsar.proxy.server.ProxyTest</a>
+```
+✔️ testPartitions
+✔️ testRegexSubscription
+✔️ testProtocolVersionAdvertisement
+✔️ testGetSchema
+✔️ testProducer
+✔️ testProducerConsumer
+```
+### ✔️ <a id="user-content-r0s151" href="#r0s151">org.apache.pulsar.proxy.server.ProxyTlsTest</a>
+```
+✔️ testProducer
+✔️ testPartitions
+```
+### ✔️ <a id="user-content-r0s152" href="#r0s152">org.apache.pulsar.proxy.server.ProxyTlsTestWithAuth</a>
+```
+✔️ testServiceStartup
+```
+### ✔️ <a id="user-content-r0s153" href="#r0s153">org.apache.pulsar.proxy.server.ProxyWithAuthorizationNegTest</a>
+```
+✔️ testProxyAuthorization
+```
+### ✔️ <a id="user-content-r0s154" href="#r0s154">org.apache.pulsar.proxy.server.ProxyWithAuthorizationTest</a>
+```
+✔️ tlsCiphersAndProtocols
+✔️ testTlsHostVerificationProxyToClient
+✔️ tlsCiphersAndProtocols
+✔️ testProxyAuthorization
+✔️ tlsCiphersAndProtocols
+✔️ testTlsHostVerificationProxyToBroker
+✔️ tlsCiphersAndProtocols
+✔️ tlsCiphersAndProtocols
+✔️ tlsCiphersAndProtocols
+✔️ testTlsHostVerificationProxyToBroker
+✔️ tlsCiphersAndProtocols
+✔️ testTlsHostVerificationProxyToClient
+✔️ tlsCiphersAndProtocols
+```
+### ✔️ <a id="user-content-r0s155" href="#r0s155">org.apache.pulsar.proxy.server.ProxyWithoutServiceDiscoveryTest</a>
+```
+✔️ testDiscoveryService
+```
+### ✔️ <a id="user-content-r0s156" href="#r0s156">org.apache.pulsar.proxy.server.SuperUserAuthedAdminProxyHandlerTest</a>
+```
+✔️ testAuthWithRandoCert
+✔️ testAuthenticatedProxyAsAdmin
+✔️ testAuthenticatedProxyAsNonAdmin
+```
+### ✔️ <a id="user-content-r0s157" href="#r0s157">org.apache.pulsar.proxy.server.UnauthedAdminProxyHandlerTest</a>
+```
+✔️ testUnauthenticatedProxy
+✔️ testVipStatus
+```
+### ✔️ <a id="user-content-r0s158" href="#r0s158">org.apache.pulsar.PulsarBrokerStarterTest</a>
+```
+✔️ testMainRunBookieNoConfig
+✔️ testLoadConfigWithException
+✔️ testMainWithNoArgument
+✔️ testLoadBalancerConfig
+✔️ testGlobalZooKeeperConfig
+✔️ testMainRunBookieRecoveryNoConfig
+✔️ testLoadConfig
+✔️ testMainEnableRunBookieThroughBrokerConfig
+✔️ testMainRunBookieAndAutoRecoveryNoConfig
+```
+### ✔️ <a id="user-content-r0s159" href="#r0s159">org.apache.pulsar.schema.compatibility.SchemaCompatibilityCheckTest</a>
+```
+✔️ testConsumerCompatibilityCheckCanReadLastTest
+✔️ testConsumerWithNotCompatibilitySchema
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testConsumerCompatibilityCheckCanReadLastTest
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testSchemaComparison
+✔️ testConsumerCompatibilityCheckCanReadLastTest
+✔️ testConsumerCompatibilityReadAllCheckTest
+✔️ testConsumerWithNotCompatibilitySchema
+✔️ testIsAutoUpdateSchema
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testConsumerCompatibilityReadAllCheckTest
+✔️ testIsAutoUpdateSchema
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testConsumerWithNotCompatibilitySchema
+✔️ testIsAutoUpdateSchema
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testConsumerWithNotCompatibilitySchema
+✔️ testProducerSendWithOldSchemaAndConsumerCanRead
+✔️ testIsAutoUpdateSchema
+✔️ testIsAutoUpdateSchema
+✔️ testConsumerCompatibilityCheckCanReadLastTest
+✔️ testIsAutoUpdateSchema
+```
+### ✔️ <a id="user-content-r0s160" href="#r0s160">org.apache.pulsar.schema.PartitionedTopicSchemaTest</a>
+```
+✔️ test
+```
+### ✔️ <a id="user-content-r0s161" href="#r0s161">org.apache.pulsar.schema.SchemaTest</a>
+```
+✔️ testIsUsingAvroSchemaParser
+✔️ testBytesSchemaDeserialize
+✔️ testMultiTopicSetSchemaProvider
+```
+### ✔️ <a id="user-content-r0s162" href="#r0s162">org.apache.pulsar.stats.client.PulsarBrokerStatsClientTest</a>
+```
+✔️ testServiceException
+✔️ testTopicInternalStats
+```
+### ✔️ <a id="user-content-r0s163" href="#r0s163">org.apache.pulsar.tests.EnumValuesDataProviderTest</a>
+```
+✔️ shouldFailIfEnumParameterIsMissing
+✔️ testEnumValuesProvider
+✔️ testEnumValuesProvider
+✔️ shouldDetermineEnumValuesFromMethod
+✔️ shouldContainAllEnumValues
+✔️ testEnumValuesProvider
+```
+### ✔️ <a id="user-content-r0s164" href="#r0s164">org.apache.pulsar.tests.TestRetrySupportBeforeMethodRetryTest</a>
+```
+✔️ shouldNotDoAnythingWhenThereIsBeforeAndAfterMethod
+✖️ shouldNotDoAnythingWhenThereIsBeforeAndAfterMethod
+✖️ shouldNotDoAnythingWhenThereIsBeforeAndAfterMethod
+✖️ shouldNotDoAnythingWhenThereIsBeforeAndAfterMethod
+✖️ shouldNotDoAnythingWhenThereIsBeforeAndAfterMethod
+```
+### ✔️ <a id="user-content-r0s165" href="#r0s165">org.apache.pulsar.tests.TestRetrySupportRetryTest</a>
+```
+✖️ shouldCallSetupBeforeRetrying
+✔️ shouldCallSetupBeforeRetrying
+✖️ shouldCallSetupBeforeRetrying
+✖️ shouldCallSetupBeforeRetrying
+✖️ shouldCallSetupBeforeRetrying
+```
+### ✔️ <a id="user-content-r0s166" href="#r0s166">org.apache.pulsar.tests.TestRetrySupportSuccessTest</a>
+```
+✔️ shouldCallSetupOnce1
+✔️ shouldCallSetupOnce3
+✔️ shouldCallSetupOnce2
+```
+### ✔️ <a id="user-content-r0s167" href="#r0s167">org.apache.pulsar.tests.ThreadDumpUtilTest</a>
+```
+✔️ testHelp
+✔️ testThreadDump
+```
+### ✔️ <a id="user-content-r0s168" href="#r0s168">org.apache.pulsar.utils.SimpleTextOutputStreamTest</a>
+```
+✔️ testBooleanFormat
+✔️ testDoubleFormat
+✔️ testLongFormat
+✔️ testString
+```
+### ✔️ <a id="user-content-r0s169" href="#r0s169">org.apache.pulsar.utils.StatsOutputStreamTest</a>
+```
+✔️ testLists
+✔️ testNamedObjects
+✔️ testNestedObjects
+✔️ testNamedLists
+✔️ testPairs
+✔️ testObjects
+```
+### ✔️ <a id="user-content-r0s170" href="#r0s170">org.apache.pulsar.websocket.proxy.ProxyAuthenticationTest</a>
+```
+✔️ unauthenticatedSocketTest
+✔️ authenticatedSocketTest
+✔️ statsTest
+✔️ anonymousSocketTest
+```
+### ✔️ <a id="user-content-r0s171" href="#r0s171">org.apache.pulsar.websocket.proxy.ProxyAuthorizationTest</a>
+```
+✔️ test
+```
+### ✔️ <a id="user-content-r0s172" href="#r0s172">org.apache.pulsar.websocket.proxy.ProxyConfigurationTest</a>
+```
+✔️ configTest
+✔️ configTest
+```
+### ✔️ <a id="user-content-r0s173" href="#r0s173">org.apache.pulsar.websocket.proxy.ProxyPublishConsumeTlsTest</a>
+```
+✔️ socketTest
+```
+### ✔️ <a id="user-content-r0s174" href="#r0s174">org.apache.pulsar.websocket.proxy.ProxyPublishConsumeWithoutZKTest</a>
+```
+✔️ socketTest
+```
+### ✔️ <a id="user-content-r0s175" href="#r0s175">org.apache.pulsar.websocket.proxy.v1.V1_ProxyAuthenticationTest</a>
+```
+✔️ anonymousSocketTest
+✔️ authenticatedSocketTest
+✔️ statsTest
+✔️ unauthenticatedSocketTest
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__outputs__/silent-notes-test-results.md b/test-reporter/__tests__/__outputs__/silent-notes-test-results.md
new file mode 100644
index 0000000..de01928
--- /dev/null
+++ b/test-reporter/__tests__/__outputs__/silent-notes-test-results.md
@@ -0,0 +1,136 @@
+![Tests passed successfully](https://img.shields.io/badge/tests-67%20passed%2C%2012%20skipped-success)
+## ✔️ <a id="user-content-r0" href="#r0">fixtures/external/SilentNotes.trx</a>
+**79** tests were completed in **1s** with **67** passed, **0** failed and **12** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[VanillaCloudStorageClientTest.CloudStorageCredentialsTest](#r0s0)|6✔️|||30ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.DropboxCloudStorageClientTest](#r0s1)|2✔️||3✖️|101ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.FtpCloudStorageClientTest](#r0s2)|4✔️||3✖️|166ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.GmxCloudStorageClientTest](#r0s3)|2✔️|||7ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.GoogleCloudStorageClientTest](#r0s4)|1✔️||3✖️|40ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.OnedriveCloudStorageClientTest](#r0s5)|1✔️||3✖️|15ms|
+|[VanillaCloudStorageClientTest.CloudStorageProviders.WebdavCloudStorageClientTest](#r0s6)|5✔️|||16ms|
+|[VanillaCloudStorageClientTest.CloudStorageTokenTest](#r0s7)|9✔️|||0ms|
+|[VanillaCloudStorageClientTest.OAuth2.AuthorizationResponseErrorTest](#r0s8)|3✔️|||3ms|
+|[VanillaCloudStorageClientTest.OAuth2.OAuth2UtilsTest](#r0s9)|9✔️|||12ms|
+|[VanillaCloudStorageClientTest.OAuth2CloudStorageClientTest](#r0s10)|5✔️|||13ms|
+|[VanillaCloudStorageClientTest.SecureStringExtensionsTest](#r0s11)|7✔️|||0ms|
+|[VanillaCloudStorageClientTest.SerializeableCloudStorageCredentialsTest](#r0s12)|13✔️|||43ms|
+### ✔️ <a id="user-content-r0s0" href="#r0s0">VanillaCloudStorageClientTest.CloudStorageCredentialsTest</a>
+```
+✔️ AreEqualWorksWithDifferentPassword
+✔️ AreEqualWorksWithSameContent
+✔️ CorrectlyConvertsSecureStringToString
+✔️ CorrectlyConvertsStringToSecureString
+✔️ ValidateAcceptsValidCredentials
+✔️ ValidateRejectsInvalidCredentials
+```
+### ✔️ <a id="user-content-r0s1" href="#r0s1">VanillaCloudStorageClientTest.CloudStorageProviders.DropboxCloudStorageClientTest</a>
+```
+✔️ FileLifecycleWorks
+✖️ ReallyDoFetchToken
+✖️ ReallyDoOpenAuthorizationPageInBrowser
+✖️ ReallyDoRefreshToken
+✔️ ThrowsAccessDeniedExceptionWithInvalidToken
+```
+### ✔️ <a id="user-content-r0s2" href="#r0s2">VanillaCloudStorageClientTest.CloudStorageProviders.FtpCloudStorageClientTest</a>
+```
+✔️ FileLifecycleWorks
+✔️ SanitizeCredentials_ChangesInvalidPrefix
+✔️ SecureSslConnectionWorks
+✔️ ThrowsWithHttpInsteadOfFtp
+✖️ ThrowsWithInvalidPassword
+✖️ ThrowsWithInvalidUrl
+✖️ ThrowsWithInvalidUsername
+```
+### ✔️ <a id="user-content-r0s3" href="#r0s3">VanillaCloudStorageClientTest.CloudStorageProviders.GmxCloudStorageClientTest</a>
+```
+✔️ ChoosesCorrectUrlForGmxComEmail
+✔️ ChoosesCorrectUrlForGmxNetEmail
+```
+### ✔️ <a id="user-content-r0s4" href="#r0s4">VanillaCloudStorageClientTest.CloudStorageProviders.GoogleCloudStorageClientTest</a>
+```
+✔️ FileLifecycleWorks
+✖️ ReallyDoFetchToken
+✖️ ReallyDoOpenAuthorizationPageInBrowser
+✖️ ReallyDoRefreshToken
+```
+### ✔️ <a id="user-content-r0s5" href="#r0s5">VanillaCloudStorageClientTest.CloudStorageProviders.OnedriveCloudStorageClientTest</a>
+```
+✔️ FileLifecycleWorks
+✖️ ReallyDoFetchToken
+✖️ ReallyDoOpenAuthorizationPageInBrowser
+✖️ ReallyDoRefreshToken
+```
+### ✔️ <a id="user-content-r0s6" href="#r0s6">VanillaCloudStorageClientTest.CloudStorageProviders.WebdavCloudStorageClientTest</a>
+```
+✔️ FileLifecycleWorks
+✔️ ParseGmxWebdavResponseCorrectly
+✔️ ParseStratoWebdavResponseCorrectly
+✔️ ThrowsWithInvalidPath
+✔️ ThrowsWithInvalidUsername
+```
+### ✔️ <a id="user-content-r0s7" href="#r0s7">VanillaCloudStorageClientTest.CloudStorageTokenTest</a>
+```
+✔️ AreEqualWorksWithNullDate
+✔️ AreEqualWorksWithSameContent
+✔️ NeedsRefreshReturnsFalseForTokenFlow
+✔️ NeedsRefreshReturnsFalseIfNotExpired
+✔️ NeedsRefreshReturnsTrueIfExpired
+✔️ NeedsRefreshReturnsTrueIfNoExpirationDate
+✔️ SetExpiryDateBySecondsWorks
+✔️ SetExpiryDateBySecondsWorksWithNull
+✔️ SetExpiryDateBySecondsWorksWithVeryShortPeriod
+```
+### ✔️ <a id="user-content-r0s8" href="#r0s8">VanillaCloudStorageClientTest.OAuth2.AuthorizationResponseErrorTest</a>
+```
+✔️ ParsesAllErrorCodesCorrectly
+✔️ ParsesNullErrorCodeCorrectly
+✔️ ParsesUnknownErrorCodeCorrectly
+```
+### ✔️ <a id="user-content-r0s9" href="#r0s9">VanillaCloudStorageClientTest.OAuth2.OAuth2UtilsTest</a>
+```
+✔️ BuildAuthorizationRequestUrlEscapesParameters
+✔️ BuildAuthorizationRequestUrlLeavesOutOptionalParameters
+✔️ BuildAuthorizationRequestUrlThrowsWithMissingRedirectUrlForTokenFlow
+✔️ BuildAuthorizationRequestUrlUsesAllParameters
+✔️ BuildAuthorizationRequestUrlUsesCodeVerifier
+✔️ ParseRealWorldDropboxRejectResponse
+✔️ ParseRealWorldDropboxSuccessResponse
+✔️ ParseRealWorldGoogleRejectResponse
+✔️ ParseRealWorldGoogleSuccessResponse
+```
+### ✔️ <a id="user-content-r0s10" href="#r0s10">VanillaCloudStorageClientTest.OAuth2CloudStorageClientTest</a>
+```
+✔️ BuildOAuth2AuthorizationRequestUrlWorks
+✔️ FetchTokenCanInterpretGoogleResponse
+✔️ FetchTokenReturnsNullForDeniedAccess
+✔️ FetchTokenThrowsWithWrongState
+✔️ RefreshTokenCanInterpretGoogleResponse
+```
+### ✔️ <a id="user-content-r0s11" href="#r0s11">VanillaCloudStorageClientTest.SecureStringExtensionsTest</a>
+```
+✔️ AreEqualsWorksCorrectly
+✔️ CorrectlyConvertsSecureStringToString
+✔️ CorrectlyConvertsSecureStringToUnicodeBytes
+✔️ CorrectlyConvertsSecureStringToUtf8Bytes
+✔️ CorrectlyConvertsStringToSecureString
+✔️ CorrectlyConvertsUnicodeBytesToSecureString
+✔️ CorrectlyConvertsUtf8BytesToSecureString
+```
+### ✔️ <a id="user-content-r0s12" href="#r0s12">VanillaCloudStorageClientTest.SerializeableCloudStorageCredentialsTest</a>
+```
+✔️ DecryptAfterDesrializationCanReadAllPropertiesBack
+✔️ DecryptAfterDesrializationRespectsNullProperties
+✔️ EncryptBeforeSerializationProtectsAllNecessaryProperties
+✔️ EncryptBeforeSerializationRespectsNullProperties
+✔️ SerializedDatacontractCanBeReadBack
+✔️ SerializedDatacontractDoesNotContainNullProperties
+✔️ SerializedDatacontractDoesNotContainPlaintextData
+✔️ SerializedJsonCanBeReadBack
+✔️ SerializedJsonDoesNotContainNullProperties
+✔️ SerializedJsonDoesNotContainPlaintextData
+✔️ SerializedXmlCanBeReadBack
+✔️ SerializedXmlDoesNotContainNullProperties
+✔️ SerializedXmlDoesNotContainPlaintextData
+```
\ No newline at end of file
diff --git a/test-reporter/__tests__/__snapshots__/dart-json.test.ts.snap b/test-reporter/__tests__/__snapshots__/dart-json.test.ts.snap
new file mode 100644
index 0000000..847db6a
--- /dev/null
+++ b/test-reporter/__tests__/__snapshots__/dart-json.test.ts.snap
@@ -0,0 +1,2052 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`dart-json tests matches report snapshot 1`] = `
+TestRunResult {
+  "path": "fixtures/dart-json.json",
+  "suites": Array [
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "Test 1",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Passing test",
+              "result": "success",
+              "time": 36,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "Test 1 Test 1.1",
+          "tests": Array [
+            TestCaseResult {
+              "error": Object {
+                "details": "package:test_api          expect
+test\\\\main_test.dart 13:9  main.<fn>.<fn>.<fn>
+",
+                "line": 13,
+                "message": "Expected: <2>
+  Actual: <1>
+",
+                "path": "test/main_test.dart",
+              },
+              "name": "Failing test",
+              "result": "failed",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "package:darttest/main.dart 2:3  throwError
+test\\\\main_test.dart 17:9        main.<fn>.<fn>.<fn>
+",
+                "line": 17,
+                "message": "Exception: Some error",
+                "path": "test/main_test.dart",
+              },
+              "name": "Exception in target unit",
+              "result": "failed",
+              "time": 6,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "Test 2",
+          "tests": Array [
+            TestCaseResult {
+              "error": Object {
+                "details": "test\\\\main_test.dart 24:7  main.<fn>.<fn>
+",
+                "line": 24,
+                "message": "Exception: Some error",
+                "path": "test/main_test.dart",
+              },
+              "name": "Exception in test",
+              "result": "failed",
+              "time": 12,
+            },
+          ],
+        },
+      ],
+      "name": "test/main_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": Object {
+                "details": "dart:isolate  _RawReceivePortImpl._handleMessage
+",
+                "line": 5,
+                "message": "TimeoutException after 0:00:00.000001: Test timed out after 0 seconds.",
+                "path": "test/second_test.dart",
+              },
+              "name": "Timeout test",
+              "result": "failed",
+              "time": 37,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Skipped test",
+              "result": "skipped",
+              "time": 14,
+            },
+          ],
+        },
+      ],
+      "name": "test/second_test.dart",
+      "totalTime": undefined,
+    },
+  ],
+  "totalTime": 3760,
+}
+`;
+
+exports[`dart-json tests report from rrousselGit/provider test results matches snapshot 1`] = `
+TestRunResult {
+  "path": "fixtures/external/flutter/provider-test-results.json",
+  "suites": Array [
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "valueListenableProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "rebuilds when value change",
+              "result": "success",
+              "time": 200,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't rebuild dependents by default",
+              "result": "success",
+              "time": 26,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "pass keys",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't listen again if stream instance doesn't change",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
+The following TestFailure object was thrown running a test:
+  Expected: <2>
+  Actual: <1>
+Unexpected number of calls
+
+When the exception was thrown, this was the stack:
+#0      fail (package:test_api/src/frontend/expect.dart:155:31)
+#1      _expect (package:test_api/src/frontend/expect.dart:150:3)
+#2      expect (package:test_api/src/frontend/expect.dart:59:3)
+#3      VerificationResult.called (package:mockito/src/mock.dart:853:5)
+#4      main.<anonymous closure>.<anonymous closure> (file:///__w/provider/provider/test/value_listenable_provider_test.dart:112:34)
+<asynchronous suspension>
+<asynchronous suspension>
+(elided one frame from package:stack_trace)
+
+The test description was:
+  pass updateShouldNotify
+════════════════════════════════════════════════════════════════════════════════════════════════════",
+                "line": 112,
+                "message": "The following TestFailure object was thrown running a test:
+  Expected: <2>
+  Actual: <1>
+Unexpected number of calls
+",
+                "path": "test/value_listenable_provider_test.dart",
+              },
+              "name": "pass updateShouldNotify",
+              "result": "failed",
+              "time": 69,
+            },
+          ],
+        },
+      ],
+      "name": "test/value_listenable_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "ListenableProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 173,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "asserts that the created notifier can have listeners",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't listen again if listenable instance doesn't change",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with null (default)",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with null (create)",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "stateful create called once",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "dispose called on unmount",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "dispose can be null",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "changing listenable rebuilds descendants",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "rebuilding with the same provider don't rebuilds descendants",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "notifylistener rebuilds descendants",
+              "result": "success",
+              "time": 9,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ListenableProvider value constructor",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "pass down key",
+              "result": "success",
+              "time": 17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "changing the Listenable instance rebuilds dependents",
+              "result": "success",
+              "time": 29,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ListenableProvider stateful constructor",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "called with context",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "pass down key",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if create is null",
+              "result": "success",
+              "time": 4,
+            },
+          ],
+        },
+      ],
+      "name": "test/listenable_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "consumer",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 181,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 16,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "consumer2",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 9,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "consumer3",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "consumer4",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "consumer5",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 9,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "consumer6",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "obtains value from Provider<T>",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashed with no builder",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used inside MultiProvider",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+      ],
+      "name": "test/consumer_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Use builder property, not child",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ChangeNotifierProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "value",
+              "result": "success",
+              "time": 185,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder1",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder2",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder3",
+              "result": "success",
+              "time": 19,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder4",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder5",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder6",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder0",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+      ],
+      "name": "test/change_notifier_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "ChangeNotifierProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "default",
+              "result": "success",
+              "time": 189,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": ".value",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ListenableProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "default",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": ".value",
+              "result": "success",
+              "time": 16,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "Provider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "default",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": ".value",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ProxyProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "0",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "1",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "2",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "3",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "4",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "5",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "6",
+              "result": "success",
+              "time": 9,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "MultiProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "with 1 ChangeNotifierProvider default",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with 2 ChangeNotifierProvider default",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ListenableProvider default",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with Provider default",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider0",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider1",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider2",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider3",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider4",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider5",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "with ProxyProvider6",
+              "result": "success",
+              "time": 7,
+            },
+          ],
+        },
+      ],
+      "name": "test/builder_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "MultiProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "throw if providers is null",
+              "result": "success",
+              "time": 30,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MultiProvider children can only access parent providers",
+              "result": "success",
+              "time": 160,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MultiProvider.providers with ignored child",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+      ],
+      "name": "test/multi_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 191,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "transition from stream to stream preserve state",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if stream has error and catchError is missing",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls catchError if present and stream has error",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with null",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "StreamProvider() crashes if builder is null",
+              "result": "success",
+              "time": 5,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "StreamProvider()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "create and dispose stream with builder",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashes if builder is null",
+              "result": "success",
+              "time": 4,
+            },
+          ],
+        },
+      ],
+      "name": "test/stream_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "watch in layoutbuilder",
+              "result": "success",
+              "time": 179,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select in layoutbuilder",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "cannot select in listView",
+              "result": "success",
+              "time": 138,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "watch in listView",
+              "result": "success",
+              "time": 33,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "watch in gridView",
+              "result": "success",
+              "time": 21,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "clears select dependencies for all dependents",
+              "result": "success",
+              "time": 19,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "BuildContext",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "internal selected value is updated",
+              "result": "success",
+              "time": 32,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "create can use read without being lazy",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "watch can be used inside InheritedProvider.update",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select doesn't fail if it loads a provider that depends on other providers",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't call old selectors if the child rebuilds individually",
+              "result": "success",
+              "time": 21,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "selects throws inside click handlers",
+              "result": "success",
+              "time": 40,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if try to read dynamic",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws ProviderNotFoundException",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if watch called inside the callback from build",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if read called inside the callback from build",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if select called inside the callback from build",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if read called inside the callback on dependency change",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if watch called inside the callback on dependency change",
+              "result": "success",
+              "time": 17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select throws if select called inside the callback on dependency change",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can call read inside didChangeDependencies",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select cannot be called inside didChangeDependencies",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "select in initState throws",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "watch in initState throws",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "read in initState works",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "consumer can be removed and selector stops to be called",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "context.select deeply compares maps",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "context.select deeply compares lists",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "context.select deeply compares iterables",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "context.select deeply compares sets",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "context.watch listens to value changes",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+      ],
+      "name": "test/context_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "ReassembleHandler",
+              "result": "success",
+              "time": 194,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "unevaluated create",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "unevaluated create",
+              "result": "success",
+              "time": 16,
+            },
+          ],
+        },
+      ],
+      "name": "test/reassemble_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 184,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "(catchError) previous future completes after transition is no-op",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "previous future completes after transition is no-op",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "transition from future to future preserve state",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if future has error and catchError is missing",
+              "result": "success",
+              "time": 24,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls catchError if present and future has error",
+              "result": "success",
+              "time": 21,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with null",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "create and dispose future with builder",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "FutureProvider() crashes if builder is null",
+              "result": "success",
+              "time": 4,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "FutureProvider()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "crashes if builder is null",
+              "result": "success",
+              "time": 3,
+            },
+          ],
+        },
+      ],
+      "name": "test/future_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 172,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "Provider.of",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if T is dynamic",
+              "result": "success",
+              "time": 26,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "listen defaults to true when building widgets",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "listen defaults to false outside of the widget tree",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "listen:false doesn't trigger rebuild",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "listen:true outside of the widget tree throws",
+              "result": "success",
+              "time": 11,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "Provider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if the provided value is a Listenable/Stream",
+              "result": "success",
+              "time": 28,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "debugCheckInvalidValueType can be disabled",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "simple usage",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws an error if no provider found",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update should notify",
+              "result": "success",
+              "time": 8,
+            },
+          ],
+        },
+      ],
+      "name": "test/provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "regression test #377",
+              "result": "success",
+              "time": 167,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "rebuild on dependency flags update",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "properly update debug flags if a create triggers another deferred create",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "properly update debug flags if a create triggers another deferred create",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "properly update debug flags if an update triggers another create/update",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "properly update debug flags if a create triggers another create/update",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Provider.of(listen: false) outside of build works when it loads a provider",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "new value is available in didChangeDependencies",
+              "result": "success",
+              "time": 26,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder receives the current value and updates independently from \`update\`",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder can _not_ rebuild when provider updates",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder rebuilds if provider is recreated",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "provider.of throws if listen:true outside of the widget tree",
+              "result": "success",
+              "time": 23,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider throws if no child is provided with default constructor",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider throws if no child is provided with value constructor",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider throws if no child is provided with default constructor",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider throws if no child is provided with value constructor",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening markNeedsNotifyDependents",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider can be subclassed",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider can be subclassed",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can be used with MultiProvider",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throw if the widget ctor changes",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider lazy loading can be disabled",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider.value lazy loading can be disabled",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider subclass don't have to specify default lazy value",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider lazy loading can be disabled",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider.value lazy loading can be disabled",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "selector",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can select multiple types from same provider",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can select same type on two different providers",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "can select same type twice on same provider",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Provider.of has a proper error message if context is null",
+              "result": "success",
+              "time": 6,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "diagnostics",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider.value",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider doesn't break lazy loading",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "InheritedProvider show if listening",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider.value",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "DeferredInheritedProvider",
+              "result": "success",
+              "time": 16,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "InheritedProvider.value()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "markNeedsNotifyDependents during startListening is noop",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening called again when create returns new value",
+              "result": "success",
+              "time": 27,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening",
+              "result": "success",
+              "time": 19,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "stopListening not called twice if rebuild doesn't have listeners",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "removeListener cannot be null",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "pass down current value",
+              "result": "success",
+              "time": 17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "default updateShouldNotify",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "custom updateShouldNotify",
+              "result": "success",
+              "time": 32,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "InheritedProvider()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "hasValue",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "provider calls update if rebuilding only due to didChangeDependencies",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "provider notifying dependents doesn't call update",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update can call Provider.of with listen:true",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update lazy loaded can call Provider.of with listen:true",
+              "result": "success",
+              "time": 10,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "markNeedsNotifyDependents during startListening is noop",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update can obtain parent of the same type than self",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "_debugCheckInvalidValueType",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening called again when create returns new value",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "stopListening not called twice if rebuild doesn't have listeners",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "removeListener cannot be null",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "fails if initialValueBuilder calls inheritFromElement/inheritFromWiggetOfExactType",
+              "result": "success",
+              "time": 17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder is called on every rebuild and after a dependency change",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder with no updateShouldNotify use ==",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder calls updateShouldNotify callback",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "initialValue is transmitted to valueBuilder",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls builder again if dependencies change",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "exposes initialValue if valueBuilder is null",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "call dispose on unmount",
+              "result": "success",
+              "time": 22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "builder unmount, dispose not called if value never read",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "call dispose after new value",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "valueBuilder works without initialBuilder",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls initialValueBuilder lazily once",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if both builder and initialBuilder are missing",
+              "result": "success",
+              "time": 5,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "DeferredInheritedProvider.value()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "hasValue",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "stopListening cannot be null",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening doesn't need setState if already initialized",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "setState without updateShouldNotify",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "setState with updateShouldNotify",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening never leave the widget uninitialized",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "startListening called again on controller change",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "DeferredInheritedProvider()",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "create can't call inherited widgets",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "creates the value lazily",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "dispose",
+              "result": "success",
+              "time": 7,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "dispose no-op if never built",
+              "result": "success",
+              "time": 7,
+            },
+          ],
+        },
+      ],
+      "name": "test/inherited_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "ListenableProxyProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if update is missing",
+              "result": "success",
+              "time": 43,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "asserts that the created notifier has no listener",
+              "result": "success",
+              "time": 177,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "asserts that the created notifier has no listener after rebuild",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "rebuilds dependendents when listeners are called",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update returning a new Listenable disposes the previously created value and update dependents",
+              "result": "success",
+              "time": 25,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "disposes of created value",
+              "result": "success",
+              "time": 13,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ListenableProxyProvider variants",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider2",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider3",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider4",
+              "result": "success",
+              "time": 17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider5",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ListenableProxyProvider6",
+              "result": "success",
+              "time": 17,
+            },
+          ],
+        },
+      ],
+      "name": "test/listenable_proxy_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "asserts that builder/selector are not null",
+              "result": "success",
+              "time": 32,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Deep compare maps by default",
+              "result": "success",
+              "time": 158,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Deep compare iterables by default",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Deep compare sets by default",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Deep compare lists by default",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "custom shouldRebuid",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "passes \`child\` and \`key\`",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls builder if the callback changes",
+              "result": "success",
+              "time": 14,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't call builder again if it rebuilds but selector returns the same thing",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "call builder again if it rebuilds abd selector returns the a different variable",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector",
+              "result": "success",
+              "time": 15,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector2",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector3",
+              "result": "success",
+              "time": 8,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector4",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector5",
+              "result": "success",
+              "time": 19,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Selector6",
+              "result": "success",
+              "time": 11,
+            },
+          ],
+        },
+      ],
+      "name": "test/selector_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "asserts",
+              "result": "success",
+              "time": 6,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 203,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "calls create only once",
+              "result": "success",
+              "time": 27,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "dispose",
+              "result": "success",
+              "time": 18,
+            },
+          ],
+        },
+      ],
+      "name": "test/stateful_provider_test.dart",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": "ProxyProvider",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if the provided value is a Listenable/Stream",
+              "result": "success",
+              "time": 209,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "debugCheckInvalidValueType can be disabled",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "create creates initial value",
+              "result": "success",
+              "time": 23,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "consume another providers",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "rebuild descendants if value change",
+              "result": "success",
+              "time": 13,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "call dispose when unmounted with the latest result",
+              "result": "success",
+              "time": 11,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "don't rebuild descendants if value doesn't change",
+              "result": "success",
+              "time": 12,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "pass down updateShouldNotify",
+              "result": "success",
+              "time": 19,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "works with MultiProvider",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "update callback can trigger descendants setState synchronously",
+              "result": "success",
+              "time": 24,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "throws if update is null",
+              "result": "success",
+              "time": 7,
+            },
+          ],
+        },
+        TestGroupResult {
+          "name": "ProxyProvider variants",
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "ProxyProvider2",
+              "result": "success",
+              "time": 18,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ProxyProvider3",
+              "result": "success",
+              "time": 16,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ProxyProvider4",
+              "result": "success",
+              "time": 9,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ProxyProvider5",
+              "result": "success",
+              "time": 20,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ProxyProvider6",
+              "result": "success",
+              "time": 10,
+            },
+          ],
+        },
+      ],
+      "name": "test/proxy_provider_test.dart",
+      "totalTime": undefined,
+    },
+  ],
+  "totalTime": 0,
+}
+`;
diff --git a/test-reporter/__tests__/__snapshots__/dotnet-trx.test.ts.snap b/test-reporter/__tests__/__snapshots__/dotnet-trx.test.ts.snap
new file mode 100644
index 0000000..0301a56
--- /dev/null
+++ b/test-reporter/__tests__/__snapshots__/dotnet-trx.test.ts.snap
@@ -0,0 +1,6271 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`dotnet-trx tests matches report snapshot 1`] = `
+TestRunResult {
+  "path": "fixtures/dotnet-trx.trx",
+  "suites": Array [
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Custom Name",
+              "result": "success",
+              "time": 0.1371,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "System.DivideByZeroException : Attempted to divide by zero.
+   at DotnetTests.Unit.Calculator.Div(Int32 a, Int32 b) in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.Unit\\\\Calculator.cs:line 9
+   at DotnetTests.XUnitTests.CalculatorTests.Exception_In_TargetTest() in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.XUnitTests\\\\CalculatorTests.cs:line 33",
+                "line": 9,
+                "message": "System.DivideByZeroException : Attempted to divide by zero.",
+                "path": "DotnetTests.Unit/Calculator.cs",
+              },
+              "name": "Exception_In_TargetTest",
+              "result": "failed",
+              "time": 0.8377,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "System.Exception : Test
+   at DotnetTests.XUnitTests.CalculatorTests.Exception_In_Test() in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.XUnitTests\\\\CalculatorTests.cs:line 39",
+                "line": 39,
+                "message": "System.Exception : Test",
+                "path": "DotnetTests.XUnitTests/CalculatorTests.cs",
+              },
+              "name": "Exception_In_Test",
+              "result": "failed",
+              "time": 2.5175,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "Assert.Equal() Failure
+Expected: 3
+Actual:   2
+   at DotnetTests.XUnitTests.CalculatorTests.Failing_Test() in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.XUnitTests\\\\CalculatorTests.cs:line 27",
+                "line": 27,
+                "message": "Assert.Equal() Failure
+Expected: 3
+Actual:   2",
+                "path": "DotnetTests.XUnitTests/CalculatorTests.cs",
+              },
+              "name": "Failing_Test",
+              "result": "failed",
+              "time": 3.8697,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Is_Even_Number(i: 2)",
+              "result": "success",
+              "time": 0.0078,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "Assert.True() Failure
+Expected: True
+Actual:   False
+   at DotnetTests.XUnitTests.CalculatorTests.Is_Even_Number(Int32 i) in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.XUnitTests\\\\CalculatorTests.cs:line 59",
+                "line": 59,
+                "message": "Assert.True() Failure
+Expected: True
+Actual:   False",
+                "path": "DotnetTests.XUnitTests/CalculatorTests.cs",
+              },
+              "name": "Is_Even_Number(i: 3)",
+              "result": "failed",
+              "time": 0.41409999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Passing_Test",
+              "result": "success",
+              "time": 0.1365,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should be even number(i: 2)",
+              "result": "success",
+              "time": 0.0097,
+            },
+            TestCaseResult {
+              "error": Object {
+                "details": "Assert.True() Failure
+Expected: True
+Actual:   False
+   at DotnetTests.XUnitTests.CalculatorTests.Theory_With_Custom_Name(Int32 i) in C:\\\\Users\\\\Michal\\\\Workspace\\\\dorny\\\\test-reporter\\\\reports\\\\dotnet\\\\DotnetTests.XUnitTests\\\\CalculatorTests.cs:line 67",
+                "line": 67,
+                "message": "Assert.True() Failure
+Expected: True
+Actual:   False",
+                "path": "DotnetTests.XUnitTests/CalculatorTests.cs",
+              },
+              "name": "Should be even number(i: 3)",
+              "result": "failed",
+              "time": 0.6537000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Skipped_Test",
+              "result": "skipped",
+              "time": 1,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Timeout_Test",
+              "result": "success",
+              "time": 108.42580000000001,
+            },
+          ],
+        },
+      ],
+      "name": "DotnetTests.XUnitTests.CalculatorTests",
+      "totalTime": undefined,
+    },
+  ],
+  "totalTime": 1116,
+}
+`;
+
+exports[`dotnet-trx tests report from FluentValidation test results matches snapshot 1`] = `
+TestRunResult {
+  "path": "fixtures/external/FluentValidation.Tests.trx",
+  "suites": Array [
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_replace_default_errorcode_resolver",
+              "result": "success",
+              "time": 0.31579999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "CanValidateInstancesOfType_returns_false_when_comparing_against_some_other_type",
+              "result": "success",
+              "time": 0.0513,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "CanValidateInstancesOfType_returns_true_when_comparing_against_same_type",
+              "result": "success",
+              "time": 0.0665,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "CanValidateInstancesOfType_returns_true_when_comparing_against_subclass",
+              "result": "success",
+              "time": 0.0566,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Default_error_code_should_be_class_name",
+              "result": "success",
+              "time": 0.1555,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "OverridePropertyName_should_override_property_name",
+              "result": "success",
+              "time": 0.1676,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "OverridePropertyName_with_lambda_should_override_property_name",
+              "result": "success",
+              "time": 0.3832,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "PreValidate_bypasses_nullcheck_on_instance",
+              "result": "success",
+              "time": 0.2142,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "RuleForeach_with_null_instances",
+              "result": "success",
+              "time": 1.9478,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_be_valid_when_there_are_no_failures_for_single_property",
+              "result": "success",
+              "time": 0.1968,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_not_main_state",
+              "result": "success",
+              "time": 0.1976,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_throw_for_non_member_expression_when_validating_single_property",
+              "result": "success",
+              "time": 0.2162,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_throw_when_rule_is_null",
+              "result": "success",
+              "time": 0.1178,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_validate_public_Field",
+              "result": "success",
+              "time": 0.3435,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_validate_single_Field",
+              "result": "success",
+              "time": 0.24439999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_validate_single_property",
+              "result": "success",
+              "time": 0.27349999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_validate_single_property_where_invalid_property_as_string",
+              "result": "success",
+              "time": 0.216,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_validate_single_property_where_property_as_string",
+              "result": "success",
+              "time": 0.3409,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_named_parameters_to_validate_ruleset",
+              "result": "success",
+              "time": 0.36160000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_single_property_by_path",
+              "result": "success",
+              "time": 0.4851,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_type_when_using_non_generic_validate_overload",
+              "result": "success",
+              "time": 0.1769,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validators_fail_then_the_errors_Should_be_accessible_via_the_errors_property",
+              "result": "success",
+              "time": 0.17,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validators_fail_then_validatorrunner_should_return_false",
+              "result": "success",
+              "time": 0.1522,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_Validators_pass_then_the_validatorRunner_should_return_true",
+              "result": "success",
+              "time": 0.1471,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_Validate(preValidationResult: )",
+              "result": "success",
+              "time": 0.9423,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_Validate(preValidationResult: AnotherInt Test Message)",
+              "result": "success",
+              "time": 0.0628,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_ValidateAsync(preValidationResult: )",
+              "result": "success",
+              "time": 0.9155,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsFalse_ResultReturnToUserImmediatly_ValidateAsync(preValidationResult: AnotherInt Test Message)",
+              "result": "success",
+              "time": 0.057800000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsTrue_ValidatorsGetHit_Validate",
+              "result": "success",
+              "time": 0.4371,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WhenPreValidationReturnsTrue_ValidatorsGetHit_ValidateAsync",
+              "result": "success",
+              "time": 1.5404,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WithErrorCode_should_override_error_code",
+              "result": "success",
+              "time": 0.1849,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WithMessage_and_WithErrorCode_should_override_error_message_and_error_code",
+              "result": "success",
+              "time": 0.1772,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WithMessage_should_override_error_message",
+              "result": "success",
+              "time": 0.159,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WithName_should_override_field_name",
+              "result": "success",
+              "time": 0.20320000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "WithName_should_override_field_name_with_value_from_other_property",
+              "result": "success",
+              "time": 0.2316,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.AbstractValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Benchmark",
+              "result": "skipped",
+              "time": 1,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Equality_comparison_check",
+              "result": "success",
+              "time": 2.1363,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_accessor",
+              "result": "success",
+              "time": 0.3872,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_member_for_nested_property",
+              "result": "success",
+              "time": 0.10840000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Identifies_if_memberexp_acts_on_model_instance",
+              "result": "success",
+              "time": 0.2977,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.AccessorCacheTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Finds_validators_for_types",
+              "result": "success",
+              "time": 0.23809999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ForEach_iterates_over_types",
+              "result": "success",
+              "time": 1.8574,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.AssemblyScannerTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Cascade_mode_can_be_set_after_validator_instantiated",
+              "result": "success",
+              "time": 0.1778,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Cascade_mode_can_be_set_after_validator_instantiated_async",
+              "result": "success",
+              "time": 0.8338,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Cascade_mode_can_be_set_after_validator_instantiated_async_legacy",
+              "result": "success",
+              "time": 1.3079,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Cascade_mode_can_be_set_after_validator_instantiated_legacy",
+              "result": "success",
+              "time": 0.2101,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure",
+              "result": "success",
+              "time": 0.1733,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_async",
+              "result": "success",
+              "time": 1.0423,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level",
+              "result": "success",
+              "time": 0.223,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_async",
+              "result": "success",
+              "time": 1.1028,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_async_legacy",
+              "result": "success",
+              "time": 1.4877,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_Stop_globally_and_overriden_at_rule_level_legacy",
+              "result": "success",
+              "time": 0.5266,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level",
+              "result": "success",
+              "time": 0.19369999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_async",
+              "result": "success",
+              "time": 1.072,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_async_legacy",
+              "result": "success",
+              "time": 1.0468000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_on_failure_when_set_to_StopOnFirstFailure_at_validator_level_and_overriden_at_rule_level_legacy",
+              "result": "success",
+              "time": 0.2332,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop",
+              "result": "success",
+              "time": 0.16670000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_async",
+              "result": "success",
+              "time": 0.987,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_async_legacy",
+              "result": "success",
+              "time": 1.1758,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_to_second_validator_when_first_validator_succeeds_and_cascade_set_to_stop_legacy",
+              "result": "success",
+              "time": 0.2155,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_when_set_to_Continue_at_validator_level",
+              "result": "success",
+              "time": 0.2021,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_continues_when_set_to_Continue_at_validator_level_async",
+              "result": "success",
+              "time": 1.0564,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level",
+              "result": "success",
+              "time": 0.2008,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_async",
+              "result": "success",
+              "time": 0.8319,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_async_legacy",
+              "result": "success",
+              "time": 0.8677,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_failure_when_set_to_Continue_and_overriden_at_rule_level_legacy",
+              "result": "success",
+              "time": 0.21660000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure",
+              "result": "success",
+              "time": 0.1706,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_async",
+              "result": "success",
+              "time": 0.7821,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_async_legacy",
+              "result": "success",
+              "time": 0.8751,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_legacy",
+              "result": "success",
+              "time": 0.1809,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level",
+              "result": "success",
+              "time": 0.188,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_and_async_validator_is_invoked_synchronously",
+              "result": "success",
+              "time": 0.4818,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_and_async_validator_is_invoked_synchronously_legacy",
+              "result": "success",
+              "time": 0.5077,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_async",
+              "result": "success",
+              "time": 0.8130000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_async_legacy",
+              "result": "success",
+              "time": 0.7979,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_Failure_when_set_to_Continue_globally_and_overriden_at_rule_level_legacy",
+              "result": "success",
+              "time": 0.23,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level",
+              "result": "success",
+              "time": 0.2738,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_async",
+              "result": "success",
+              "time": 0.909,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_async_legacy",
+              "result": "success",
+              "time": 0.8011,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_stops_on_first_failure_when_set_to_StopOnFirstFailure_at_validator_level_legacy",
+              "result": "success",
+              "time": 0.2253,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CascadingFailuresTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_validate_using_validator_for_base_type",
+              "result": "success",
+              "time": 1.4496,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Chained_property_should_be_excluded",
+              "result": "success",
+              "time": 0.1883,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Chained_validator_descriptor",
+              "result": "success",
+              "time": 0.29710000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Chained_validator_should_not_be_invoked_on_null_property",
+              "result": "success",
+              "time": 0.121,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_should_work_with_chained_property",
+              "result": "success",
+              "time": 0.2054,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Explicitly_included_properties_should_be_propagated_to_nested_validators",
+              "result": "success",
+              "time": 0.2691,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Explicitly_included_properties_should_be_propagated_to_nested_validators_using_strings",
+              "result": "success",
+              "time": 0.1753,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Separate_validation_on_chained_property",
+              "result": "success",
+              "time": 1.368,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Separate_validation_on_chained_property_conditional",
+              "result": "success",
+              "time": 0.2104,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Separate_validation_on_chained_property_valid",
+              "result": "success",
+              "time": 0.20249999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_allow_normal_rules_and_chained_property_on_same_property",
+              "result": "success",
+              "time": 0.2278,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_explicit_ruleset",
+              "result": "success",
+              "time": 0.5128,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_chained_property",
+              "result": "success",
+              "time": 1.0483,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ChainedValidationTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Options_should_only_apply_to_current_validator",
+              "result": "success",
+              "time": 0.23479999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_create_multiple_validators",
+              "result": "success",
+              "time": 0.3311,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_execute_multiple_validators",
+              "result": "success",
+              "time": 0.17809999999999998,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ChainingValidatorsTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_define_nested_rules_for_collection",
+              "result": "success",
+              "time": 1.6096000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ChildRules_works_with_RuleSet",
+              "result": "success",
+              "time": 5.8553,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ChildRulesTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_should_work_with_child_collection",
+              "result": "success",
+              "time": 0.7258,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_specify_condition_for_individual_collection_elements",
+              "result": "success",
+              "time": 0.9154,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_validate_collection_using_validator_for_base_type",
+              "result": "success",
+              "time": 1.0736999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_excluded",
+              "result": "success",
+              "time": 0.525,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_explicitly_included_with_expression",
+              "result": "success",
+              "time": 0.6578,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_explicitly_included_with_string",
+              "result": "success",
+              "time": 0.5651999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_should_work_with_child_collection",
+              "result": "success",
+              "time": 0.7357,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Creates_validator_using_context_from_property_value",
+              "result": "success",
+              "time": 1.1184,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_override_property_name",
+              "result": "success",
+              "time": 0.47550000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_work_with_top_level_collection_validator",
+              "result": "success",
+              "time": 0.7123999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_work_with_top_level_collection_validator_and_overriden_name",
+              "result": "success",
+              "time": 0.3943,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Skips_null_items",
+              "result": "success",
+              "time": 0.5809,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection",
+              "result": "success",
+              "time": 1.0225,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection_asynchronously",
+              "result": "success",
+              "time": 1.4053,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection_several_levels_deep",
+              "result": "success",
+              "time": 1.1437000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection_several_levels_deep_async",
+              "result": "success",
+              "time": 1.0832000000000002,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CollectionValidatorWithParentTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_should_work_with_complex_property",
+              "result": "success",
+              "time": 2.3119,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_should_work_with_complex_property_when_validator_invoked_synchronously",
+              "result": "success",
+              "time": 0.7052,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_directly_validate_multiple_fields_of_same_type",
+              "result": "success",
+              "time": 3.4497,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_validate_using_validator_for_base_type",
+              "result": "success",
+              "time": 1.2612,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Complex_property_should_be_excluded",
+              "result": "success",
+              "time": 0.3153,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Complex_validator_should_not_be_invoked_on_null_property",
+              "result": "success",
+              "time": 0.16770000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_should_work_with_complex_property",
+              "result": "success",
+              "time": 0.4359,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_should_work_with_complex_property_when_invoked_async",
+              "result": "success",
+              "time": 0.8233,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Explicitly_included_properties_should_be_propagated_to_nested_validators",
+              "result": "success",
+              "time": 2.3437,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Explicitly_included_properties_should_be_propagated_to_nested_validators_using_strings",
+              "result": "success",
+              "time": 0.8307,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Multiple_rules_in_chain_with_childvalidator_shouldnt_reuse_accessor",
+              "result": "success",
+              "time": 0.7222,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Multiple_rules_in_chain_with_childvalidator_shouldnt_reuse_accessor_async",
+              "result": "success",
+              "time": 5.7314,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_allow_normal_rules_and_complex_property_on_same_property",
+              "result": "success",
+              "time": 0.7508,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_override_propertyName",
+              "result": "success",
+              "time": 0.7523,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_child_validator_asynchronously",
+              "result": "success",
+              "time": 3.7361999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_child_validator_synchronously",
+              "result": "success",
+              "time": 0.4716,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_complex_property",
+              "result": "success",
+              "time": 0.9825999999999999,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ComplexValidationTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_executed_synchronosuly_with_asynchronous_collection_rule",
+              "result": "success",
+              "time": 0.3309,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_executed_synchronosuly_with_asynchronous_rule",
+              "result": "success",
+              "time": 0.2385,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_executed_synchronosuly_with_synchronous_collection_role",
+              "result": "success",
+              "time": 0.3216,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_executed_synchronosuly_with_synchronous_role",
+              "result": "success",
+              "time": 0.3076,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_is_applied_to_all_validators_in_the_chain",
+              "result": "success",
+              "time": 0.8143999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_is_applied_to_all_validators_in_the_chain_when_executed_synchronously",
+              "result": "success",
+              "time": 0.5363,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator",
+              "result": "success",
+              "time": 0.8180000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_is_applied_to_all_validators_in_the_chain",
+              "result": "success",
+              "time": 0.3098,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator",
+              "result": "success",
+              "time": 0.3365,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Sync_condition_is_applied_to_async_validators",
+              "result": "success",
+              "time": 0.6349,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_fail_when_async_condition_does_not_match",
+              "result": "success",
+              "time": 0.4177,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_fail_when_async_condition_matches",
+              "result": "success",
+              "time": 0.7917,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_fail_when_condition_does_not_match",
+              "result": "success",
+              "time": 0.0888,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_fail_when_condition_matches",
+              "result": "success",
+              "time": 0.07550000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_succeed_when_async_condition_does_not_match",
+              "result": "success",
+              "time": 0.355,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_succeed_when_async_condition_matches",
+              "result": "success",
+              "time": 1.5482,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_succeed_when_condition_does_not_match",
+              "result": "success",
+              "time": 0.2873,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validation_should_succeed_when_condition_matches",
+              "result": "success",
+              "time": 0.4142,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ConditionTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "IsValidTests",
+              "result": "success",
+              "time": 0.2171,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_validation_fails_the_default_error_should_be_set",
+              "result": "success",
+              "time": 1.7932,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CreditCardValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Does_not_invoke_action_if_validation_success",
+              "result": "success",
+              "time": 0.2528,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Invokes_custom_action_on_failure",
+              "result": "success",
+              "time": 0.2284,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Passes_object_being_validated_to_action",
+              "result": "success",
+              "time": 0.852,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CustomFailureActionTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Replaces_propertyvalue_placeholder",
+              "result": "success",
+              "time": 0.9778,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Replaces_propertyvalue_with_empty_string_when_null",
+              "result": "success",
+              "time": 0.2456,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_format_custom_message",
+              "result": "success",
+              "time": 0.22440000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_custom_delegate_for_building_message",
+              "result": "success",
+              "time": 0.5952999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_custom_delegate_for_building_message_only_for_specific_validator",
+              "result": "success",
+              "time": 0.3785,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_property_value_in_message",
+              "result": "success",
+              "time": 0.8185,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CustomMessageFormatTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_Custom_Returns_single_failure",
+              "result": "success",
+              "time": 0.335,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_Custom_Returns_single_failure_async",
+              "result": "success",
+              "time": 0.5860000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_custom_uses_empty_property_name_for_model_level_rule",
+              "result": "success",
+              "time": 0.2515,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_Custom_When_property_name_omitted_infers_property_name",
+              "result": "success",
+              "time": 0.23379999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_Custom_When_property_name_omitted_infers_property_name_nested",
+              "result": "success",
+              "time": 0.6317999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_Custom_within_ruleset",
+              "result": "success",
+              "time": 0.5423,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "New_CustomAsync_within_ruleset",
+              "result": "success",
+              "time": 0.8872,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Perserves_property_chain_using_custom",
+              "result": "success",
+              "time": 0.8351,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Runs_async_rule_synchronously_when_validator_invoked_synchronously",
+              "result": "success",
+              "time": 0.6956,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Runs_sync_rule_asynchronously_when_validator_invoked_asynchronously",
+              "result": "success",
+              "time": 0.5199,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.CustomValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Empty_should_create_EmptyValidator",
+              "result": "success",
+              "time": 0.2993,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Equal_should_create_EqualValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.3343,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Equal_should_create_EqualValidator_with_lambda",
+              "result": "success",
+              "time": 0.6079,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "GreaterThan_should_create_GreaterThanValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.2587,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "GreaterThan_should_create_GreaterThanValidator_with_lambda",
+              "result": "success",
+              "time": 0.4794,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.2869,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_lambda",
+              "result": "success",
+              "time": 7.8787,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "GreaterThanOrEqual_should_create_GreaterThanOrEqualValidator_with_lambda_with_other_Nullable",
+              "result": "success",
+              "time": 1.9588999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Length_should_create_ExactLengthValidator",
+              "result": "success",
+              "time": 0.3392,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Length_should_create_LengthValidator",
+              "result": "success",
+              "time": 0.2207,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Length_should_create_MaximumLengthValidator",
+              "result": "success",
+              "time": 0.2942,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Length_should_create_MinimumLengthValidator",
+              "result": "success",
+              "time": 0.26449999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "LessThan_should_create_LessThanValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.2742,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "LessThan_should_create_LessThanValidator_with_lambda",
+              "result": "success",
+              "time": 0.5323,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "LessThanOrEqual_should_create_LessThanOrEqualValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.6133,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "LessThanOrEqual_should_create_LessThanOrEqualValidator_with_lambda",
+              "result": "success",
+              "time": 0.5095,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "LessThanOrEqual_should_create_LessThanOrEqualValidator_with_lambda_with_other_Nullable",
+              "result": "success",
+              "time": 0.4351,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Must_should_create_PredicateValidator_with_context",
+              "result": "success",
+              "time": 0.8328,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Must_should_create_PredicateValidator_with_PropertyValidatorContext",
+              "result": "success",
+              "time": 4.2745,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Must_should_create_PredicteValidator",
+              "result": "success",
+              "time": 0.49379999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MustAsync_should_create_AsyncPredicateValidator_with_context",
+              "result": "success",
+              "time": 0.1972,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MustAsync_should_create_AsyncPredicateValidator_with_PropertyValidatorContext",
+              "result": "success",
+              "time": 1.6184999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MustAsync_should_create_AsyncPredicteValidator",
+              "result": "success",
+              "time": 0.5833,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "MustAsync_should_not_throw_InvalidCastException",
+              "result": "success",
+              "time": 10.451600000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "NotEmpty_should_create_NotEmptyValidator",
+              "result": "success",
+              "time": 0.2803,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "NotEqual_should_create_NotEqualValidator_with_explicit_value",
+              "result": "success",
+              "time": 0.3685,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "NotEqual_should_create_NotEqualValidator_with_lambda",
+              "result": "success",
+              "time": 0.5555,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "NotNull_should_create_NotNullValidator",
+              "result": "success",
+              "time": 0.2713,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ScalePrecision_should_create_ScalePrecisionValidator",
+              "result": "success",
+              "time": 2.0008999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ScalePrecision_should_create_ScalePrecisionValidator_with_ignore_trailing_zeros",
+              "result": "success",
+              "time": 0.2146,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.DefaultValidatorExtensionTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\" \\\\r \\\\t \\\\n\\")",
+              "result": "success",
+              "time": 0.0247,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"\\")",
+              "result": "success",
+              "time": 0.0314,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0248,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"@someDomain@abc.com\\")",
+              "result": "success",
+              "time": 0.0234,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"0\\")",
+              "result": "success",
+              "time": 0.2222,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"someName\\")",
+              "result": "success",
+              "time": 0.023,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"someName@\\")",
+              "result": "success",
+              "time": 0.022699999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Fails_email_validation_aspnetcore_compatible(email: \\"someName@a@b.com\\")",
+              "result": "success",
+              "time": 0.022699999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Invalid_email_addressex_regex(email: \\"\\")",
+              "result": "success",
+              "time": 0.3859,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Invalid_email_addressex_regex(email: \\"first.last@test..co.uk\\")",
+              "result": "success",
+              "time": 0.0334,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Invalid_email_addressex_regex(email: \\"testperso\\")",
+              "result": "success",
+              "time": 0.0395,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Invalid_email_addressex_regex(email: \\"thisisaverylongstringcodeplex.com\\")",
+              "result": "success",
+              "time": 0.0316,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\" @someDomain.com\\")",
+              "result": "success",
+              "time": 0.0173,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"!#$%&'*+-/=?^_\`|~@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0174,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"\\\\\\"firstName.lastName\\\\\\"@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0167,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"1234@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0307,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"firstName.lastName@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0191,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"someName@1234.com\\")",
+              "result": "success",
+              "time": 0.0158,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"someName@some_domain.com\\")",
+              "result": "success",
+              "time": 0.0162,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"someName@some~domain.com\\")",
+              "result": "success",
+              "time": 0.0166,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"someName@someDomain.com\\")",
+              "result": "success",
+              "time": 0.0167,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: \\"someName@someDomain￯.com\\")",
+              "result": "success",
+              "time": 0.0157,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_aspnetcore_compatible(email: null)",
+              "result": "success",
+              "time": 0.2029,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"__somename@example.com\\")",
+              "result": "success",
+              "time": 0.0219,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"!def!xyz%abc@example.com\\")",
+              "result": "success",
+              "time": 0.022,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"\\\\\\"Abc@def\\\\\\"@example.com\\")",
+              "result": "success",
+              "time": 0.0245,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"\\\\\\"Abc\\\\\\\\@def\\\\\\"@example.com\\")",
+              "result": "success",
+              "time": 0.026,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"\\\\\\"Fred Bloggs\\\\\\"@example.com\\")",
+              "result": "success",
+              "time": 0.0258,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"\\\\\\"Joe\\\\\\\\Blow\\\\\\"@example.com\\")",
+              "result": "success",
+              "time": 0.0244,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"$A12345@example.com\\")",
+              "result": "success",
+              "time": 0.022600000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"customer/department=shipping@example.com\\")",
+              "result": "success",
+              "time": 0.0234,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"first.last@test.co.uk\\")",
+              "result": "success",
+              "time": 0.022600000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"testperson@gmail.com\\")",
+              "result": "success",
+              "time": 16.3384,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"TestPerson@gmail.com\\")",
+              "result": "success",
+              "time": 0.0805,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: \\"testperson+label@gmail.com\\")",
+              "result": "success",
+              "time": 0.0275,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Valid_email_addresses_regex(email: null)",
+              "result": "success",
+              "time": 0.4724,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.EmailValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Passes_for_ienumerable_that_doesnt_implement_ICollection",
+              "result": "success",
+              "time": 3.0376000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Passes_when_collection_empty",
+              "result": "success",
+              "time": 0.27,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_there_is_a_value_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.19890000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_validation_fails_error_should_be_set",
+              "result": "success",
+              "time": 0.2342,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_value_is_Default_for_type_validator_should_pass_datetime",
+              "result": "success",
+              "time": 0.3325,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_value_is_Default_for_type_validator_should_pass_int",
+              "result": "success",
+              "time": 0.3963,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_value_is_empty_string_validator_should_pass",
+              "result": "success",
+              "time": 0.2019,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_value_is_null_validator_should_pass",
+              "result": "success",
+              "time": 0.2377,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_value_is_whitespace_validation_should_pass",
+              "result": "success",
+              "time": 0.1968,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.EmptyTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Flags_enum_invalid_when_using_outofrange_negative_value",
+              "result": "success",
+              "time": 0.756,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Flags_enum_invalid_when_using_outofrange_positive_value",
+              "result": "success",
+              "time": 0.7806,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Flags_enum_valid_when_using_bitwise_value",
+              "result": "success",
+              "time": 19.2413,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Flags_enum_validates_correctly_when_using_zero_value",
+              "result": "success",
+              "time": 0.38539999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Flags_enum_with_overlapping_flags_valid_when_using_bitwise_value",
+              "result": "success",
+              "time": 0.299,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "IsValidTests",
+              "result": "success",
+              "time": 0.1215,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nullable_enum_invalid_when_bad_value_specified",
+              "result": "success",
+              "time": 0.38470000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nullable_enum_valid_when_property_value_is_null",
+              "result": "success",
+              "time": 0.1594,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nullable_enum_valid_when_value_specified",
+              "result": "success",
+              "time": 1.8848,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_enum_is_initialized_with_invalid_value_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2553,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_enum_is_not_initialized_with_valid_value_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.0819,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_validation_fails_the_default_error_should_be_set",
+              "result": "success",
+              "time": 0.1061,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.EnumValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_property_uses_custom_resolver",
+              "result": "success",
+              "time": 0.38289999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_store_comparison_type",
+              "result": "success",
+              "time": 0.3359,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_store_property_to_compare",
+              "result": "success",
+              "time": 0.32830000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_on_case_insensitive_comparison",
+              "result": "success",
+              "time": 0.2418,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_on_case_insensitive_comparison_using_expression",
+              "result": "success",
+              "time": 0.25379999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_use_ordinal_comparison_by_default",
+              "result": "success",
+              "time": 0.1642,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_against_property",
+              "result": "success",
+              "time": 0.29329999999999995,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_objects_are_equal_validation_should_succeed",
+              "result": "success",
+              "time": 0.3395,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_objects_are_not_equal_validation_should_fail",
+              "result": "success",
+              "time": 0.21919999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_validation_fails_the_error_should_be_set",
+              "result": "success",
+              "time": 0.23,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.EqualValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Min_and_max_properties_should_be_set",
+              "result": "success",
+              "time": 0.0707,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_exact_length_rule_failes_error_should_have_exact_length_error_errorcode",
+              "result": "success",
+              "time": 0.6217,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_an_exact_length_the_validator_should_pass",
+              "result": "success",
+              "time": 0.2146,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_length_is_larger_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2571,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_length_is_smaller_the_validator_should_fail",
+              "result": "success",
+              "time": 0.46890000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 0.2836,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ExactLengthValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "To_and_from_properties_should_be_set",
+              "result": "success",
+              "time": 0.0668,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "To_and_from_properties_should_be_set_for_dates",
+              "result": "success",
+              "time": 0.09179999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "To_and_from_properties_should_be_set_for_strings",
+              "result": "success",
+              "time": 0.067,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.2242,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.6361,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.5079,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_range_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.2392,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_to_is_smaller_than_the_from_then_the_validator_should_throw",
+              "result": "success",
+              "time": 0.3029,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_strings",
+              "result": "success",
+              "time": 0.1562,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 0.2535,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set_for_strings",
+              "result": "success",
+              "time": 0.2668,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_between_the_range_specified_then_the_validator_should_pass",
+              "result": "success",
+              "time": 1.1727,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_between_the_range_specified_then_the_validator_should_pass_for_strings",
+              "result": "success",
+              "time": 0.2261,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.28390000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.9095000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2263,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.23399999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_smaller_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.24689999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_smaller_than_the_range_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.2367,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ExclusiveBetweenValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_extract_member_from_member_expression",
+              "result": "success",
+              "time": 0.11299999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_return_null_for_non_member_expressions",
+              "result": "success",
+              "time": 0.1368,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_split_pascal_cased_member_name",
+              "result": "success",
+              "time": 0.6315000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "SplitPascalCase_should_return_null_when_input_is_null",
+              "result": "success",
+              "time": 0.0314,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ExtensionTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Async_condition_should_work_with_child_collection",
+              "result": "success",
+              "time": 0.7127,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_access_colletion_index",
+              "result": "success",
+              "time": 0.28659999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_access_colletion_index_async",
+              "result": "success",
+              "time": 0.7781,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_access_parent_index",
+              "result": "success",
+              "time": 1.6135,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_access_parent_index_async",
+              "result": "success",
+              "time": 1.1019,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_specify_condition_for_individual_collection_elements",
+              "result": "success",
+              "time": 0.4461,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_use_cascade_with_RuleForEach",
+              "result": "success",
+              "time": 0.6943,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_validate_collection_using_validator_for_base_type",
+              "result": "success",
+              "time": 0.4768,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_excluded",
+              "result": "success",
+              "time": 0.544,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_explicitly_included_with_expression",
+              "result": "success",
+              "time": 0.7823,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Collection_should_be_explicitly_included_with_string",
+              "result": "success",
+              "time": 0.5339,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Condition_should_work_with_child_collection",
+              "result": "success",
+              "time": 0.3728,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Correctly_gets_collection_indices",
+              "result": "success",
+              "time": 0.3199,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Correctly_gets_collection_indices_async",
+              "result": "success",
+              "time": 0.699,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Executes_rule_for_each_item_in_collection",
+              "result": "success",
+              "time": 0.3186,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Executes_rule_for_each_item_in_collection_async",
+              "result": "success",
+              "time": 0.7972,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nested_collection_for_null_property_should_not_throw_null_reference",
+              "result": "success",
+              "time": 0.4414,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nested_conditions_Rule_For",
+              "result": "success",
+              "time": 0.9052,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Nested_conditions_Rule_For_Each",
+              "result": "success",
+              "time": 0.9411,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Overrides_indexer",
+              "result": "success",
+              "time": 1.2315,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Overrides_indexer_async",
+              "result": "success",
+              "time": 1.1100999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Regular_rules_can_drop_into_RuleForEach",
+              "result": "success",
+              "time": 0.8814,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "RuleForEach_async_RunsTasksSynchronously",
+              "result": "success",
+              "time": 23.6497,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_not_scramble_property_name_when_using_collection_validators_several_levels_deep",
+              "result": "success",
+              "time": 0.3437,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_not_scramble_property_name_when_using_collection_validators_several_levels_deep_with_ValidateAsync",
+              "result": "success",
+              "time": 2.1348000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_override_property_name",
+              "result": "success",
+              "time": 0.3821,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Skips_null_items",
+              "result": "success",
+              "time": 0.47859999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Top_level_collection",
+              "result": "success",
+              "time": 0.3379,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_useful_error_message_when_used_on_non_property",
+              "result": "success",
+              "time": 0.43560000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_child_validator_asynchronously",
+              "result": "success",
+              "time": 0.7232000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_child_validator_synchronously",
+              "result": "success",
+              "time": 0.356,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection",
+              "result": "success",
+              "time": 0.5174,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_runs_outside_RuleForEach_loop",
+              "result": "success",
+              "time": 0.4935,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_runs_outside_RuleForEach_loop_async",
+              "result": "success",
+              "time": 1.2558,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.ForEachRuleTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_property_uses_custom_resolver",
+              "result": "success",
+              "time": 0.5497,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_type",
+              "result": "success",
+              "time": 0.20120000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_less_than_input",
+              "result": "success",
+              "time": 0.3948,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_localize_value",
+              "result": "success",
+              "time": 0.8092,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_set_default_error_when_validation_fails",
+              "result": "success",
+              "time": 0.1125,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_equal_to_input",
+              "result": "success",
+              "time": 0.078,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_greater_than_input",
+              "result": "success",
+              "time": 0.08,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_nullable_with_nullable_property",
+              "result": "success",
+              "time": 0.38539999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_property",
+              "result": "success",
+              "time": 0.5735,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.2299,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null_cross_property",
+              "result": "success",
+              "time": 0.46599999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.24649999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null_cross_property",
+              "result": "success",
+              "time": 0.2763,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_property",
+              "result": "success",
+              "time": 0.2879,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.GreaterThanOrEqualToValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_property_uses_custom_resolver",
+              "result": "success",
+              "time": 0.4008,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_Type",
+              "result": "success",
+              "time": 0.2061,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_equal_to_input",
+              "result": "success",
+              "time": 0.07289999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_less_than_input",
+              "result": "success",
+              "time": 0.0764,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_set_default_error_when_validation_fails",
+              "result": "success",
+              "time": 0.1047,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_greater_than_input",
+              "result": "success",
+              "time": 0.08600000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_nullable_with_nullable_property",
+              "result": "success",
+              "time": 0.6029,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_property",
+              "result": "success",
+              "time": 0.5936,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.5006999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null_cross_property",
+              "result": "success",
+              "time": 0.26689999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.2583,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null_cross_property",
+              "result": "success",
+              "time": 0.5453,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_property",
+              "result": "success",
+              "time": 0.4626,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.GreaterThanValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "To_and_from_properties_should_be_set",
+              "result": "success",
+              "time": 0.131,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "To_and_from_properties_should_be_set_for_strings",
+              "result": "success",
+              "time": 0.0602,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.2167,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.4472,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2468,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_range_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.2377,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_to_is_smaller_than_the_from_then_the_validator_should_throw",
+              "result": "success",
+              "time": 0.6235999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_to_is_smaller_than_the_from_then_the_validator_should_throw_for_strings",
+              "result": "success",
+              "time": 0.13290000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 0.2354,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set_for_strings",
+              "result": "success",
+              "time": 0.2385,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_between_the_range_specified_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.3572,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_between_the_range_specified_then_the_validator_should_pass_for_strings",
+              "result": "success",
+              "time": 0.22,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.1963,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass_for_strings",
+              "result": "success",
+              "time": 0.3005,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.2097,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass_for_strings",
+              "result": "success",
+              "time": 0.19560000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_smaller_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2294,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_value_is_smaller_than_the_range_then_the_validator_should_fail_for_strings",
+              "result": "success",
+              "time": 0.2161,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.InclusiveBetweenValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_use_custom_subclass_with_nongeneric_overload",
+              "result": "success",
+              "time": 0.6978000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection",
+              "result": "success",
+              "time": 0.6633,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_collection_async",
+              "result": "success",
+              "time": 4.0405,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_inheritance_async",
+              "result": "success",
+              "time": 1.2651999999999999,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_inheritance_hierarchy",
+              "result": "success",
+              "time": 0.5062000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_ruleset",
+              "result": "success",
+              "time": 0.9577,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_ruleset_async",
+              "result": "success",
+              "time": 1.6248,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_callback",
+              "result": "success",
+              "time": 1.2889,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_callback_accepting_derived",
+              "result": "success",
+              "time": 0.6007,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_callback_accepting_derived_async",
+              "result": "success",
+              "time": 4.8985,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_callback_async",
+              "result": "success",
+              "time": 1.1747999999999998,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.InheritanceValidatorTest",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_inline_validator_to_build_rules",
+              "result": "success",
+              "time": 1.6789999999999998,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.InlineValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "All_languages_should_be_loaded",
+              "result": "success",
+              "time": 1.3878,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "All_localizations_have_same_parameters_as_English",
+              "result": "success",
+              "time": 21.761,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Always_use_specific_language",
+              "result": "success",
+              "time": 0.0666,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Always_use_specific_language_with_string_source",
+              "result": "success",
+              "time": 0.3219,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_replace_message",
+              "result": "success",
+              "time": 0.24509999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Can_replace_message_without_overriding_all_languages",
+              "result": "success",
+              "time": 0.1258,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Disables_localization",
+              "result": "success",
+              "time": 0.16369999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Falls_back_to_default_localization_key_when_error_code_key_not_found",
+              "result": "success",
+              "time": 0.28150000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Falls_back_to_english_when_culture_not_registered",
+              "result": "success",
+              "time": 0.2145,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Falls_back_to_english_when_translation_missing",
+              "result": "success",
+              "time": 0.523,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Falls_back_to_parent_culture",
+              "result": "success",
+              "time": 0.4487,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_bosnian_latin_culture(cultureName: \\"bs-Latn-BA\\")",
+              "result": "success",
+              "time": 0.011,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_bosnian_latin_culture(cultureName: \\"bs-Latn\\")",
+              "result": "success",
+              "time": 0.0627,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_bosnian_latin_culture(cultureName: \\"bs\\")",
+              "result": "success",
+              "time": 0.661,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_croatian_culture",
+              "result": "success",
+              "time": 0.45990000000000003,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_culture",
+              "result": "success",
+              "time": 0.0741,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_serbian_culture(cultureName: \\"sr-Latn-RS\\")",
+              "result": "success",
+              "time": 0.01,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_serbian_culture(cultureName: \\"sr-Latn\\")",
+              "result": "success",
+              "time": 0.0484,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_serbian_culture(cultureName: \\"sr\\")",
+              "result": "success",
+              "time": 0.4456,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Gets_translation_for_specific_culture",
+              "result": "success",
+              "time": 0.0989,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_error_code_as_localization_key",
+              "result": "success",
+              "time": 0.3393,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LanguageManagerTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Min_and_max_properties_should_be_set",
+              "result": "success",
+              "time": 0.1047,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_input_is_null_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.352,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_max_is_smaller_than_the_min_then_the_validator_should_throw",
+              "result": "success",
+              "time": 0.4785,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_maxlength_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 0.356,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_minlength_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 0.3529,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_between_the_lambda_range_specified_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.92,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_between_the_range_specified_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.2179,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_exactly_the_size_of_the_lambda_lower_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.3377,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_exactly_the_size_of_the_lambda_upper_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.3149,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_exactly_the_size_of_the_lower_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.21910000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_exactly_the_size_of_the_upper_bound_then_the_validator_should_pass",
+              "result": "success",
+              "time": 0.32899999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_lambda_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.3557,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_larger_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.2796,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_smaller_than_the_lambda_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.3612,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_text_is_smaller_than_the_range_then_the_validator_should_fail",
+              "result": "success",
+              "time": 0.4057,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "When_the_validator_fails_the_error_message_should_be_set",
+              "result": "success",
+              "time": 11.340599999999998,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LengthValidatorTests",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_property_uses_custom_resolver",
+              "result": "success",
+              "time": 1.0333,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_type",
+              "result": "success",
+              "time": 0.1094,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_greater_than_input",
+              "result": "success",
+              "time": 0.0853,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_set_default_error_when_validation_fails",
+              "result": "success",
+              "time": 0.1044,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_equal_to_input",
+              "result": "success",
+              "time": 0.0726,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_less_than_input",
+              "result": "success",
+              "time": 0.082,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_nullable_with_nullable_property",
+              "result": "success",
+              "time": 0.4362,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_property",
+              "result": "success",
+              "time": 0.5804,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.36719999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null_cross_property",
+              "result": "success",
+              "time": 0.4822,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.2607,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null_cross_property",
+              "result": "success",
+              "time": 0.283,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_property",
+              "result": "success",
+              "time": 0.321,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LessThanOrEqualToValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_property_uses_custom_resolver",
+              "result": "success",
+              "time": 0.3967,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Comparison_type",
+              "result": "success",
+              "time": 0.08940000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Extracts_property_from_constant_using_expression",
+              "result": "success",
+              "time": 0.0678,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Extracts_property_from_expression",
+              "result": "success",
+              "time": 0.5257000000000001,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_equal_to_input",
+              "result": "success",
+              "time": 0.2,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_fail_when_greater_than_input",
+              "result": "success",
+              "time": 0.19249999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_set_default_validation_message_when_validation_fails",
+              "result": "success",
+              "time": 0.352,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_succeed_when_less_than_input",
+              "result": "success",
+              "time": 0.1783,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Should_throw_when_value_to_compare_is_null",
+              "result": "success",
+              "time": 0.28909999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_against_property",
+              "result": "success",
+              "time": 1.2022000000000002,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_nullable_with_nullable_property",
+              "result": "success",
+              "time": 0.773,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_property",
+              "result": "success",
+              "time": 0.5793,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_is_null",
+              "result": "success",
+              "time": 0.34259999999999996,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null",
+              "result": "success",
+              "time": 0.2586,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_not_null_cross_property",
+              "result": "success",
+              "time": 0.27080000000000004,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Validates_with_nullable_when_property_null_cross_property",
+              "result": "success",
+              "time": 0.46349999999999997,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LessThanValidatorTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Correctly_assigns_default_localized_error_message",
+              "result": "success",
+              "time": 1.1647,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Does_not_throw_InvalidCastException_when_using_RuleForEach",
+              "result": "success",
+              "time": 0.4547,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Formats_string_with_placeholders",
+              "result": "success",
+              "time": 0.2342,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Formats_string_with_placeholders_when_you_cant_edit_the_string",
+              "result": "success",
+              "time": 0.2285,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_func_to_get_message",
+              "result": "success",
+              "time": 0.23829999999999998,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_string_format_with_property_value",
+              "result": "success",
+              "time": 0.7191,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LocalisedMessagesTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_localized_name",
+              "result": "success",
+              "time": 0.3024,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Uses_localized_name_expression",
+              "result": "success",
+              "time": 0.8903,
+            },
+          ],
+        },
+      ],
+      "name": "FluentValidation.Tests.LocalisedNameTester",
+      "totalTime": undefined,
+    },
+    TestSuiteResult {
+      "groups": Array [
+        TestGroupResult {
+          "name": null,
+          "tests": Array [
+            TestCaseResult {
+              "error": undefined,
+              "name": "ComplexPropertyGet",
+              "result": "success",
+              "time": 0.9033,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ComplexPropertySet",
+              "result": "success",
+              "time": 0.44129999999999997,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "Equality",
+              "result": "success",
+              "time": 1.0753,
+            },
+            TestCaseResult {
+              "error": undefined,
+              "name": "ImplicitCast",
+              "result": "success",
... 145529 lines suppressed ...

[pulsar-test-infra] 01/02: Add copy of cirruslabs/http-cache-action@master action

Posted by lh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-test-infra.git

commit b9df410a965f2b2a039506220e1400d64c1af0d2
Author: Lari Hotari <la...@hotari.net>
AuthorDate: Wed Mar 23 05:41:11 2022 +0200

    Add copy of cirruslabs/http-cache-action@master action
    
    from https://github.com/cirruslabs/http-cache-action hash d4ad73d
---
 http-cache-action/.github/workflows/tests.yml |   30 +
 http-cache-action/.gitignore                  |    2 +
 http-cache-action/LICENSE                     |   21 +
 http-cache-action/README.md                   |   74 +
 http-cache-action/action.yml                  |   14 +
 http-cache-action/dist/index.js               | 3010 +++++++++++++++++++++++++
 http-cache-action/dist/package.json           |    3 +
 http-cache-action/package-lock.json           | 1809 +++++++++++++++
 http-cache-action/package.json                |   47 +
 http-cache-action/proxy/Dockerfile            |   17 +
 http-cache-action/proxy/go.mod                |    5 +
 http-cache-action/proxy/go.sum                |    2 +
 http-cache-action/proxy/proxy.go              |  266 +++
 http-cache-action/proxy/proxy_test.go         |   62 +
 http-cache-action/src/action.ts               |   23 +
 http-cache-action/tsconfig.json               |    7 +
 16 files changed, 5392 insertions(+)

diff --git a/http-cache-action/.github/workflows/tests.yml b/http-cache-action/.github/workflows/tests.yml
new file mode 100644
index 0000000..ef2ecb2
--- /dev/null
+++ b/http-cache-action/.github/workflows/tests.yml
@@ -0,0 +1,30 @@
+name: tests
+on:
+  push:
+    branches:
+      - master
+    paths-ignore:
+      - '**.md'
+  pull_request:
+    paths-ignore:
+      - '**.md'
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    name: Test Proxy
+    steps:
+      - uses: actions/checkout@v2
+      - name: Proxy Integration Tests
+        # Need it so ACTIONS_CACHE_URL and ACTIONS_RUNTIME_TOKEN are available
+        uses: cedrickring/golang-action@1.6.0
+        with:
+          args: cd proxy && go test -v ./...
+      - name: Push Image
+        if: github.event_name == 'push' && github.ref == 'refs/heads/master'
+        env:
+          CR_PAT: ${{secrets.CR_PAT}}
+        run: |
+          echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin
+          docker build proxy --tag ghcr.io/cirruslabs/actions-http-cache-proxy:latest
+          docker push ghcr.io/cirruslabs/actions-http-cache-proxy:latest
diff --git a/http-cache-action/.gitignore b/http-cache-action/.gitignore
new file mode 100644
index 0000000..637d92c
--- /dev/null
+++ b/http-cache-action/.gitignore
@@ -0,0 +1,2 @@
+.idea/
+node_modules/
\ No newline at end of file
diff --git a/http-cache-action/LICENSE b/http-cache-action/LICENSE
new file mode 100644
index 0000000..075ea2e
--- /dev/null
+++ b/http-cache-action/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Cirrus Labs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/http-cache-action/README.md b/http-cache-action/README.md
new file mode 100644
index 0000000..116c85b
--- /dev/null
+++ b/http-cache-action/README.md
@@ -0,0 +1,74 @@
+# Action to run an HTTP Caching server
+
+Run a local server with an API compatible with build systems like Gradle, Bazel, Buck, Pants, etc.
+
+`/<KEY>` endpoint supports `GET`, `POST`/`PUT` and `HEAD` methods for downloading, uploading and existence checking of a cache entry with `KEY` cache key.
+
+## Inputs
+
+### `port`
+
+**Optional** Port number to start the proxy on. By default, `12321` is used.
+
+## Example usage
+
+```yaml
+uses: cirruslabs/http-cache-action@master
+```
+
+After that you can reach the HTTP Caching Proxy via `http://localhost:12321/`
+
+### Gradle Example
+
+```yaml
+name: Tests
+on: [push, pull_request]
+
+jobs:
+  test-gradle:
+    runs-on: ubuntu-latest
+    name: Gradle Check
+    steps:
+      - uses: actions/checkout@v2
+      - uses: cirruslabs/http-cache-action@master
+      - uses: actions/setup-java@v1
+        with:
+          java-version: 13
+      - run: ./gradlew check
+```
+
+Don't forget to add the following to your `settings.gradle`:
+
+```groovy
+ext.isCiServer = System.getenv().containsKey("CI")
+
+buildCache {
+  local {
+    enabled = !isCiServer
+  }
+  remote(HttpBuildCache) {
+    url = 'http://' + System.getenv().getOrDefault("CIRRUS_HTTP_CACHE_HOST", "localhost:12321") + "/"
+    enabled = isCiServer
+    push = true
+  }
+}
+```
+
+Or the following to your `settings.gradle.kts` if you are using Kotlin Script:
+
+
+```kotlin
+val isCiServer = System.getenv().containsKey("CI")
+
+buildCache {
+  local {
+    isEnabled = !isCiServer
+  }
+  remote<HttpBuildCache> {
+    val cacheHost = System.getenv().getOrDefault("CIRRUS_HTTP_CACHE_HOST", "localhost:12321")
+    url = uri("http://$cacheHost/")
+    isEnabled = isCiServer
+    isPush = true
+  }
+}
+```
diff --git a/http-cache-action/action.yml b/http-cache-action/action.yml
new file mode 100644
index 0000000..63a7d9c
--- /dev/null
+++ b/http-cache-action/action.yml
@@ -0,0 +1,14 @@
+name: 'HTTP Cache Proxy'
+description: 'Run HTTP Cache Proxy'
+branding:
+  icon: 'box'  
+  color: gray-dark
+inputs:
+  port:
+    description: 'Port to run on'
+    required: false
+    default: '12321'
+
+runs:
+  using: 'node12'
+  main: 'dist/index.js'
diff --git a/http-cache-action/dist/index.js b/http-cache-action/dist/index.js
new file mode 100644
index 0000000..55a5017
--- /dev/null
+++ b/http-cache-action/dist/index.js
@@ -0,0 +1,3010 @@
+import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module";
+/******/ var __webpack_modules__ = ({
+
+/***/ 351:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.issue = exports.issueCommand = void 0;
+const os = __importStar(__nccwpck_require__(37));
+const utils_1 = __nccwpck_require__(278);
+/**
+ * Commands
+ *
+ * Command Format:
+ *   ::name key=value,key=value::message
+ *
+ * Examples:
+ *   ::warning::This is the message
+ *   ::set-env name=MY_VAR::some value
+ */
+function issueCommand(command, properties, message) {
+    const cmd = new Command(command, properties, message);
+    process.stdout.write(cmd.toString() + os.EOL);
+}
+exports.issueCommand = issueCommand;
+function issue(name, message = '') {
+    issueCommand(name, {}, message);
+}
+exports.issue = issue;
+const CMD_STRING = '::';
+class Command {
+    constructor(command, properties, message) {
+        if (!command) {
+            command = 'missing.command';
+        }
+        this.command = command;
+        this.properties = properties;
+        this.message = message;
+    }
+    toString() {
+        let cmdStr = CMD_STRING + this.command;
+        if (this.properties && Object.keys(this.properties).length > 0) {
+            cmdStr += ' ';
+            let first = true;
+            for (const key in this.properties) {
+                if (this.properties.hasOwnProperty(key)) {
+                    const val = this.properties[key];
+                    if (val) {
+                        if (first) {
+                            first = false;
+                        }
+                        else {
+                            cmdStr += ',';
+                        }
+                        cmdStr += `${key}=${escapeProperty(val)}`;
+                    }
+                }
+            }
+        }
+        cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
+        return cmdStr;
+    }
+}
+function escapeData(s) {
+    return utils_1.toCommandValue(s)
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A');
+}
+function escapeProperty(s) {
+    return utils_1.toCommandValue(s)
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A')
+        .replace(/:/g, '%3A')
+        .replace(/,/g, '%2C');
+}
+//# sourceMappingURL=command.js.map
+
+/***/ }),
+
+/***/ 186:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0;
+const command_1 = __nccwpck_require__(351);
+const file_command_1 = __nccwpck_require__(717);
+const utils_1 = __nccwpck_require__(278);
+const os = __importStar(__nccwpck_require__(37));
+const path = __importStar(__nccwpck_require__(17));
+const oidc_utils_1 = __nccwpck_require__(41);
+/**
+ * The code to exit an action
+ */
+var ExitCode;
+(function (ExitCode) {
+    /**
+     * A code indicating that the action was successful
+     */
+    ExitCode[ExitCode["Success"] = 0] = "Success";
+    /**
+     * A code indicating that the action was a failure
+     */
+    ExitCode[ExitCode["Failure"] = 1] = "Failure";
+})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
+//-----------------------------------------------------------------------
+// Variables
+//-----------------------------------------------------------------------
+/**
+ * Sets env variable for this action and future actions in the job
+ * @param name the name of the variable to set
+ * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function exportVariable(name, val) {
+    const convertedVal = utils_1.toCommandValue(val);
+    process.env[name] = convertedVal;
+    const filePath = process.env['GITHUB_ENV'] || '';
+    if (filePath) {
+        const delimiter = '_GitHubActionsFileCommandDelimeter_';
+        const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
+        file_command_1.issueCommand('ENV', commandValue);
+    }
+    else {
+        command_1.issueCommand('set-env', { name }, convertedVal);
+    }
+}
+exports.exportVariable = exportVariable;
+/**
+ * Registers a secret which will get masked from logs
+ * @param secret value of the secret
+ */
+function setSecret(secret) {
+    command_1.issueCommand('add-mask', {}, secret);
+}
+exports.setSecret = setSecret;
+/**
+ * Prepends inputPath to the PATH (for this action and future actions)
+ * @param inputPath
+ */
+function addPath(inputPath) {
+    const filePath = process.env['GITHUB_PATH'] || '';
+    if (filePath) {
+        file_command_1.issueCommand('PATH', inputPath);
+    }
+    else {
+        command_1.issueCommand('add-path', {}, inputPath);
+    }
+    process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
+}
+exports.addPath = addPath;
+/**
+ * Gets the value of an input.
+ * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed.
+ * Returns an empty string if the value is not defined.
+ *
+ * @param     name     name of the input to get
+ * @param     options  optional. See InputOptions.
+ * @returns   string
+ */
+function getInput(name, options) {
+    const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
+    if (options && options.required && !val) {
+        throw new Error(`Input required and not supplied: ${name}`);
+    }
+    if (options && options.trimWhitespace === false) {
+        return val;
+    }
+    return val.trim();
+}
+exports.getInput = getInput;
+/**
+ * Gets the values of an multiline input.  Each value is also trimmed.
+ *
+ * @param     name     name of the input to get
+ * @param     options  optional. See InputOptions.
+ * @returns   string[]
+ *
+ */
+function getMultilineInput(name, options) {
+    const inputs = getInput(name, options)
+        .split('\n')
+        .filter(x => x !== '');
+    return inputs;
+}
+exports.getMultilineInput = getMultilineInput;
+/**
+ * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification.
+ * Support boolean input list: `true | True | TRUE | false | False | FALSE` .
+ * The return value is also in boolean type.
+ * ref: https://yaml.org/spec/1.2/spec.html#id2804923
+ *
+ * @param     name     name of the input to get
+ * @param     options  optional. See InputOptions.
+ * @returns   boolean
+ */
+function getBooleanInput(name, options) {
+    const trueValue = ['true', 'True', 'TRUE'];
+    const falseValue = ['false', 'False', 'FALSE'];
+    const val = getInput(name, options);
+    if (trueValue.includes(val))
+        return true;
+    if (falseValue.includes(val))
+        return false;
+    throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` +
+        `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
+}
+exports.getBooleanInput = getBooleanInput;
+/**
+ * Sets the value of an output.
+ *
+ * @param     name     name of the output to set
+ * @param     value    value to store. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function setOutput(name, value) {
+    process.stdout.write(os.EOL);
+    command_1.issueCommand('set-output', { name }, value);
+}
+exports.setOutput = setOutput;
+/**
+ * Enables or disables the echoing of commands into stdout for the rest of the step.
+ * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
+ *
+ */
+function setCommandEcho(enabled) {
+    command_1.issue('echo', enabled ? 'on' : 'off');
+}
+exports.setCommandEcho = setCommandEcho;
+//-----------------------------------------------------------------------
+// Results
+//-----------------------------------------------------------------------
+/**
+ * Sets the action status to failed.
+ * When the action exits it will be with an exit code of 1
+ * @param message add error issue message
+ */
+function setFailed(message) {
+    process.exitCode = ExitCode.Failure;
+    error(message);
+}
+exports.setFailed = setFailed;
+//-----------------------------------------------------------------------
+// Logging Commands
+//-----------------------------------------------------------------------
+/**
+ * Gets whether Actions Step Debug is on or not
+ */
+function isDebug() {
+    return process.env['RUNNER_DEBUG'] === '1';
+}
+exports.isDebug = isDebug;
+/**
+ * Writes debug message to user log
+ * @param message debug message
+ */
+function debug(message) {
+    command_1.issueCommand('debug', {}, message);
+}
+exports.debug = debug;
+/**
+ * Adds an error issue
+ * @param message error issue message. Errors will be converted to string via toString()
+ * @param properties optional properties to add to the annotation.
+ */
+function error(message, properties = {}) {
+    command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
+}
+exports.error = error;
+/**
+ * Adds a warning issue
+ * @param message warning issue message. Errors will be converted to string via toString()
+ * @param properties optional properties to add to the annotation.
+ */
+function warning(message, properties = {}) {
+    command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
+}
+exports.warning = warning;
+/**
+ * Adds a notice issue
+ * @param message notice issue message. Errors will be converted to string via toString()
+ * @param properties optional properties to add to the annotation.
+ */
+function notice(message, properties = {}) {
+    command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
+}
+exports.notice = notice;
+/**
+ * Writes info to log with console.log.
+ * @param message info message
+ */
+function info(message) {
+    process.stdout.write(message + os.EOL);
+}
+exports.info = info;
+/**
+ * Begin an output group.
+ *
+ * Output until the next `groupEnd` will be foldable in this group
+ *
+ * @param name The name of the output group
+ */
+function startGroup(name) {
+    command_1.issue('group', name);
+}
+exports.startGroup = startGroup;
+/**
+ * End an output group.
+ */
+function endGroup() {
+    command_1.issue('endgroup');
+}
+exports.endGroup = endGroup;
+/**
+ * Wrap an asynchronous function call in a group.
+ *
+ * Returns the same type as the function itself.
+ *
+ * @param name The name of the group
+ * @param fn The function to wrap in the group
+ */
+function group(name, fn) {
+    return __awaiter(this, void 0, void 0, function* () {
+        startGroup(name);
+        let result;
+        try {
+            result = yield fn();
+        }
+        finally {
+            endGroup();
+        }
+        return result;
+    });
+}
+exports.group = group;
+//-----------------------------------------------------------------------
+// Wrapper action state
+//-----------------------------------------------------------------------
+/**
+ * Saves state for current action, the state can only be retrieved by this action's post job execution.
+ *
+ * @param     name     name of the state to store
+ * @param     value    value to store. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function saveState(name, value) {
+    command_1.issueCommand('save-state', { name }, value);
+}
+exports.saveState = saveState;
+/**
+ * Gets the value of an state set by this action's main execution.
+ *
+ * @param     name     name of the state to get
+ * @returns   string
+ */
+function getState(name) {
+    return process.env[`STATE_${name}`] || '';
+}
+exports.getState = getState;
+function getIDToken(aud) {
+    return __awaiter(this, void 0, void 0, function* () {
+        return yield oidc_utils_1.OidcClient.getIDToken(aud);
+    });
+}
+exports.getIDToken = getIDToken;
+//# sourceMappingURL=core.js.map
+
+/***/ }),
+
+/***/ 717:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+// For internal use, subject to change.
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.issueCommand = void 0;
+// We use any as a valid input type
+/* eslint-disable @typescript-eslint/no-explicit-any */
+const fs = __importStar(__nccwpck_require__(147));
+const os = __importStar(__nccwpck_require__(37));
+const utils_1 = __nccwpck_require__(278);
+function issueCommand(command, message) {
+    const filePath = process.env[`GITHUB_${command}`];
+    if (!filePath) {
+        throw new Error(`Unable to find environment variable for file command ${command}`);
+    }
+    if (!fs.existsSync(filePath)) {
+        throw new Error(`Missing file at path: ${filePath}`);
+    }
+    fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, {
+        encoding: 'utf8'
+    });
+}
+exports.issueCommand = issueCommand;
+//# sourceMappingURL=file-command.js.map
+
+/***/ }),
+
+/***/ 41:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.OidcClient = void 0;
+const http_client_1 = __nccwpck_require__(925);
+const auth_1 = __nccwpck_require__(702);
+const core_1 = __nccwpck_require__(186);
+class OidcClient {
+    static createHttpClient(allowRetry = true, maxRetry = 10) {
+        const requestOptions = {
+            allowRetries: allowRetry,
+            maxRetries: maxRetry
+        };
+        return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions);
+    }
+    static getRequestToken() {
+        const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'];
+        if (!token) {
+            throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable');
+        }
+        return token;
+    }
+    static getIDTokenUrl() {
+        const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL'];
+        if (!runtimeUrl) {
+            throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable');
+        }
+        return runtimeUrl;
+    }
+    static getCall(id_token_url) {
+        var _a;
+        return __awaiter(this, void 0, void 0, function* () {
+            const httpclient = OidcClient.createHttpClient();
+            const res = yield httpclient
+                .getJson(id_token_url)
+                .catch(error => {
+                throw new Error(`Failed to get ID Token. \n 
+        Error Code : ${error.statusCode}\n 
+        Error Message: ${error.result.message}`);
+            });
+            const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
+            if (!id_token) {
+                throw new Error('Response json body do not have ID Token field');
+            }
+            return id_token;
+        });
+    }
+    static getIDToken(audience) {
+        return __awaiter(this, void 0, void 0, function* () {
+            try {
+                // New ID Token is requested from action service
+                let id_token_url = OidcClient.getIDTokenUrl();
+                if (audience) {
+                    const encodedAudience = encodeURIComponent(audience);
+                    id_token_url = `${id_token_url}&audience=${encodedAudience}`;
+                }
+                core_1.debug(`ID token url is ${id_token_url}`);
+                const id_token = yield OidcClient.getCall(id_token_url);
+                core_1.setSecret(id_token);
+                return id_token;
+            }
+            catch (error) {
+                throw new Error(`Error message: ${error.message}`);
+            }
+        });
+    }
+}
+exports.OidcClient = OidcClient;
+//# sourceMappingURL=oidc-utils.js.map
+
+/***/ }),
+
+/***/ 278:
+/***/ ((__unused_webpack_module, exports) => {
+
+
+// We use any as a valid input type
+/* eslint-disable @typescript-eslint/no-explicit-any */
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.toCommandProperties = exports.toCommandValue = void 0;
+/**
+ * Sanitizes an input into a string so it can be passed into issueCommand safely
+ * @param input input to sanitize into a string
+ */
+function toCommandValue(input) {
+    if (input === null || input === undefined) {
+        return '';
+    }
+    else if (typeof input === 'string' || input instanceof String) {
+        return input;
+    }
+    return JSON.stringify(input);
+}
+exports.toCommandValue = toCommandValue;
+/**
+ *
+ * @param annotationProperties
+ * @returns The command properties to send with the actual annotation command
+ * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646
+ */
+function toCommandProperties(annotationProperties) {
+    if (!Object.keys(annotationProperties).length) {
+        return {};
+    }
+    return {
+        title: annotationProperties.title,
+        file: annotationProperties.file,
+        line: annotationProperties.startLine,
+        endLine: annotationProperties.endLine,
+        col: annotationProperties.startColumn,
+        endColumn: annotationProperties.endColumn
+    };
+}
+exports.toCommandProperties = toCommandProperties;
+//# sourceMappingURL=utils.js.map
+
+/***/ }),
+
+/***/ 514:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.getExecOutput = exports.exec = void 0;
+const string_decoder_1 = __nccwpck_require__(576);
+const tr = __importStar(__nccwpck_require__(159));
+/**
+ * Exec a command.
+ * Output will be streamed to the live console.
+ * Returns promise with return code
+ *
+ * @param     commandLine        command to execute (can include additional args). Must be correctly escaped.
+ * @param     args               optional arguments for tool. Escaping is handled by the lib.
+ * @param     options            optional exec options.  See ExecOptions
+ * @returns   Promise<number>    exit code
+ */
+function exec(commandLine, args, options) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const commandArgs = tr.argStringToArray(commandLine);
+        if (commandArgs.length === 0) {
+            throw new Error(`Parameter 'commandLine' cannot be null or empty.`);
+        }
+        // Path to tool to execute should be first arg
+        const toolPath = commandArgs[0];
+        args = commandArgs.slice(1).concat(args || []);
+        const runner = new tr.ToolRunner(toolPath, args, options);
+        return runner.exec();
+    });
+}
+exports.exec = exec;
+/**
+ * Exec a command and get the output.
+ * Output will be streamed to the live console.
+ * Returns promise with the exit code and collected stdout and stderr
+ *
+ * @param     commandLine           command to execute (can include additional args). Must be correctly escaped.
+ * @param     args                  optional arguments for tool. Escaping is handled by the lib.
+ * @param     options               optional exec options.  See ExecOptions
+ * @returns   Promise<ExecOutput>   exit code, stdout, and stderr
+ */
+function getExecOutput(commandLine, args, options) {
+    var _a, _b;
+    return __awaiter(this, void 0, void 0, function* () {
+        let stdout = '';
+        let stderr = '';
+        //Using string decoder covers the case where a mult-byte character is split
+        const stdoutDecoder = new string_decoder_1.StringDecoder('utf8');
+        const stderrDecoder = new string_decoder_1.StringDecoder('utf8');
+        const originalStdoutListener = (_a = options === null || options === void 0 ? void 0 : options.listeners) === null || _a === void 0 ? void 0 : _a.stdout;
+        const originalStdErrListener = (_b = options === null || options === void 0 ? void 0 : options.listeners) === null || _b === void 0 ? void 0 : _b.stderr;
+        const stdErrListener = (data) => {
+            stderr += stderrDecoder.write(data);
+            if (originalStdErrListener) {
+                originalStdErrListener(data);
+            }
+        };
+        const stdOutListener = (data) => {
+            stdout += stdoutDecoder.write(data);
+            if (originalStdoutListener) {
+                originalStdoutListener(data);
+            }
+        };
+        const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener });
+        const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners }));
+        //flush any remaining characters
+        stdout += stdoutDecoder.end();
+        stderr += stderrDecoder.end();
+        return {
+            exitCode,
+            stdout,
+            stderr
+        };
+    });
+}
+exports.getExecOutput = getExecOutput;
+//# sourceMappingURL=exec.js.map
+
+/***/ }),
+
+/***/ 159:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.argStringToArray = exports.ToolRunner = void 0;
+const os = __importStar(__nccwpck_require__(37));
+const events = __importStar(__nccwpck_require__(361));
+const child = __importStar(__nccwpck_require__(81));
+const path = __importStar(__nccwpck_require__(17));
+const io = __importStar(__nccwpck_require__(436));
+const ioUtil = __importStar(__nccwpck_require__(962));
+const timers_1 = __nccwpck_require__(512);
+/* eslint-disable @typescript-eslint/unbound-method */
+const IS_WINDOWS = process.platform === 'win32';
+/*
+ * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way.
+ */
+class ToolRunner extends events.EventEmitter {
+    constructor(toolPath, args, options) {
+        super();
+        if (!toolPath) {
+            throw new Error("Parameter 'toolPath' cannot be null or empty.");
+        }
+        this.toolPath = toolPath;
+        this.args = args || [];
+        this.options = options || {};
+    }
+    _debug(message) {
+        if (this.options.listeners && this.options.listeners.debug) {
+            this.options.listeners.debug(message);
+        }
+    }
+    _getCommandString(options, noPrefix) {
+        const toolPath = this._getSpawnFileName();
+        const args = this._getSpawnArgs(options);
+        let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
+        if (IS_WINDOWS) {
+            // Windows + cmd file
+            if (this._isCmdFile()) {
+                cmd += toolPath;
+                for (const a of args) {
+                    cmd += ` ${a}`;
+                }
+            }
+            // Windows + verbatim
+            else if (options.windowsVerbatimArguments) {
+                cmd += `"${toolPath}"`;
+                for (const a of args) {
+                    cmd += ` ${a}`;
+                }
+            }
+            // Windows (regular)
+            else {
+                cmd += this._windowsQuoteCmdArg(toolPath);
+                for (const a of args) {
+                    cmd += ` ${this._windowsQuoteCmdArg(a)}`;
+                }
+            }
+        }
+        else {
+            // OSX/Linux - this can likely be improved with some form of quoting.
+            // creating processes on Unix is fundamentally different than Windows.
+            // on Unix, execvp() takes an arg array.
+            cmd += toolPath;
+            for (const a of args) {
+                cmd += ` ${a}`;
+            }
+        }
+        return cmd;
+    }
+    _processLineBuffer(data, strBuffer, onLine) {
+        try {
+            let s = strBuffer + data.toString();
+            let n = s.indexOf(os.EOL);
+            while (n > -1) {
+                const line = s.substring(0, n);
+                onLine(line);
+                // the rest of the string ...
+                s = s.substring(n + os.EOL.length);
+                n = s.indexOf(os.EOL);
+            }
+            return s;
+        }
+        catch (err) {
+            // streaming lines to console is best effort.  Don't fail a build.
+            this._debug(`error processing line. Failed with error ${err}`);
+            return '';
+        }
+    }
+    _getSpawnFileName() {
+        if (IS_WINDOWS) {
+            if (this._isCmdFile()) {
+                return process.env['COMSPEC'] || 'cmd.exe';
+            }
+        }
+        return this.toolPath;
+    }
+    _getSpawnArgs(options) {
+        if (IS_WINDOWS) {
+            if (this._isCmdFile()) {
+                let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`;
+                for (const a of this.args) {
+                    argline += ' ';
+                    argline += options.windowsVerbatimArguments
+                        ? a
+                        : this._windowsQuoteCmdArg(a);
+                }
+                argline += '"';
+                return [argline];
+            }
+        }
+        return this.args;
+    }
+    _endsWith(str, end) {
+        return str.endsWith(end);
+    }
+    _isCmdFile() {
+        const upperToolPath = this.toolPath.toUpperCase();
+        return (this._endsWith(upperToolPath, '.CMD') ||
+            this._endsWith(upperToolPath, '.BAT'));
+    }
+    _windowsQuoteCmdArg(arg) {
+        // for .exe, apply the normal quoting rules that libuv applies
+        if (!this._isCmdFile()) {
+            return this._uvQuoteCmdArg(arg);
+        }
+        // otherwise apply quoting rules specific to the cmd.exe command line parser.
+        // the libuv rules are generic and are not designed specifically for cmd.exe
+        // command line parser.
+        //
+        // for a detailed description of the cmd.exe command line parser, refer to
+        // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
+        // need quotes for empty arg
+        if (!arg) {
+            return '""';
+        }
+        // determine whether the arg needs to be quoted
+        const cmdSpecialChars = [
+            ' ',
+            '\t',
+            '&',
+            '(',
+            ')',
+            '[',
+            ']',
+            '{',
+            '}',
+            '^',
+            '=',
+            ';',
+            '!',
+            "'",
+            '+',
+            ',',
+            '`',
+            '~',
+            '|',
+            '<',
+            '>',
+            '"'
+        ];
+        let needsQuotes = false;
+        for (const char of arg) {
+            if (cmdSpecialChars.some(x => x === char)) {
+                needsQuotes = true;
+                break;
+            }
+        }
+        // short-circuit if quotes not needed
+        if (!needsQuotes) {
+            return arg;
+        }
+        // the following quoting rules are very similar to the rules that by libuv applies.
+        //
+        // 1) wrap the string in quotes
+        //
+        // 2) double-up quotes - i.e. " => ""
+        //
+        //    this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
+        //    doesn't work well with a cmd.exe command line.
+        //
+        //    note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
+        //    for example, the command line:
+        //          foo.exe "myarg:""my val"""
+        //    is parsed by a .NET console app into an arg array:
+        //          [ "myarg:\"my val\"" ]
+        //    which is the same end result when applying libuv quoting rules. although the actual
+        //    command line from libuv quoting rules would look like:
+        //          foo.exe "myarg:\"my val\""
+        //
+        // 3) double-up slashes that precede a quote,
+        //    e.g.  hello \world    => "hello \world"
+        //          hello\"world    => "hello\\""world"
+        //          hello\\"world   => "hello\\\\""world"
+        //          hello world\    => "hello world\\"
+        //
+        //    technically this is not required for a cmd.exe command line, or the batch argument parser.
+        //    the reasons for including this as a .cmd quoting rule are:
+        //
+        //    a) this is optimized for the scenario where the argument is passed from the .cmd file to an
+        //       external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
+        //
+        //    b) it's what we've been doing previously (by deferring to node default behavior) and we
+        //       haven't heard any complaints about that aspect.
+        //
+        // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
+        // escaped when used on the command line directly - even though within a .cmd file % can be escaped
+        // by using %%.
+        //
+        // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
+        // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
+        //
+        // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
+        // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
+        // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
+        // to an external program.
+        //
+        // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
+        // % can be escaped within a .cmd file.
+        let reverse = '"';
+        let quoteHit = true;
+        for (let i = arg.length; i > 0; i--) {
+            // walk the string in reverse
+            reverse += arg[i - 1];
+            if (quoteHit && arg[i - 1] === '\\') {
+                reverse += '\\'; // double the slash
+            }
+            else if (arg[i - 1] === '"') {
+                quoteHit = true;
+                reverse += '"'; // double the quote
+            }
+            else {
+                quoteHit = false;
+            }
+        }
+        reverse += '"';
+        return reverse
+            .split('')
+            .reverse()
+            .join('');
+    }
+    _uvQuoteCmdArg(arg) {
+        // Tool runner wraps child_process.spawn() and needs to apply the same quoting as
+        // Node in certain cases where the undocumented spawn option windowsVerbatimArguments
+        // is used.
+        //
+        // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
+        // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
+        // pasting copyright notice from Node within this function:
+        //
+        //      Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+        //
+        //      Permission is hereby granted, free of charge, to any person obtaining a copy
+        //      of this software and associated documentation files (the "Software"), to
+        //      deal in the Software without restriction, including without limitation the
+        //      rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+        //      sell copies of the Software, and to permit persons to whom the Software is
+        //      furnished to do so, subject to the following conditions:
+        //
+        //      The above copyright notice and this permission notice shall be included in
+        //      all copies or substantial portions of the Software.
+        //
+        //      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+        //      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+        //      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+        //      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        //      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+        //      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+        //      IN THE SOFTWARE.
+        if (!arg) {
+            // Need double quotation for empty argument
+            return '""';
+        }
+        if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) {
+            // No quotation needed
+            return arg;
+        }
+        if (!arg.includes('"') && !arg.includes('\\')) {
+            // No embedded double quotes or backslashes, so I can just wrap
+            // quote marks around the whole thing.
+            return `"${arg}"`;
+        }
+        // Expected input/output:
+        //   input : hello"world
+        //   output: "hello\"world"
+        //   input : hello""world
+        //   output: "hello\"\"world"
+        //   input : hello\world
+        //   output: hello\world
+        //   input : hello\\world
+        //   output: hello\\world
+        //   input : hello\"world
+        //   output: "hello\\\"world"
+        //   input : hello\\"world
+        //   output: "hello\\\\\"world"
+        //   input : hello world\
+        //   output: "hello world\\" - note the comment in libuv actually reads "hello world\"
+        //                             but it appears the comment is wrong, it should be "hello world\\"
+        let reverse = '"';
+        let quoteHit = true;
+        for (let i = arg.length; i > 0; i--) {
+            // walk the string in reverse
+            reverse += arg[i - 1];
+            if (quoteHit && arg[i - 1] === '\\') {
+                reverse += '\\';
+            }
+            else if (arg[i - 1] === '"') {
+                quoteHit = true;
+                reverse += '\\';
+            }
+            else {
+                quoteHit = false;
+            }
+        }
+        reverse += '"';
+        return reverse
+            .split('')
+            .reverse()
+            .join('');
+    }
+    _cloneExecOptions(options) {
+        options = options || {};
+        const result = {
+            cwd: options.cwd || process.cwd(),
+            env: options.env || process.env,
+            silent: options.silent || false,
+            windowsVerbatimArguments: options.windowsVerbatimArguments || false,
+            failOnStdErr: options.failOnStdErr || false,
+            ignoreReturnCode: options.ignoreReturnCode || false,
+            delay: options.delay || 10000
+        };
+        result.outStream = options.outStream || process.stdout;
+        result.errStream = options.errStream || process.stderr;
+        return result;
+    }
+    _getSpawnOptions(options, toolPath) {
+        options = options || {};
+        const result = {};
+        result.cwd = options.cwd;
+        result.env = options.env;
+        result['windowsVerbatimArguments'] =
+            options.windowsVerbatimArguments || this._isCmdFile();
+        if (options.windowsVerbatimArguments) {
+            result.argv0 = `"${toolPath}"`;
+        }
+        return result;
+    }
+    /**
+     * Exec a tool.
+     * Output will be streamed to the live console.
+     * Returns promise with return code
+     *
+     * @param     tool     path to tool to exec
+     * @param     options  optional exec options.  See ExecOptions
+     * @returns   number
+     */
+    exec() {
+        return __awaiter(this, void 0, void 0, function* () {
+            // root the tool path if it is unrooted and contains relative pathing
+            if (!ioUtil.isRooted(this.toolPath) &&
+                (this.toolPath.includes('/') ||
+                    (IS_WINDOWS && this.toolPath.includes('\\')))) {
+                // prefer options.cwd if it is specified, however options.cwd may also need to be rooted
+                this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
+            }
+            // if the tool is only a file name, then resolve it from the PATH
+            // otherwise verify it exists (add extension on Windows if necessary)
+            this.toolPath = yield io.which(this.toolPath, true);
+            return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
+                this._debug(`exec tool: ${this.toolPath}`);
+                this._debug('arguments:');
+                for (const arg of this.args) {
+                    this._debug(`   ${arg}`);
+                }
+                const optionsNonNull = this._cloneExecOptions(this.options);
+                if (!optionsNonNull.silent && optionsNonNull.outStream) {
+                    optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
+                }
+                const state = new ExecState(optionsNonNull, this.toolPath);
+                state.on('debug', (message) => {
+                    this._debug(message);
+                });
+                if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) {
+                    return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`));
+                }
+                const fileName = this._getSpawnFileName();
+                const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName));
+                let stdbuffer = '';
+                if (cp.stdout) {
+                    cp.stdout.on('data', (data) => {
+                        if (this.options.listeners && this.options.listeners.stdout) {
+                            this.options.listeners.stdout(data);
+                        }
+                        if (!optionsNonNull.silent && optionsNonNull.outStream) {
+                            optionsNonNull.outStream.write(data);
+                        }
+                        stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => {
+                            if (this.options.listeners && this.options.listeners.stdline) {
+                                this.options.listeners.stdline(line);
+                            }
+                        });
+                    });
+                }
+                let errbuffer = '';
+                if (cp.stderr) {
+                    cp.stderr.on('data', (data) => {
+                        state.processStderr = true;
+                        if (this.options.listeners && this.options.listeners.stderr) {
+                            this.options.listeners.stderr(data);
+                        }
+                        if (!optionsNonNull.silent &&
+                            optionsNonNull.errStream &&
+                            optionsNonNull.outStream) {
+                            const s = optionsNonNull.failOnStdErr
+                                ? optionsNonNull.errStream
+                                : optionsNonNull.outStream;
+                            s.write(data);
+                        }
+                        errbuffer = this._processLineBuffer(data, errbuffer, (line) => {
+                            if (this.options.listeners && this.options.listeners.errline) {
+                                this.options.listeners.errline(line);
+                            }
+                        });
+                    });
+                }
+                cp.on('error', (err) => {
+                    state.processError = err.message;
+                    state.processExited = true;
+                    state.processClosed = true;
+                    state.CheckComplete();
+                });
+                cp.on('exit', (code) => {
+                    state.processExitCode = code;
+                    state.processExited = true;
+                    this._debug(`Exit code ${code} received from tool '${this.toolPath}'`);
+                    state.CheckComplete();
+                });
+                cp.on('close', (code) => {
+                    state.processExitCode = code;
+                    state.processExited = true;
+                    state.processClosed = true;
+                    this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);
+                    state.CheckComplete();
+                });
+                state.on('done', (error, exitCode) => {
+                    if (stdbuffer.length > 0) {
+                        this.emit('stdline', stdbuffer);
+                    }
+                    if (errbuffer.length > 0) {
+                        this.emit('errline', errbuffer);
+                    }
+                    cp.removeAllListeners();
+                    if (error) {
+                        reject(error);
+                    }
+                    else {
+                        resolve(exitCode);
+                    }
+                });
+                if (this.options.input) {
+                    if (!cp.stdin) {
+                        throw new Error('child process missing stdin');
+                    }
+                    cp.stdin.end(this.options.input);
+                }
+            }));
+        });
+    }
+}
+exports.ToolRunner = ToolRunner;
+/**
+ * Convert an arg string to an array of args. Handles escaping
+ *
+ * @param    argString   string of arguments
+ * @returns  string[]    array of arguments
+ */
+function argStringToArray(argString) {
+    const args = [];
+    let inQuotes = false;
+    let escaped = false;
+    let arg = '';
+    function append(c) {
+        // we only escape double quotes.
+        if (escaped && c !== '"') {
+            arg += '\\';
+        }
+        arg += c;
+        escaped = false;
+    }
+    for (let i = 0; i < argString.length; i++) {
+        const c = argString.charAt(i);
+        if (c === '"') {
+            if (!escaped) {
+                inQuotes = !inQuotes;
+            }
+            else {
+                append(c);
+            }
+            continue;
+        }
+        if (c === '\\' && escaped) {
+            append(c);
+            continue;
+        }
+        if (c === '\\' && inQuotes) {
+            escaped = true;
+            continue;
+        }
+        if (c === ' ' && !inQuotes) {
+            if (arg.length > 0) {
+                args.push(arg);
+                arg = '';
+            }
+            continue;
+        }
+        append(c);
+    }
+    if (arg.length > 0) {
+        args.push(arg.trim());
+    }
+    return args;
+}
+exports.argStringToArray = argStringToArray;
+class ExecState extends events.EventEmitter {
+    constructor(options, toolPath) {
+        super();
+        this.processClosed = false; // tracks whether the process has exited and stdio is closed
+        this.processError = '';
+        this.processExitCode = 0;
+        this.processExited = false; // tracks whether the process has exited
+        this.processStderr = false; // tracks whether stderr was written to
+        this.delay = 10000; // 10 seconds
+        this.done = false;
+        this.timeout = null;
+        if (!toolPath) {
+            throw new Error('toolPath must not be empty');
+        }
+        this.options = options;
+        this.toolPath = toolPath;
+        if (options.delay) {
+            this.delay = options.delay;
+        }
+    }
+    CheckComplete() {
+        if (this.done) {
+            return;
+        }
+        if (this.processClosed) {
+            this._setResult();
+        }
+        else if (this.processExited) {
+            this.timeout = timers_1.setTimeout(ExecState.HandleTimeout, this.delay, this);
+        }
+    }
+    _debug(message) {
+        this.emit('debug', message);
+    }
+    _setResult() {
+        // determine whether there is an error
+        let error;
+        if (this.processExited) {
+            if (this.processError) {
+                error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`);
+            }
+            else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) {
+                error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);
+            }
+            else if (this.processStderr && this.options.failOnStdErr) {
+                error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`);
+            }
+        }
+        // clear the timeout
+        if (this.timeout) {
+            clearTimeout(this.timeout);
+            this.timeout = null;
+        }
+        this.done = true;
+        this.emit('done', error, this.processExitCode);
+    }
+    static HandleTimeout(state) {
+        if (state.done) {
+            return;
+        }
+        if (!state.processClosed && state.processExited) {
+            const message = `The STDIO streams did not close within ${state.delay /
+                1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;
+            state._debug(message);
+        }
+        state._setResult();
+    }
+}
+//# sourceMappingURL=toolrunner.js.map
+
+/***/ }),
+
+/***/ 702:
+/***/ ((__unused_webpack_module, exports) => {
+
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+class BasicCredentialHandler {
+    constructor(username, password) {
+        this.username = username;
+        this.password = password;
+    }
+    prepareRequest(options) {
+        options.headers['Authorization'] =
+            'Basic ' +
+                Buffer.from(this.username + ':' + this.password).toString('base64');
+    }
+    // This handler cannot handle 401
+    canHandleAuthentication(response) {
+        return false;
+    }
+    handleAuthentication(httpClient, requestInfo, objs) {
+        return null;
+    }
+}
+exports.BasicCredentialHandler = BasicCredentialHandler;
+class BearerCredentialHandler {
+    constructor(token) {
+        this.token = token;
+    }
+    // currently implements pre-authorization
+    // TODO: support preAuth = false where it hooks on 401
+    prepareRequest(options) {
+        options.headers['Authorization'] = 'Bearer ' + this.token;
+    }
+    // This handler cannot handle 401
+    canHandleAuthentication(response) {
+        return false;
+    }
+    handleAuthentication(httpClient, requestInfo, objs) {
+        return null;
+    }
+}
+exports.BearerCredentialHandler = BearerCredentialHandler;
+class PersonalAccessTokenCredentialHandler {
+    constructor(token) {
+        this.token = token;
+    }
+    // currently implements pre-authorization
+    // TODO: support preAuth = false where it hooks on 401
+    prepareRequest(options) {
+        options.headers['Authorization'] =
+            'Basic ' + Buffer.from('PAT:' + this.token).toString('base64');
+    }
+    // This handler cannot handle 401
+    canHandleAuthentication(response) {
+        return false;
+    }
+    handleAuthentication(httpClient, requestInfo, objs) {
+        return null;
+    }
+}
+exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler;
+
+
+/***/ }),
+
+/***/ 925:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+const http = __nccwpck_require__(685);
+const https = __nccwpck_require__(687);
+const pm = __nccwpck_require__(443);
+let tunnel;
+var HttpCodes;
+(function (HttpCodes) {
+    HttpCodes[HttpCodes["OK"] = 200] = "OK";
+    HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
+    HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
+    HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
+    HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
+    HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
+    HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
+    HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
+    HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
+    HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
+    HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
+    HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
+    HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
+    HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
+    HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
+    HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
+    HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
+    HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
+    HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
+    HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
+    HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
+    HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests";
+    HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
+    HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
+    HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
+    HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
+    HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
+})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
+var Headers;
+(function (Headers) {
+    Headers["Accept"] = "accept";
+    Headers["ContentType"] = "content-type";
+})(Headers = exports.Headers || (exports.Headers = {}));
+var MediaTypes;
+(function (MediaTypes) {
+    MediaTypes["ApplicationJson"] = "application/json";
+})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {}));
+/**
+ * Returns the proxy URL, depending upon the supplied url and proxy environment variables.
+ * @param serverUrl  The server URL where the request will be sent. For example, https://api.github.com
+ */
+function getProxyUrl(serverUrl) {
+    let proxyUrl = pm.getProxyUrl(new URL(serverUrl));
+    return proxyUrl ? proxyUrl.href : '';
+}
+exports.getProxyUrl = getProxyUrl;
+const HttpRedirectCodes = [
+    HttpCodes.MovedPermanently,
+    HttpCodes.ResourceMoved,
+    HttpCodes.SeeOther,
+    HttpCodes.TemporaryRedirect,
+    HttpCodes.PermanentRedirect
+];
+const HttpResponseRetryCodes = [
+    HttpCodes.BadGateway,
+    HttpCodes.ServiceUnavailable,
+    HttpCodes.GatewayTimeout
+];
+const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
+const ExponentialBackoffCeiling = 10;
+const ExponentialBackoffTimeSlice = 5;
+class HttpClientError extends Error {
+    constructor(message, statusCode) {
+        super(message);
+        this.name = 'HttpClientError';
+        this.statusCode = statusCode;
+        Object.setPrototypeOf(this, HttpClientError.prototype);
+    }
+}
+exports.HttpClientError = HttpClientError;
+class HttpClientResponse {
+    constructor(message) {
+        this.message = message;
+    }
+    readBody() {
+        return new Promise(async (resolve, reject) => {
+            let output = Buffer.alloc(0);
+            this.message.on('data', (chunk) => {
+                output = Buffer.concat([output, chunk]);
+            });
+            this.message.on('end', () => {
+                resolve(output.toString());
+            });
+        });
+    }
+}
+exports.HttpClientResponse = HttpClientResponse;
+function isHttps(requestUrl) {
+    let parsedUrl = new URL(requestUrl);
+    return parsedUrl.protocol === 'https:';
+}
+exports.isHttps = isHttps;
+class HttpClient {
+    constructor(userAgent, handlers, requestOptions) {
+        this._ignoreSslError = false;
+        this._allowRedirects = true;
+        this._allowRedirectDowngrade = false;
+        this._maxRedirects = 50;
+        this._allowRetries = false;
+        this._maxRetries = 1;
+        this._keepAlive = false;
+        this._disposed = false;
+        this.userAgent = userAgent;
+        this.handlers = handlers || [];
+        this.requestOptions = requestOptions;
+        if (requestOptions) {
+            if (requestOptions.ignoreSslError != null) {
+                this._ignoreSslError = requestOptions.ignoreSslError;
+            }
+            this._socketTimeout = requestOptions.socketTimeout;
+            if (requestOptions.allowRedirects != null) {
+                this._allowRedirects = requestOptions.allowRedirects;
+            }
+            if (requestOptions.allowRedirectDowngrade != null) {
+                this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade;
+            }
+            if (requestOptions.maxRedirects != null) {
+                this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
+            }
+            if (requestOptions.keepAlive != null) {
+                this._keepAlive = requestOptions.keepAlive;
+            }
+            if (requestOptions.allowRetries != null) {
+                this._allowRetries = requestOptions.allowRetries;
+            }
+            if (requestOptions.maxRetries != null) {
+                this._maxRetries = requestOptions.maxRetries;
+            }
+        }
+    }
+    options(requestUrl, additionalHeaders) {
+        return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
+    }
+    get(requestUrl, additionalHeaders) {
+        return this.request('GET', requestUrl, null, additionalHeaders || {});
+    }
+    del(requestUrl, additionalHeaders) {
+        return this.request('DELETE', requestUrl, null, additionalHeaders || {});
+    }
+    post(requestUrl, data, additionalHeaders) {
+        return this.request('POST', requestUrl, data, additionalHeaders || {});
+    }
+    patch(requestUrl, data, additionalHeaders) {
+        return this.request('PATCH', requestUrl, data, additionalHeaders || {});
+    }
+    put(requestUrl, data, additionalHeaders) {
+        return this.request('PUT', requestUrl, data, additionalHeaders || {});
+    }
+    head(requestUrl, additionalHeaders) {
+        return this.request('HEAD', requestUrl, null, additionalHeaders || {});
+    }
+    sendStream(verb, requestUrl, stream, additionalHeaders) {
+        return this.request(verb, requestUrl, stream, additionalHeaders);
+    }
+    /**
+     * Gets a typed object from an endpoint
+     * Be aware that not found returns a null.  Other errors (4xx, 5xx) reject the promise
+     */
+    async getJson(requestUrl, additionalHeaders = {}) {
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        let res = await this.get(requestUrl, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async postJson(requestUrl, obj, additionalHeaders = {}) {
+        let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
+        let res = await this.post(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async putJson(requestUrl, obj, additionalHeaders = {}) {
+        let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
+        let res = await this.put(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async patchJson(requestUrl, obj, additionalHeaders = {}) {
+        let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
+        let res = await this.patch(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    /**
+     * Makes a raw http request.
+     * All other methods such as get, post, patch, and request ultimately call this.
+     * Prefer get, del, post and patch
+     */
+    async request(verb, requestUrl, data, headers) {
+        if (this._disposed) {
+            throw new Error('Client has already been disposed.');
+        }
+        let parsedUrl = new URL(requestUrl);
+        let info = this._prepareRequest(verb, parsedUrl, headers);
+        // Only perform retries on reads since writes may not be idempotent.
+        let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
+            ? this._maxRetries + 1
+            : 1;
+        let numTries = 0;
+        let response;
+        while (numTries < maxTries) {
+            response = await this.requestRaw(info, data);
+            // Check if it's an authentication challenge
+            if (response &&
+                response.message &&
+                response.message.statusCode === HttpCodes.Unauthorized) {
+                let authenticationHandler;
+                for (let i = 0; i < this.handlers.length; i++) {
+                    if (this.handlers[i].canHandleAuthentication(response)) {
+                        authenticationHandler = this.handlers[i];
+                        break;
+                    }
+                }
+                if (authenticationHandler) {
+                    return authenticationHandler.handleAuthentication(this, info, data);
+                }
+                else {
+                    // We have received an unauthorized response but have no handlers to handle it.
+                    // Let the response return to the caller.
+                    return response;
+                }
+            }
+            let redirectsRemaining = this._maxRedirects;
+            while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
+                this._allowRedirects &&
+                redirectsRemaining > 0) {
+                const redirectUrl = response.message.headers['location'];
+                if (!redirectUrl) {
+                    // if there's no location to redirect to, we won't
+                    break;
+                }
+                let parsedRedirectUrl = new URL(redirectUrl);
+                if (parsedUrl.protocol == 'https:' &&
+                    parsedUrl.protocol != parsedRedirectUrl.protocol &&
+                    !this._allowRedirectDowngrade) {
+                    throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.');
+                }
+                // we need to finish reading the response before reassigning response
+                // which will leak the open socket.
+                await response.readBody();
+                // strip authorization header if redirected to a different hostname
+                if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
+                    for (let header in headers) {
+                        // header names are case insensitive
+                        if (header.toLowerCase() === 'authorization') {
+                            delete headers[header];
+                        }
+                    }
+                }
+                // let's make the request with the new redirectUrl
+                info = this._prepareRequest(verb, parsedRedirectUrl, headers);
+                response = await this.requestRaw(info, data);
+                redirectsRemaining--;
+            }
+            if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
+                // If not a retry code, return immediately instead of retrying
+                return response;
+            }
+            numTries += 1;
+            if (numTries < maxTries) {
+                await response.readBody();
+                await this._performExponentialBackoff(numTries);
+            }
+        }
+        return response;
+    }
+    /**
+     * Needs to be called if keepAlive is set to true in request options.
+     */
+    dispose() {
+        if (this._agent) {
+            this._agent.destroy();
+        }
+        this._disposed = true;
+    }
+    /**
+     * Raw request.
+     * @param info
+     * @param data
+     */
+    requestRaw(info, data) {
+        return new Promise((resolve, reject) => {
+            let callbackForResult = function (err, res) {
+                if (err) {
+                    reject(err);
+                }
+                resolve(res);
+            };
+            this.requestRawWithCallback(info, data, callbackForResult);
+        });
+    }
+    /**
+     * Raw request with callback.
+     * @param info
+     * @param data
+     * @param onResult
+     */
+    requestRawWithCallback(info, data, onResult) {
+        let socket;
+        if (typeof data === 'string') {
+            info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
+        }
+        let callbackCalled = false;
+        let handleResult = (err, res) => {
+            if (!callbackCalled) {
+                callbackCalled = true;
+                onResult(err, res);
+            }
+        };
+        let req = info.httpModule.request(info.options, (msg) => {
+            let res = new HttpClientResponse(msg);
+            handleResult(null, res);
+        });
+        req.on('socket', sock => {
+            socket = sock;
+        });
+        // If we ever get disconnected, we want the socket to timeout eventually
+        req.setTimeout(this._socketTimeout || 3 * 60000, () => {
+            if (socket) {
+                socket.end();
+            }
+            handleResult(new Error('Request timeout: ' + info.options.path), null);
+        });
+        req.on('error', function (err) {
+            // err has statusCode property
+            // res should have headers
+            handleResult(err, null);
+        });
+        if (data && typeof data === 'string') {
+            req.write(data, 'utf8');
+        }
+        if (data && typeof data !== 'string') {
+            data.on('close', function () {
+                req.end();
+            });
+            data.pipe(req);
+        }
+        else {
+            req.end();
+        }
+    }
+    /**
+     * Gets an http agent. This function is useful when you need an http agent that handles
+     * routing through a proxy server - depending upon the url and proxy environment variables.
+     * @param serverUrl  The server URL where the request will be sent. For example, https://api.github.com
+     */
+    getAgent(serverUrl) {
+        let parsedUrl = new URL(serverUrl);
+        return this._getAgent(parsedUrl);
+    }
+    _prepareRequest(method, requestUrl, headers) {
+        const info = {};
+        info.parsedUrl = requestUrl;
+        const usingSsl = info.parsedUrl.protocol === 'https:';
+        info.httpModule = usingSsl ? https : http;
+        const defaultPort = usingSsl ? 443 : 80;
+        info.options = {};
+        info.options.host = info.parsedUrl.hostname;
+        info.options.port = info.parsedUrl.port
+            ? parseInt(info.parsedUrl.port)
+            : defaultPort;
+        info.options.path =
+            (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
+        info.options.method = method;
+        info.options.headers = this._mergeHeaders(headers);
+        if (this.userAgent != null) {
+            info.options.headers['user-agent'] = this.userAgent;
+        }
+        info.options.agent = this._getAgent(info.parsedUrl);
+        // gives handlers an opportunity to participate
+        if (this.handlers) {
+            this.handlers.forEach(handler => {
+                handler.prepareRequest(info.options);
+            });
+        }
+        return info;
+    }
+    _mergeHeaders(headers) {
+        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
+        if (this.requestOptions && this.requestOptions.headers) {
+            return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
+        }
+        return lowercaseKeys(headers || {});
+    }
+    _getExistingOrDefaultHeader(additionalHeaders, header, _default) {
+        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
+        let clientHeader;
+        if (this.requestOptions && this.requestOptions.headers) {
+            clientHeader = lowercaseKeys(this.requestOptions.headers)[header];
+        }
+        return additionalHeaders[header] || clientHeader || _default;
+    }
+    _getAgent(parsedUrl) {
+        let agent;
+        let proxyUrl = pm.getProxyUrl(parsedUrl);
+        let useProxy = proxyUrl && proxyUrl.hostname;
+        if (this._keepAlive && useProxy) {
+            agent = this._proxyAgent;
+        }
+        if (this._keepAlive && !useProxy) {
+            agent = this._agent;
+        }
+        // if agent is already assigned use that agent.
+        if (!!agent) {
+            return agent;
+        }
+        const usingSsl = parsedUrl.protocol === 'https:';
+        let maxSockets = 100;
+        if (!!this.requestOptions) {
+            maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
+        }
+        if (useProxy) {
+            // If using proxy, need tunnel
+            if (!tunnel) {
+                tunnel = __nccwpck_require__(294);
+            }
+            const agentOptions = {
+                maxSockets: maxSockets,
+                keepAlive: this._keepAlive,
+                proxy: {
+                    ...((proxyUrl.username || proxyUrl.password) && {
+                        proxyAuth: `${proxyUrl.username}:${proxyUrl.password}`
+                    }),
+                    host: proxyUrl.hostname,
+                    port: proxyUrl.port
+                }
+            };
+            let tunnelAgent;
+            const overHttps = proxyUrl.protocol === 'https:';
+            if (usingSsl) {
+                tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
+            }
+            else {
+                tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
+            }
+            agent = tunnelAgent(agentOptions);
+            this._proxyAgent = agent;
+        }
+        // if reusing agent across request and tunneling agent isn't assigned create a new agent
+        if (this._keepAlive && !agent) {
+            const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
+            agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
+            this._agent = agent;
+        }
+        // if not using private agent and tunnel agent isn't setup then use global agent
+        if (!agent) {
+            agent = usingSsl ? https.globalAgent : http.globalAgent;
+        }
+        if (usingSsl && this._ignoreSslError) {
+            // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
+            // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
+            // we have to cast it to any and change it directly
+            agent.options = Object.assign(agent.options || {}, {
+                rejectUnauthorized: false
+            });
+        }
+        return agent;
+    }
+    _performExponentialBackoff(retryNumber) {
+        retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
+        const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
+        return new Promise(resolve => setTimeout(() => resolve(), ms));
+    }
+    static dateTimeDeserializer(key, value) {
+        if (typeof value === 'string') {
+            let a = new Date(value);
+            if (!isNaN(a.valueOf())) {
+                return a;
+            }
+        }
+        return value;
+    }
+    async _processResponse(res, options) {
+        return new Promise(async (resolve, reject) => {
+            const statusCode = res.message.statusCode;
+            const response = {
+                statusCode: statusCode,
+                result: null,
+                headers: {}
+            };
+            // not found leads to null obj returned
+            if (statusCode == HttpCodes.NotFound) {
+                resolve(response);
+            }
+            let obj;
+            let contents;
+            // get the result from the body
+            try {
+                contents = await res.readBody();
+                if (contents && contents.length > 0) {
+                    if (options && options.deserializeDates) {
+                        obj = JSON.parse(contents, HttpClient.dateTimeDeserializer);
+                    }
+                    else {
+                        obj = JSON.parse(contents);
+                    }
+                    response.result = obj;
+                }
+                response.headers = res.message.headers;
+            }
+            catch (err) {
+                // Invalid resource (contents not json);  leaving result obj null
+            }
+            // note that 3xx redirects are handled by the http layer.
+            if (statusCode > 299) {
+                let msg;
+                // if exception/error in body, attempt to get better error
+                if (obj && obj.message) {
+                    msg = obj.message;
+                }
+                else if (contents && contents.length > 0) {
+                    // it may be the case that the exception is in the body message as string
+                    msg = contents;
+                }
+                else {
+                    msg = 'Failed request: (' + statusCode + ')';
+                }
+                let err = new HttpClientError(msg, statusCode);
+                err.result = response.result;
+                reject(err);
+            }
+            else {
+                resolve(response);
+            }
+        });
+    }
+}
+exports.HttpClient = HttpClient;
+
+
+/***/ }),
+
+/***/ 443:
+/***/ ((__unused_webpack_module, exports) => {
+
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+function getProxyUrl(reqUrl) {
+    let usingSsl = reqUrl.protocol === 'https:';
+    let proxyUrl;
+    if (checkBypass(reqUrl)) {
+        return proxyUrl;
+    }
+    let proxyVar;
+    if (usingSsl) {
+        proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
+    }
+    else {
+        proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
+    }
+    if (proxyVar) {
+        proxyUrl = new URL(proxyVar);
+    }
+    return proxyUrl;
+}
+exports.getProxyUrl = getProxyUrl;
+function checkBypass(reqUrl) {
+    if (!reqUrl.hostname) {
+        return false;
+    }
+    let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
+    if (!noProxy) {
+        return false;
+    }
+    // Determine the request port
+    let reqPort;
+    if (reqUrl.port) {
+        reqPort = Number(reqUrl.port);
+    }
+    else if (reqUrl.protocol === 'http:') {
+        reqPort = 80;
+    }
+    else if (reqUrl.protocol === 'https:') {
+        reqPort = 443;
+    }
+    // Format the request hostname and hostname with port
+    let upperReqHosts = [reqUrl.hostname.toUpperCase()];
+    if (typeof reqPort === 'number') {
+        upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`);
+    }
+    // Compare request host against noproxy
+    for (let upperNoProxyItem of noProxy
+        .split(',')
+        .map(x => x.trim().toUpperCase())
+        .filter(x => x)) {
+        if (upperReqHosts.some(x => x === upperNoProxyItem)) {
+            return true;
+        }
+    }
+    return false;
+}
+exports.checkBypass = checkBypass;
+
+
+/***/ }),
+
+/***/ 962:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var _a;
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rename = exports.readlink = exports.readdir = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0;
+const fs = __importStar(__nccwpck_require__(147));
+const path = __importStar(__nccwpck_require__(17));
+_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink;
+exports.IS_WINDOWS = process.platform === 'win32';
+function exists(fsPath) {
+    return __awaiter(this, void 0, void 0, function* () {
+        try {
+            yield exports.stat(fsPath);
+        }
+        catch (err) {
+            if (err.code === 'ENOENT') {
+                return false;
+            }
+            throw err;
+        }
+        return true;
+    });
+}
+exports.exists = exists;
+function isDirectory(fsPath, useStat = false) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath);
+        return stats.isDirectory();
+    });
+}
+exports.isDirectory = isDirectory;
+/**
+ * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
+ * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
+ */
+function isRooted(p) {
+    p = normalizeSeparators(p);
+    if (!p) {
+        throw new Error('isRooted() parameter "p" cannot be empty');
+    }
+    if (exports.IS_WINDOWS) {
+        return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello
+        ); // e.g. C: or C:\hello
+    }
+    return p.startsWith('/');
+}
+exports.isRooted = isRooted;
+/**
+ * Best effort attempt to determine whether a file exists and is executable.
+ * @param filePath    file path to check
+ * @param extensions  additional file extensions to try
+ * @return if file exists and is executable, returns the file path. otherwise empty string.
+ */
+function tryGetExecutablePath(filePath, extensions) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let stats = undefined;
+        try {
+            // test file exists
+            stats = yield exports.stat(filePath);
+        }
+        catch (err) {
+            if (err.code !== 'ENOENT') {
+                // eslint-disable-next-line no-console
+                console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
+            }
+        }
+        if (stats && stats.isFile()) {
+            if (exports.IS_WINDOWS) {
+                // on Windows, test for valid extension
+                const upperExt = path.extname(filePath).toUpperCase();
+                if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) {
+                    return filePath;
+                }
+            }
+            else {
+                if (isUnixExecutable(stats)) {
+                    return filePath;
+                }
+            }
+        }
+        // try each extension
+        const originalFilePath = filePath;
+        for (const extension of extensions) {
+            filePath = originalFilePath + extension;
+            stats = undefined;
+            try {
+                stats = yield exports.stat(filePath);
+            }
+            catch (err) {
+                if (err.code !== 'ENOENT') {
+                    // eslint-disable-next-line no-console
+                    console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
+                }
+            }
+            if (stats && stats.isFile()) {
+                if (exports.IS_WINDOWS) {
+                    // preserve the case of the actual file (since an extension was appended)
+                    try {
+                        const directory = path.dirname(filePath);
+                        const upperName = path.basename(filePath).toUpperCase();
+                        for (const actualName of yield exports.readdir(directory)) {
+                            if (upperName === actualName.toUpperCase()) {
+                                filePath = path.join(directory, actualName);
+                                break;
+                            }
+                        }
+                    }
+                    catch (err) {
+                        // eslint-disable-next-line no-console
+                        console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`);
+                    }
+                    return filePath;
+                }
+                else {
+                    if (isUnixExecutable(stats)) {
+                        return filePath;
+                    }
+                }
+            }
+        }
+        return '';
+    });
+}
+exports.tryGetExecutablePath = tryGetExecutablePath;
+function normalizeSeparators(p) {
+    p = p || '';
+    if (exports.IS_WINDOWS) {
+        // convert slashes on Windows
+        p = p.replace(/\//g, '\\');
+        // remove redundant slashes
+        return p.replace(/\\\\+/g, '\\');
+    }
+    // remove redundant slashes
+    return p.replace(/\/\/+/g, '/');
+}
+// on Mac/Linux, test the execute bit
+//     R   W  X  R  W X R W X
+//   256 128 64 32 16 8 4 2 1
+function isUnixExecutable(stats) {
+    return ((stats.mode & 1) > 0 ||
+        ((stats.mode & 8) > 0 && stats.gid === process.getgid()) ||
+        ((stats.mode & 64) > 0 && stats.uid === process.getuid()));
+}
+// Get the path of cmd.exe in windows
+function getCmdPath() {
+    var _a;
+    return (_a = process.env['COMSPEC']) !== null && _a !== void 0 ? _a : `cmd.exe`;
+}
+exports.getCmdPath = getCmdPath;
+//# sourceMappingURL=io-util.js.map
+
+/***/ }),
+
+/***/ 436:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0;
+const assert_1 = __nccwpck_require__(491);
+const childProcess = __importStar(__nccwpck_require__(81));
+const path = __importStar(__nccwpck_require__(17));
+const util_1 = __nccwpck_require__(837);
+const ioUtil = __importStar(__nccwpck_require__(962));
+const exec = util_1.promisify(childProcess.exec);
+const execFile = util_1.promisify(childProcess.execFile);
+/**
+ * Copies a file or folder.
+ * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
+ *
+ * @param     source    source path
+ * @param     dest      destination path
+ * @param     options   optional. See CopyOptions.
+ */
+function cp(source, dest, options = {}) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const { force, recursive, copySourceDirectory } = readCopyOptions(options);
+        const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null;
+        // Dest is an existing file, but not forcing
+        if (destStat && destStat.isFile() && !force) {
+            return;
+        }
+        // If dest is an existing directory, should copy inside.
+        const newDest = destStat && destStat.isDirectory() && copySourceDirectory
+            ? path.join(dest, path.basename(source))
+            : dest;
+        if (!(yield ioUtil.exists(source))) {
+            throw new Error(`no such file or directory: ${source}`);
+        }
+        const sourceStat = yield ioUtil.stat(source);
+        if (sourceStat.isDirectory()) {
+            if (!recursive) {
+                throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`);
+            }
+            else {
+                yield cpDirRecursive(source, newDest, 0, force);
+            }
+        }
+        else {
+            if (path.relative(source, newDest) === '') {
+                // a file cannot be copied to itself
+                throw new Error(`'${newDest}' and '${source}' are the same file`);
+            }
+            yield copyFile(source, newDest, force);
+        }
+    });
+}
+exports.cp = cp;
+/**
+ * Moves a path.
+ *
+ * @param     source    source path
+ * @param     dest      destination path
+ * @param     options   optional. See MoveOptions.
+ */
+function mv(source, dest, options = {}) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (yield ioUtil.exists(dest)) {
+            let destExists = true;
+            if (yield ioUtil.isDirectory(dest)) {
+                // If dest is directory copy src into dest
+                dest = path.join(dest, path.basename(source));
+                destExists = yield ioUtil.exists(dest);
+            }
+            if (destExists) {
+                if (options.force == null || options.force) {
+                    yield rmRF(dest);
+                }
+                else {
+                    throw new Error('Destination already exists');
+                }
+            }
+        }
+        yield mkdirP(path.dirname(dest));
+        yield ioUtil.rename(source, dest);
+    });
+}
+exports.mv = mv;
+/**
+ * Remove a path recursively with force
+ *
+ * @param inputPath path to remove
+ */
+function rmRF(inputPath) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (ioUtil.IS_WINDOWS) {
+            // Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
+            // program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
+            // Check for invalid characters
+            // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+            if (/[*"<>|]/.test(inputPath)) {
+                throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows');
+            }
+            try {
+                const cmdPath = ioUtil.getCmdPath();
+                if (yield ioUtil.isDirectory(inputPath, true)) {
+                    yield exec(`${cmdPath} /s /c "rd /s /q "%inputPath%""`, {
+                        env: { inputPath }
+                    });
+                }
+                else {
+                    yield exec(`${cmdPath} /s /c "del /f /a "%inputPath%""`, {
+                        env: { inputPath }
+                    });
+                }
+            }
+            catch (err) {
+                // if you try to delete a file that doesn't exist, desired result is achieved
+                // other errors are valid
+                if (err.code !== 'ENOENT')
+                    throw err;
+            }
+            // Shelling out fails to remove a symlink folder with missing source, this unlink catches that
+            try {
+                yield ioUtil.unlink(inputPath);
+            }
+            catch (err) {
+                // if you try to delete a file that doesn't exist, desired result is achieved
+                // other errors are valid
+                if (err.code !== 'ENOENT')
+                    throw err;
+            }
+        }
+        else {
+            let isDir = false;
+            try {
+                isDir = yield ioUtil.isDirectory(inputPath);
+            }
+            catch (err) {
+                // if you try to delete a file that doesn't exist, desired result is achieved
+                // other errors are valid
+                if (err.code !== 'ENOENT')
+                    throw err;
+                return;
+            }
+            if (isDir) {
+                yield execFile(`rm`, [`-rf`, `${inputPath}`]);
+            }
+            else {
+                yield ioUtil.unlink(inputPath);
+            }
+        }
+    });
+}
+exports.rmRF = rmRF;
+/**
+ * Make a directory.  Creates the full path with folders in between
+ * Will throw if it fails
+ *
+ * @param   fsPath        path to create
+ * @returns Promise<void>
+ */
+function mkdirP(fsPath) {
+    return __awaiter(this, void 0, void 0, function* () {
+        assert_1.ok(fsPath, 'a path argument must be provided');
+        yield ioUtil.mkdir(fsPath, { recursive: true });
+    });
+}
+exports.mkdirP = mkdirP;
+/**
+ * Returns path of a tool had the tool actually been invoked.  Resolves via paths.
+ * If you check and the tool does not exist, it will throw.
+ *
+ * @param     tool              name of the tool
+ * @param     check             whether to check if tool exists
+ * @returns   Promise<string>   path to tool
+ */
+function which(tool, check) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (!tool) {
+            throw new Error("parameter 'tool' is required");
+        }
+        // recursive when check=true
+        if (check) {
+            const result = yield which(tool, false);
+            if (!result) {
+                if (ioUtil.IS_WINDOWS) {
+                    throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
+                }
+                else {
+                    throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`);
+                }
+            }
+            return result;
+        }
+        const matches = yield findInPath(tool);
+        if (matches && matches.length > 0) {
+            return matches[0];
+        }
+        return '';
+    });
+}
+exports.which = which;
+/**
+ * Returns a list of all occurrences of the given tool on the system path.
+ *
+ * @returns   Promise<string[]>  the paths of the tool
+ */
+function findInPath(tool) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (!tool) {
+            throw new Error("parameter 'tool' is required");
+        }
+        // build the list of extensions to try
+        const extensions = [];
+        if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) {
+            for (const extension of process.env['PATHEXT'].split(path.delimiter)) {
+                if (extension) {
+                    extensions.push(extension);
+                }
+            }
+        }
+        // if it's rooted, return it if exists. otherwise return empty.
+        if (ioUtil.isRooted(tool)) {
+            const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions);
+            if (filePath) {
+                return [filePath];
+            }
+            return [];
+        }
+        // if any path separators, return empty
+        if (tool.includes(path.sep)) {
+            return [];
+        }
+        // build the list of directories
+        //
+        // Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
+        // it feels like we should not do this. Checking the current directory seems like more of a use
+        // case of a shell, and the which() function exposed by the toolkit should strive for consistency
+        // across platforms.
+        const directories = [];
+        if (process.env.PATH) {
+            for (const p of process.env.PATH.split(path.delimiter)) {
+                if (p) {
+                    directories.push(p);
+                }
+            }
+        }
+        // find all matches
+        const matches = [];
+        for (const directory of directories) {
+            const filePath = yield ioUtil.tryGetExecutablePath(path.join(directory, tool), extensions);
+            if (filePath) {
+                matches.push(filePath);
+            }
+        }
+        return matches;
+    });
+}
+exports.findInPath = findInPath;
+function readCopyOptions(options) {
+    const force = options.force == null ? true : options.force;
+    const recursive = Boolean(options.recursive);
+    const copySourceDirectory = options.copySourceDirectory == null
+        ? true
+        : Boolean(options.copySourceDirectory);
+    return { force, recursive, copySourceDirectory };
+}
+function cpDirRecursive(sourceDir, destDir, currentDepth, force) {
+    return __awaiter(this, void 0, void 0, function* () {
+        // Ensure there is not a run away recursive copy
+        if (currentDepth >= 255)
+            return;
+        currentDepth++;
+        yield mkdirP(destDir);
+        const files = yield ioUtil.readdir(sourceDir);
+        for (const fileName of files) {
+            const srcFile = `${sourceDir}/${fileName}`;
+            const destFile = `${destDir}/${fileName}`;
+            const srcFileStat = yield ioUtil.lstat(srcFile);
+            if (srcFileStat.isDirectory()) {
+                // Recurse
+                yield cpDirRecursive(srcFile, destFile, currentDepth, force);
+            }
+            else {
+                yield copyFile(srcFile, destFile, force);
+            }
+        }
+        // Change the mode for the newly created directory
+        yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode);
+    });
+}
+// Buffered file copy
+function copyFile(srcFile, destFile, force) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) {
+            // unlink/re-link it
+            try {
+                yield ioUtil.lstat(destFile);
+                yield ioUtil.unlink(destFile);
+            }
+            catch (e) {
+                // Try to override file permission
+                if (e.code === 'EPERM') {
+                    yield ioUtil.chmod(destFile, '0666');
+                    yield ioUtil.unlink(destFile);
+                }
+                // other errors = it doesn't exist, no work to do
+            }
+            // Copy over symlink
+            const symlinkFull = yield ioUtil.readlink(srcFile);
+            yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null);
+        }
+        else if (!(yield ioUtil.exists(destFile)) || force) {
+            yield ioUtil.copyFile(srcFile, destFile);
+        }
+    });
+}
+//# sourceMappingURL=io.js.map
+
+/***/ }),
+
+/***/ 294:
+/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
+
+module.exports = __nccwpck_require__(219);
+
+
+/***/ }),
+
+/***/ 219:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+
+
+var net = __nccwpck_require__(808);
+var tls = __nccwpck_require__(404);
+var http = __nccwpck_require__(685);
+var https = __nccwpck_require__(687);
+var events = __nccwpck_require__(361);
+var assert = __nccwpck_require__(491);
+var util = __nccwpck_require__(837);
+
+
+exports.httpOverHttp = httpOverHttp;
+exports.httpsOverHttp = httpsOverHttp;
+exports.httpOverHttps = httpOverHttps;
+exports.httpsOverHttps = httpsOverHttps;
+
+
+function httpOverHttp(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = http.request;
+  return agent;
+}
+
+function httpsOverHttp(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = http.request;
+  agent.createSocket = createSecureSocket;
+  agent.defaultPort = 443;
+  return agent;
+}
+
+function httpOverHttps(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = https.request;
+  return agent;
+}
+
+function httpsOverHttps(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = https.request;
+  agent.createSocket = createSecureSocket;
+  agent.defaultPort = 443;
+  return agent;
+}
+
+
+function TunnelingAgent(options) {
+  var self = this;
+  self.options = options || {};
+  self.proxyOptions = self.options.proxy || {};
+  self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets;
+  self.requests = [];
+  self.sockets = [];
+
+  self.on('free', function onFree(socket, host, port, localAddress) {
+    var options = toOptions(host, port, localAddress);
+    for (var i = 0, len = self.requests.length; i < len; ++i) {
+      var pending = self.requests[i];
+      if (pending.host === options.host && pending.port === options.port) {
+        // Detect the request to connect same origin server,
+        // reuse the connection.
+        self.requests.splice(i, 1);
+        pending.request.onSocket(socket);
+        return;
+      }
+    }
+    socket.destroy();
+    self.removeSocket(socket);
+  });
+}
+util.inherits(TunnelingAgent, events.EventEmitter);
+
+TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) {
+  var self = this;
+  var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress));
+
+  if (self.sockets.length >= this.maxSockets) {
+    // We are over limit so we'll add it to the queue.
+    self.requests.push(options);
+    return;
+  }
+
+  // If we are under maxSockets create a new one.
+  self.createSocket(options, function(socket) {
+    socket.on('free', onFree);
+    socket.on('close', onCloseOrRemove);
+    socket.on('agentRemove', onCloseOrRemove);
+    req.onSocket(socket);
+
+    function onFree() {
+      self.emit('free', socket, options);
+    }
+
+    function onCloseOrRemove(err) {
+      self.removeSocket(socket);
+      socket.removeListener('free', onFree);
+      socket.removeListener('close', onCloseOrRemove);
+      socket.removeListener('agentRemove', onCloseOrRemove);
+    }
+  });
+};
+
+TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
+  var self = this;
+  var placeholder = {};
+  self.sockets.push(placeholder);
+
+  var connectOptions = mergeOptions({}, self.proxyOptions, {
+    method: 'CONNECT',
+    path: options.host + ':' + options.port,
+    agent: false,
+    headers: {
+      host: options.host + ':' + options.port
+    }
+  });
+  if (options.localAddress) {
+    connectOptions.localAddress = options.localAddress;
+  }
+  if (connectOptions.proxyAuth) {
+    connectOptions.headers = connectOptions.headers || {};
+    connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
+        new Buffer(connectOptions.proxyAuth).toString('base64');
+  }
+
+  debug('making CONNECT request');
+  var connectReq = self.request(connectOptions);
+  connectReq.useChunkedEncodingByDefault = false; // for v0.6
+  connectReq.once('response', onResponse); // for v0.6
+  connectReq.once('upgrade', onUpgrade);   // for v0.6
+  connectReq.once('connect', onConnect);   // for v0.7 or later
+  connectReq.once('error', onError);
+  connectReq.end();
+
+  function onResponse(res) {
+    // Very hacky. This is necessary to avoid http-parser leaks.
+    res.upgrade = true;
+  }
+
+  function onUpgrade(res, socket, head) {
+    // Hacky.
+    process.nextTick(function() {
+      onConnect(res, socket, head);
+    });
+  }
+
+  function onConnect(res, socket, head) {
+    connectReq.removeAllListeners();
+    socket.removeAllListeners();
+
+    if (res.statusCode !== 200) {
+      debug('tunneling socket could not be established, statusCode=%d',
+        res.statusCode);
+      socket.destroy();
+      var error = new Error('tunneling socket could not be established, ' +
+        'statusCode=' + res.statusCode);
+      error.code = 'ECONNRESET';
+      options.request.emit('error', error);
+      self.removeSocket(placeholder);
+      return;
+    }
+    if (head.length > 0) {
+      debug('got illegal response body from proxy');
+      socket.destroy();
+      var error = new Error('got illegal response body from proxy');
+      error.code = 'ECONNRESET';
+      options.request.emit('error', error);
+      self.removeSocket(placeholder);
+      return;
+    }
+    debug('tunneling connection has established');
+    self.sockets[self.sockets.indexOf(placeholder)] = socket;
+    return cb(socket);
+  }
+
+  function onError(cause) {
+    connectReq.removeAllListeners();
+
+    debug('tunneling socket could not be established, cause=%s\n',
+          cause.message, cause.stack);
+    var error = new Error('tunneling socket could not be established, ' +
+                          'cause=' + cause.message);
+    error.code = 'ECONNRESET';
+    options.request.emit('error', error);
+    self.removeSocket(placeholder);
+  }
+};
+
+TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
+  var pos = this.sockets.indexOf(socket)
+  if (pos === -1) {
+    return;
+  }
+  this.sockets.splice(pos, 1);
+
+  var pending = this.requests.shift();
+  if (pending) {
+    // If we have pending requests and a socket gets closed a new one
+    // needs to be created to take over in the pool for the one that closed.
+    this.createSocket(pending, function(socket) {
+      pending.request.onSocket(socket);
+    });
+  }
+};
+
+function createSecureSocket(options, cb) {
+  var self = this;
+  TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
+    var hostHeader = options.request.getHeader('host');
+    var tlsOptions = mergeOptions({}, self.options, {
+      socket: socket,
+      servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host
+    });
+
+    // 0 is dummy port for v0.6
+    var secureSocket = tls.connect(0, tlsOptions);
+    self.sockets[self.sockets.indexOf(socket)] = secureSocket;
+    cb(secureSocket);
+  });
+}
+
+
+function toOptions(host, port, localAddress) {
+  if (typeof host === 'string') { // since v0.10
+    return {
+      host: host,
+      port: port,
+      localAddress: localAddress
+    };
+  }
+  return host; // for v0.11 or later
+}
+
+function mergeOptions(target) {
+  for (var i = 1, len = arguments.length; i < len; ++i) {
+    var overrides = arguments[i];
+    if (typeof overrides === 'object') {
+      var keys = Object.keys(overrides);
+      for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
+        var k = keys[j];
+        if (overrides[k] !== undefined) {
+          target[k] = overrides[k];
+        }
+      }
+    }
+  }
+  return target;
+}
+
+
+var debug;
+if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
+  debug = function() {
+    var args = Array.prototype.slice.call(arguments);
+    if (typeof args[0] === 'string') {
+      args[0] = 'TUNNEL: ' + args[0];
+    } else {
+      args.unshift('TUNNEL:');
+    }
+    console.error.apply(console, args);
+  }
+} else {
+  debug = function() {};
+}
+exports.debug = debug; // for test
+
+
+/***/ }),
+
+/***/ 491:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert");
+
+/***/ }),
+
+/***/ 81:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("child_process");
+
+/***/ }),
+
+/***/ 361:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events");
+
+/***/ }),
+
+/***/ 147:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs");
+
+/***/ }),
+
+/***/ 685:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http");
+
+/***/ }),
+
+/***/ 687:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https");
+
+/***/ }),
+
+/***/ 808:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net");
+
+/***/ }),
+
+/***/ 37:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os");
+
+/***/ }),
+
+/***/ 17:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path");
+
+/***/ }),
+
+/***/ 576:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("string_decoder");
+
+/***/ }),
+
+/***/ 512:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("timers");
+
+/***/ }),
+
+/***/ 404:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls");
+
+/***/ }),
+
+/***/ 837:
+/***/ ((module) => {
+
+module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util");
+
+/***/ })
+
+/******/ });
+/************************************************************************/
+/******/ // The module cache
+/******/ var __webpack_module_cache__ = {};
+/******/ 
+/******/ // The require function
+/******/ function __nccwpck_require__(moduleId) {
+/******/ 	// Check if module is in cache
+/******/ 	var cachedModule = __webpack_module_cache__[moduleId];
+/******/ 	if (cachedModule !== undefined) {
+/******/ 		return cachedModule.exports;
+/******/ 	}
+/******/ 	// Create a new module (and put it into the cache)
+/******/ 	var module = __webpack_module_cache__[moduleId] = {
+/******/ 		// no module.id needed
+/******/ 		// no module.loaded needed
+/******/ 		exports: {}
+/******/ 	};
+/******/ 
+/******/ 	// Execute the module function
+/******/ 	var threw = true;
+/******/ 	try {
+/******/ 		__webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__);
+/******/ 		threw = false;
+/******/ 	} finally {
+/******/ 		if(threw) delete __webpack_module_cache__[moduleId];
+/******/ 	}
+/******/ 
+/******/ 	// Return the exports of the module
+/******/ 	return module.exports;
+/******/ }
+/******/ 
+/************************************************************************/
+/******/ /* webpack/runtime/compat get default export */
+/******/ (() => {
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__nccwpck_require__.n = (module) => {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			() => (module['default']) :
+/******/ 			() => (module);
+/******/ 		__nccwpck_require__.d(getter, { a: getter });
+/******/ 		return getter;
+/******/ 	};
+/******/ })();
+/******/ 
+/******/ /* webpack/runtime/define property getters */
+/******/ (() => {
+/******/ 	// define getter functions for harmony exports
+/******/ 	__nccwpck_require__.d = (exports, definition) => {
+/******/ 		for(var key in definition) {
+/******/ 			if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
+/******/ 				Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
+/******/ 			}
+/******/ 		}
+/******/ 	};
+/******/ })();
+/******/ 
+/******/ /* webpack/runtime/hasOwnProperty shorthand */
+/******/ (() => {
+/******/ 	__nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
+/******/ })();
+/******/ 
+/******/ /* webpack/runtime/compat */
+/******/ 
+/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/";
+/******/ 
+/************************************************************************/
+var __webpack_exports__ = {};
+// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
+(() => {
+/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(186);
+/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__nccwpck_require__.n(_actions_core__WEBPACK_IMPORTED_MODULE_0__);
+/* harmony import */ var _actions_exec__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(514);
+/* harmony import */ var _actions_exec__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__nccwpck_require__.n(_actions_exec__WEBPACK_IMPORTED_MODULE_1__);
+var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+
+
+function run() {
+    return __awaiter(this, void 0, void 0, function* () {
+        const port = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('port');
+        let runArguments = [
+            "run", "-d", "-p", `${port}:12321`,
+            "--name", "cache_proxy",
+            "--env", "ACTIONS_CACHE_URL",
+            "--env", "ACTIONS_RUNTIME_URL",
+            "--env", "ACTIONS_RUNTIME_TOKEN",
+            "ghcr.io/cirruslabs/actions-http-cache-proxy:latest"
+        ];
+        try {
+            yield (0,_actions_exec__WEBPACK_IMPORTED_MODULE_1__.exec)(`"docker"`, runArguments);
+        }
+        catch (e) {
+            _actions_core__WEBPACK_IMPORTED_MODULE_0__.error(e);
+        }
+    });
+}
+run();
+
+})();
+
diff --git a/http-cache-action/dist/package.json b/http-cache-action/dist/package.json
new file mode 100644
index 0000000..3dbc1ca
--- /dev/null
+++ b/http-cache-action/dist/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "module"
+}
diff --git a/http-cache-action/package-lock.json b/http-cache-action/package-lock.json
new file mode 100644
index 0000000..c99d0d7
--- /dev/null
+++ b/http-cache-action/package-lock.json
@@ -0,0 +1,1809 @@
+{
+  "name": "http-cache-service",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@actions/core": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
+      "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
+      "requires": {
+        "@actions/http-client": "^1.0.11"
+      }
+    },
+    "@actions/exec": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
+      "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
+      "requires": {
+        "@actions/io": "^1.0.1"
+      }
+    },
+    "@actions/glob": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz",
+      "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==",
+      "requires": {
+        "@actions/core": "^1.2.6",
+        "minimatch": "^3.0.4"
+      }
+    },
+    "@actions/http-client": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
+      "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
+      "requires": {
+        "tunnel": "0.0.6"
+      }
+    },
+    "@actions/io": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
+      "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
+    },
+    "@azure/abort-controller": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz",
+      "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==",
+      "requires": {
+        "tslib": "^2.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/core-asynciterator-polyfill": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz",
+      "integrity": "sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw=="
+    },
+    "@azure/core-auth": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz",
+      "integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==",
+      "requires": {
+        "@azure/abort-controller": "^1.0.0",
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/core-http": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.4.tgz",
+      "integrity": "sha512-QmmJmexXKtPyc3/rsZR/YTLDvMatzbzAypJmLzvlfxgz/SkgnqV/D4f6F2LsK6tBj1qhyp8BoXiOebiej0zz3A==",
+      "requires": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-asynciterator-polyfill": "^1.0.0",
+        "@azure/core-auth": "^1.3.0",
+        "@azure/core-tracing": "1.0.0-preview.13",
+        "@azure/logger": "^1.0.0",
+        "@types/node-fetch": "^2.5.0",
+        "@types/tunnel": "^0.0.3",
+        "form-data": "^4.0.0",
+        "node-fetch": "^2.6.7",
+        "process": "^0.11.10",
+        "tough-cookie": "^4.0.0",
+        "tslib": "^2.2.0",
+        "tunnel": "^0.0.6",
+        "uuid": "^8.3.0",
+        "xml2js": "^0.4.19"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+          "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        },
+        "tough-cookie": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+          "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+          "requires": {
+            "psl": "^1.1.33",
+            "punycode": "^2.1.1",
+            "universalify": "^0.1.2"
+          }
+        },
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/core-lro": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.2.4.tgz",
+      "integrity": "sha512-e1I2v2CZM0mQo8+RSix0x091Av493e4bnT22ds2fcQGslTHzM2oTbswkB65nP4iEpCxBrFxOSDPKExmTmjCVtQ==",
+      "requires": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-tracing": "1.0.0-preview.13",
+        "@azure/logger": "^1.0.0",
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/core-paging": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.2.1.tgz",
+      "integrity": "sha512-UtH5iMlYsvg+nQYIl4UHlvvSrsBjOlRF4fs0j7mxd3rWdAStrKYrh2durOpHs5C9yZbVhsVDaisoyaf/lL1EVA==",
+      "requires": {
+        "@azure/core-asynciterator-polyfill": "^1.0.0",
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/core-tracing": {
+      "version": "1.0.0-preview.13",
+      "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
+      "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==",
+      "requires": {
+        "@opentelemetry/api": "^1.0.1",
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/logger": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
+      "integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
+      "requires": {
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@azure/ms-rest-js": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.6.1.tgz",
+      "integrity": "sha512-LLi4jRe/qy5IM8U2CkoDgSZp2OH+MgDe2wePmhz8uY84Svc53EhHaamVyoU6BjjHBxvCRh1vcD1urJDccrxqIw==",
+      "requires": {
+        "@azure/core-auth": "^1.1.4",
+        "abort-controller": "^3.0.0",
+        "form-data": "^2.5.0",
+        "node-fetch": "^2.6.7",
+        "tough-cookie": "^3.0.1",
+        "tslib": "^1.10.0",
+        "tunnel": "0.0.6",
+        "uuid": "^8.3.2",
+        "xml2js": "^0.4.19"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "2.5.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+          "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.6",
+            "mime-types": "^2.1.12"
+          }
+        },
+        "tough-cookie": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+          "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+          "requires": {
+            "ip-regex": "^2.1.0",
+            "psl": "^1.1.28",
+            "punycode": "^2.1.1"
+          }
+        }
+      }
+    },
+    "@azure/storage-blob": {
+      "version": "12.9.0",
+      "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.9.0.tgz",
+      "integrity": "sha512-ank38FdCLfJ+EoeMzCz3hkYJuZAd63ARvDKkxZYRDb+beBYf+/+gx8jNTqkq/hfyUl4dJQ/a7tECU0Y0F98CHg==",
+      "requires": {
+        "@azure/abort-controller": "^1.0.0",
+        "@azure/core-http": "^2.0.0",
+        "@azure/core-lro": "^2.2.0",
+        "@azure/core-paging": "^1.1.1",
+        "@azure/core-tracing": "1.0.0-preview.13",
+        "@azure/logger": "^1.0.0",
+        "events": "^3.0.0",
+        "tslib": "^2.2.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
+    "@opentelemetry/api": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.1.0.tgz",
+      "integrity": "sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ=="
+    },
+    "@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+    },
+    "@szmarczak/http-timer": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+      "requires": {
+        "defer-to-connect": "^2.0.0"
+      }
+    },
+    "@types/cacheable-request": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
+      "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
+      "requires": {
+        "@types/http-cache-semantics": "*",
+        "@types/keyv": "*",
+        "@types/node": "*",
+        "@types/responselike": "*"
+      }
+    },
+    "@types/caseless": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
+      "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
+    },
+    "@types/chai": {
+      "version": "4.2.12",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz",
+      "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==",
+      "dev": true
+    },
+    "@types/http-cache-semantics": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
+      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+    },
+    "@types/keyv": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+      "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/mocha": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
+      "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "14.18.12",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz",
+      "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A=="
+    },
+    "@types/node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+      "requires": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+          "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
+    "@types/request": {
+      "version": "2.48.8",
+      "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz",
+      "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==",
+      "requires": {
+        "@types/caseless": "*",
+        "@types/node": "*",
+        "@types/tough-cookie": "*",
+        "form-data": "^2.5.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "2.5.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+          "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.6",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
+    "@types/responselike": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
+      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/semver": {
+      "version": "7.3.9",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
+      "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ=="
+    },
+    "@types/tough-cookie": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+      "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
+    },
+    "@types/tunnel": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
+      "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/uuid": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
+      "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
+    },
+    "@ungap/promise-all-settled": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+      "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+      "dev": true
+    },
+    "@vercel/ncc": {
+      "version": "0.33.3",
+      "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.33.3.tgz",
+      "integrity": "sha512-JGZ11QV+/ZcfudW2Cz2JVp54/pJNXbsuWRgSh2ZmmZdQBKXqBtIGrwI1Wyx8nlbzAiEFe7FHi4K1zX4//jxTnQ==",
+      "dev": true
+    },
+    "abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "requires": {
+        "event-target-shim": "^5.0.0"
+      }
+    },
+    "ajv": {
+      "version": "6.12.4",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
+      "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "assertion-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+      "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+      "dev": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
+      "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "cacheable-lookup": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
+    },
+    "cacheable-request": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
+      "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
+      "requires": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^4.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^6.0.1",
+        "responselike": "^2.0.0"
+      }
+    },
+    "camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "chai": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+      "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+      "dev": true,
+      "requires": {
+        "assertion-error": "^1.1.0",
+        "check-error": "^1.0.2",
+        "deep-eql": "^3.0.1",
+        "get-func-name": "^2.0.0",
+        "pathval": "^1.1.0",
+        "type-detect": "^4.0.5"
+      }
+    },
+    "chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "check-error": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      }
+    },
+    "cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "clone-response": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
+      "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+      "requires": {
+        "mimic-response": "^1.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "debug": {
+      "version": "4.3.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+      "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        }
+      }
+    },
+    "decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "dev": true
+    },
+    "decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "requires": {
+        "mimic-response": "^3.1.0"
+      },
+      "dependencies": {
+        "mimic-response": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+          "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+        }
+      }
+    },
+    "deep-eql": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+      "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+      "dev": true,
+      "requires": {
+        "type-detect": "^4.0.0"
+      }
+    },
+    "defer-to-connect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true
+    },
+    "escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true
+    },
+    "event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
+    },
+    "events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "get-func-name": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "dev": true
+    },
+    "get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "got": {
+      "version": "11.8.3",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
+      "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
+      "requires": {
+        "@sindresorhus/is": "^4.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.2",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.5.2",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
+      }
+    },
+    "growl": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+      "dev": true
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
+    },
+    "har-validator": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.3",
+        "har-schema": "^2.0.0"
+      }
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "http-cache-semantics": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "http2-wrapper": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+      "requires": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.0.0"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "ip-regex": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
+    },
+    "json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+    },
+    "json-schema": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+      "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
+    },
+    "jsprim": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+      "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+      "dev": true,
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.4.0",
+        "verror": "1.10.0"
+      }
+    },
+    "keyv": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
+      "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==",
+      "requires": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^5.0.0"
+      }
+    },
+    "log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      }
+    },
+    "lowercase-keys": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "mime-db": {
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+      "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+    },
+    "mime-types": {
+      "version": "2.1.27",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+      "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+      "requires": {
+        "mime-db": "1.44.0"
+      }
+    },
+    "mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "mocha": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz",
+      "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==",
+      "dev": true,
+      "requires": {
+        "@ungap/promise-all-settled": "1.1.2",
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.5.3",
+        "debug": "4.3.3",
+        "diff": "5.0.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "7.2.0",
+        "growl": "1.10.5",
+        "he": "1.2.0",
+        "js-yaml": "4.1.0",
+        "log-symbols": "4.1.0",
+        "minimatch": "4.2.1",
+        "ms": "2.1.3",
+        "nanoid": "3.3.1",
+        "serialize-javascript": "6.0.0",
+        "strip-json-comments": "3.1.1",
+        "supports-color": "8.1.1",
+        "which": "2.0.2",
+        "workerpool": "6.2.0",
+        "yargs": "16.2.0",
+        "yargs-parser": "20.2.4",
+        "yargs-unparser": "2.0.0"
+      },
+      "dependencies": {
+        "diff": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+          "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz",
+          "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true
+    },
+    "nanoid": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
+      "dev": true
+    },
+    "node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "requires": {
+        "whatwg-url": "^5.0.0"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "p-cancelable": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="
+    },
+    "p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "requires": {
+        "yocto-queue": "^0.1.0"
+      }
+    },
+    "p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^3.0.2"
+      }
+    },
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "pathval": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+      "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+      "dev": true
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+    },
+    "psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
+    "qs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "dev": true
+    },
+    "quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "request": {
+      "version": "2.88.2",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "dev": true,
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.3",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.5.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+          "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+          "dev": true
+        }
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "resolve-alpn": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+    },
+    "responselike": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
+      "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
+      "requires": {
+        "lowercase-keys": "^2.0.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
+    "semver": {
+      "version": "7.3.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
+    },
+    "serialize-javascript": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^4.0.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "tough-cookie": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "dev": true,
+      "requires": {
+        "psl": "^1.1.28",
+        "punycode": "^2.1.1"
+      }
+    },
+    "tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+    },
+    "ts-node": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+      "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+      "dev": true,
+      "requires": {
+        "arg": "^4.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.17",
+        "yn": "3.1.1"
+      }
+    },
+    "tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
+    "tunnel": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
+    },
+    "type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true
+    },
+    "typescript": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+      "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg=="
+    },
+    "universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+    },
+    "uri-js": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+      "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+    },
+    "whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "requires": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "workerpool": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz",
+      "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "xml2js": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+      "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+      "requires": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      }
+    },
+    "xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    },
+    "yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      }
+    },
+    "yargs-parser": {
+      "version": "20.2.4",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+      "dev": true
+    },
+    "yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^6.0.0",
+        "decamelize": "^4.0.0",
+        "flat": "^5.0.2",
+        "is-plain-obj": "^2.1.0"
+      }
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true
+    }
+  }
+}
diff --git a/http-cache-action/package.json b/http-cache-action/package.json
new file mode 100644
index 0000000..494f60e
--- /dev/null
+++ b/http-cache-action/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "http-cache-service",
+  "version": "1.0.0",
+  "description": "HTTP Caching service for GitHub Actions",
+  "type": "module",
+  "scripts": {
+    "build": "ncc build src/action.ts",
+    "test": "mocha --timeout 30000 -r ts-node/register src/**.spec.ts"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/cirruslabs/http-cache-service.git"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/cirruslabs/http-cache-service/issues"
+  },
+  "homepage": "https://github.com/cirruslabs/http-cache-service#readme",
+  "dependencies": {
+    "@actions/core": "^1.2.5",
+    "@actions/exec": "^1.0.4",
+    "@actions/glob": "^0.1.0",
+    "@actions/http-client": "^1.0.8",
+    "@actions/io": "^1.0.2",
+    "@azure/ms-rest-js": "^2.0.8",
+    "@azure/storage-blob": "^12.2.0-preview.1",
+    "@types/node": "^14.6.2",
+    "@types/request": "^2.48.5",
+    "@types/semver": "^7.3.3",
+    "@types/uuid": "^8.3.0",
+    "got": "^11.5.2",
+    "semver": "^7.3.2",
+    "typescript": "^4.0.2",
+    "uuid": "^8.3.0"
+  },
+  "devDependencies": {
+    "@types/chai": "^4.2.12",
+    "@types/mocha": "^8.0.3",
+    "@vercel/ncc": "^0.33.3",
+    "chai": "^4.2.0",
+    "mocha": "^9.2.2",
+    "request": "^2.88.2",
+    "ts-node": "^9.0.0"
+  }
+}
diff --git a/http-cache-action/proxy/Dockerfile b/http-cache-action/proxy/Dockerfile
new file mode 100644
index 0000000..481c578
--- /dev/null
+++ b/http-cache-action/proxy/Dockerfile
@@ -0,0 +1,17 @@
+FROM golang:latest as builder
+
+WORKDIR /tmp/build
+ADD . /tmp/build/
+
+RUN go get ./... && \
+    CGO_ENABLED=0 GOOS=linux go build -o proxy proxy.go
+
+FROM alpine:latest
+
+LABEL org.opencontainers.image.source = "https://github.com/cirruslabs/http-cache-action/"
+EXPOSE 12321
+
+RUN apk --no-cache add ca-certificates
+COPY --from=builder /tmp/build/proxy /app/
+WORKDIR /app
+CMD ["./proxy"]
\ No newline at end of file
diff --git a/http-cache-action/proxy/go.mod b/http-cache-action/proxy/go.mod
new file mode 100644
index 0000000..0e736b5
--- /dev/null
+++ b/http-cache-action/proxy/go.mod
@@ -0,0 +1,5 @@
+module github.com/cirruslabs/http-cache-action/proxy
+
+go 1.15
+
+require github.com/dimchansky/utfbom v1.1.0
diff --git a/http-cache-action/proxy/go.sum b/http-cache-action/proxy/go.sum
new file mode 100644
index 0000000..b302f53
--- /dev/null
+++ b/http-cache-action/proxy/go.sum
@@ -0,0 +1,2 @@
+github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
+github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
diff --git a/http-cache-action/proxy/proxy.go b/http-cache-action/proxy/proxy.go
new file mode 100644
index 0000000..7ae2eaa
--- /dev/null
+++ b/http-cache-action/proxy/proxy.go
@@ -0,0 +1,266 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/sha256"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"github.com/dimchansky/utfbom"
+	"io"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+)
+
+var token = os.Getenv("ACTIONS_RUNTIME_TOKEN")
+var httpClient = &http.Client{}
+
+type GetCacheResponse struct {
+	ArchiveLocation string `json:"archiveLocation"`
+}
+
+type ReserveCacheResponse struct {
+	CacheId int `json:"cacheId"`
+}
+
+func main() {
+	http.HandleFunc("/", handler)
+
+	log.Println("Starting http cache server on port 12321")
+	err := http.ListenAndServe(":12321", nil)
+	if err != nil {
+		log.Printf("Failed to start server: %v\n", err)
+	}
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	startTime := time.Now()
+	key := r.URL.Path
+	if key[0] == '/' {
+		key = key[1:]
+	}
+	if key == "" {
+		_, _ = w.Write([]byte("HTTP Cache is running!"))
+		w.WriteHeader(200)
+	} else if r.Method == "GET" {
+		downloadCache(w, r, key)
+	} else if r.Method == "HEAD" {
+		checkCacheExists(w, key)
+	} else if r.Method == "POST" {
+		uploadCache(w, r, key)
+	} else if r.Method == "PUT" {
+		uploadCache(w, r, key)
+	}
+	duration := time.Since(startTime)
+	log.Printf("Served %s request for %s key in %dms\n", r.Method, key, duration.Milliseconds())
+}
+
+func downloadCache(w http.ResponseWriter, r *http.Request, key string) {
+	location, err := findCacheLocation(key)
+	if err != nil {
+		log.Printf("Failed to download key %s: %v\n", key, err)
+		w.Write([]byte(err.Error()))
+		w.WriteHeader(500)
+		return
+	}
+	if location == "" {
+		log.Printf("Cache %s not found\n", key)
+		w.WriteHeader(404)
+		return
+	}
+	proxyDownloadFromURL(w, location)
+}
+
+func proxyDownloadFromURL(w http.ResponseWriter, url string) {
+	resp, err := http.Get(url)
+	if err != nil {
+		log.Printf("Proxying cache %s failed: %v\n", url, err)
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	successfulStatus := 100 <= resp.StatusCode && resp.StatusCode < 300
+	if !successfulStatus {
+		log.Printf("Proxying cache %s failed with %d status\n", url, resp.StatusCode)
+		w.WriteHeader(resp.StatusCode)
+		return
+	}
+	_, err = io.Copy(w, resp.Body)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+	}
+	w.WriteHeader(http.StatusOK)
+}
+
+func checkCacheExists(w http.ResponseWriter, key string) {
+	location, err := findCacheLocation(key)
+	if location == "" || err != nil {
+		log.Printf("Cache %s not found\n", key)
+		w.WriteHeader(404)
+		return
+	}
+	w.WriteHeader(200)
+}
+
+func findCacheLocation(key string) (string, error) {
+	resource := fmt.Sprintf("cache?keys=%s&version=%s", key, calculateSHA256(key))
+	requestUrl := getCacheApiUrl(resource)
+	request, _ := http.NewRequest("GET", requestUrl, nil)
+	request.Header.Set("Authorization", "Bearer "+token)
+	request.Header.Set("User-Agent", "actions/cache")
+	request.Header.Set("Accept", "application/json;api-version=6.0-preview.1")
+	request.Header.Set("Accept-Charset", "utf-8")
+
+	response, err := httpClient.Do(request)
+	if err != nil {
+		return "", err
+	}
+	if response.StatusCode == 404 {
+		return "", nil
+	}
+	if response.StatusCode == 204 {
+		// no content
+		return "", nil
+	}
+	defer response.Body.Close()
+	bodyBytes, err := ioutil.ReadAll(utfbom.SkipOnly(response.Body))
+	if response.StatusCode >= 400 {
+		log.Printf("Failed to download key %s: %d %s\n", key, response.StatusCode, string(bodyBytes))
+		return "", fmt.Errorf("failed to get location: %d", response.StatusCode)
+	}
+
+	cacheResponse := GetCacheResponse{}
+	err = json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&cacheResponse)
+	if err != nil {
+		log.Println(string(bodyBytes))
+		return "", err
+	}
+	if cacheResponse.ArchiveLocation == "" {
+		log.Println(string(bodyBytes))
+	}
+	return cacheResponse.ArchiveLocation, nil
+}
+
+func uploadCache(w http.ResponseWriter, r *http.Request, key string) {
+	cacheId, err := reserveCache(key)
+	if err != nil {
+		log.Printf("Failed to reserve upload for cache key %s: %v\n", key, err)
+		w.Write([]byte(err.Error()))
+		w.WriteHeader(500)
+		return
+	}
+	err = uploadCacheFromReader(cacheId, r.Body)
+	if err != nil {
+		log.Printf("Failed to upload cache %s: %v\n", key, err)
+		w.Write([]byte(err.Error()))
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+	w.WriteHeader(http.StatusCreated)
+}
+
+func uploadCacheFromReader(cacheId int, body io.Reader) error {
+	resourceUrl := getCacheApiUrl(fmt.Sprintf("caches/%d", cacheId))
+	readBufferSize := int(1024 * 1024)
+	readBuffer := make([]byte, readBufferSize)
+	bufferedBodyReader := bufio.NewReaderSize(body, readBufferSize)
+	bytesUploaded := 0
+	for {
+		n, err := bufferedBodyReader.Read(readBuffer)
+
+		if n > 0 {
+			uploadCacheChunk(resourceUrl, readBuffer[:n], bytesUploaded)
+			bytesUploaded += n
+		}
+
+		if err == io.EOF || n == 0 {
+			break
+		}
+		if err != nil {
+			return err
+		}
+	}
+	return commitCache(cacheId, bytesUploaded)
+}
+
+func uploadCacheChunk(url string, data []byte, position int) error {
+	request, _ := http.NewRequest("PATCH", url, bytes.NewBuffer(data))
+	request.Header.Set("Authorization", "Bearer "+token)
+	request.Header.Set("User-Agent", "actions/cache")
+	request.Header.Set("Content-Type", "application/octet-stream")
+	request.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/*", position, position+len(data)-1))
+	request.Header.Set("Accept", "application/json;api-version=6.0-preview.1")
+	request.Header.Set("Accept-Charset", "utf-8")
+
+	response, _ := httpClient.Do(request)
+	if response.StatusCode != 204 {
+		defer response.Body.Close()
+		bodyBytes, _ := ioutil.ReadAll(response.Body)
+		log.Printf("Failed to upload cache chunk: %s\n", string(bodyBytes))
+		log.Println(string(bodyBytes))
+		return fmt.Errorf("failed to upload chunk with status %d: %s", response.StatusCode, string(bodyBytes))
+	}
+	return nil
+}
+
+func commitCache(cacheId int, size int) error {
+	url := getCacheApiUrl(fmt.Sprintf("caches/%d", cacheId))
+	requestBody := fmt.Sprintf("{ \"size\": \"%d\" }", size)
+	request, _ := http.NewRequest("POST", url, bytes.NewBufferString(requestBody))
+	request.Header.Set("Authorization", "Bearer "+token)
+	request.Header.Set("User-Agent", "actions/cache")
+	request.Header.Set("Content-Type", "application/json")
+	request.Header.Set("Accept", "application/json;api-version=6.0-preview.1")
+	request.Header.Set("Accept-Charset", "utf-8")
+	response, _ := httpClient.Do(request)
+	if response.StatusCode != 204 {
+		defer response.Body.Close()
+		bodyBytes, _ := ioutil.ReadAll(response.Body)
+		log.Printf("Failed to commit cache %d: %s\n", cacheId, string(bodyBytes))
+		return fmt.Errorf("failed to commit cache %d with status %d: %s", cacheId, response.StatusCode, string(bodyBytes))
+	}
+	return nil
+}
+
+func reserveCache(key string) (int, error) {
+	requestUrl := getCacheApiUrl("caches")
+	requestBody := fmt.Sprintf("{ \"key\": \"%s\", \"version\": \"%s\" }", key, calculateSHA256(key))
+	request, _ := http.NewRequest("POST", requestUrl, bytes.NewBufferString(requestBody))
+	request.Header.Set("Authorization", "Bearer "+token)
+	request.Header.Set("User-Agent", "actions/cache")
+	request.Header.Set("Content-Type", "application/json")
+	request.Header.Set("Accept", "application/json;api-version=6.0-preview.1")
+	request.Header.Set("Accept-Charset", "utf-8")
+
+	response, err := httpClient.Do(request)
+	if err != nil {
+		return -1, err
+	}
+	defer response.Body.Close()
+	bodyBytes, err := ioutil.ReadAll(utfbom.SkipOnly(response.Body))
+	if response.StatusCode >= 400 {
+		return -1, fmt.Errorf("failed to reserve cache: %d", response.StatusCode)
+	}
+
+	var cacheResponse ReserveCacheResponse
+	err = json.Unmarshal(bodyBytes, &cacheResponse)
+	if err != nil {
+		return -1, err
+	}
+	return cacheResponse.CacheId, nil
+}
+
+func calculateSHA256(s string) string {
+	h := sha256.New()
+	h.Write([]byte(s))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func getCacheApiUrl(resource string) string {
+	baseUrl := strings.ReplaceAll(os.Getenv("ACTIONS_CACHE_URL"), "pipelines", "artifactcache")
+	return baseUrl + "_apis/artifactcache/" + resource
+}
diff --git a/http-cache-action/proxy/proxy_test.go b/http-cache-action/proxy/proxy_test.go
new file mode 100644
index 0000000..40a1d64
--- /dev/null
+++ b/http-cache-action/proxy/proxy_test.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+	"crypto/rand"
+	"fmt"
+	"io"
+	"net/http"
+	"testing"
+	"time"
+)
+
+func Test_API(t *testing.T) {
+	//log.Printf("Cache URL for debugging: %s\n", os.Getenv("ACTIONS_CACHE_URL"))
+	//encodedToken := base32.StdEncoding.EncodeToString([]byte(os.Getenv("ACTIONS_RUNTIME_TOKEN")))
+	//log.Printf("Token for debugging: %s\n", encodedToken)
+
+	cacheKey := fmt.Sprintf("Linux-node-%d", time.Now().Unix())
+	location, err := findCacheLocation(cacheKey)
+	if err != nil {
+		t.Error(err)
+	}
+	if location != "" {
+		t.Error("cache should not exist")
+	}
+
+	cacheId, err := reserveCache(cacheKey)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	var cacheEntrySize int64 = 100 * 1024 * 1024
+	err = uploadCacheFromReader(cacheId, io.LimitReader(rand.Reader, cacheEntrySize))
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	location, err = findCacheLocation(cacheKey)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if location == "" {
+		t.Error("cache should exist now!")
+		return
+	}
+
+	resp, err := http.Get(location)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if resp.StatusCode != 200 {
+		t.Errorf("Failed to download cache entry: %d %s", resp.StatusCode, resp.Status)
+	}
+
+	if resp.ContentLength != cacheEntrySize {
+		t.Errorf("Downloaded only %d bytes!", resp.ContentLength)
+		return
+	}
+}
diff --git a/http-cache-action/src/action.ts b/http-cache-action/src/action.ts
new file mode 100644
index 0000000..f6ccdd9
--- /dev/null
+++ b/http-cache-action/src/action.ts
@@ -0,0 +1,23 @@
+import * as core from '@actions/core';
+import {exec} from "@actions/exec";
+
+async function run() {
+    const port = core.getInput('port');
+
+    let runArguments = [
+        "run", "-d", "-p", `${port}:12321`,
+        "--name", "cache_proxy",
+        "--env", "ACTIONS_CACHE_URL",
+        "--env", "ACTIONS_RUNTIME_URL",
+        "--env", "ACTIONS_RUNTIME_TOKEN",
+        "ghcr.io/cirruslabs/actions-http-cache-proxy:latest"
+    ];
+
+    try {
+        await exec(`"docker"`, runArguments);
+    } catch (e: any) {
+        core.error(e)
+    }
+}
+
+run();
\ No newline at end of file
diff --git a/http-cache-action/tsconfig.json b/http-cache-action/tsconfig.json
new file mode 100644
index 0000000..165aee8
--- /dev/null
+++ b/http-cache-action/tsconfig.json
@@ -0,0 +1,7 @@
+{
+  "compilerOptions": {
+    "moduleResolution": "node",
+    "strict": true,
+    "target": "es6"
+  }
+}
\ No newline at end of file