├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── skipped-build.yml │ ├── security-scanning-csharp.yml │ └── build.yml ├── .nuke ├── parameters.json └── build.schema.json ├── .gitattributes ├── .vscode ├── settings.json └── extensions.json ├── GitVersion.yml ├── nuget.config ├── .gitignore ├── tests └── XsltTests │ ├── expected │ ├── display-name-test.xml │ ├── passed-test.xml │ ├── skipped-test.xml │ ├── inline-data-test.xml │ └── failed-test.xml │ ├── input │ ├── display-name-test.xml │ ├── passed-test.xml │ ├── skipped-test.xml │ ├── inline-data-test.xml │ └── failed-test.xml │ ├── ProgramTests.cs │ ├── XsltTests.csproj │ └── JUnitTransformerTests.cs ├── xUnitToJUnit.sln.DotSettings ├── src ├── xUnitToJUnit │ ├── README.md │ ├── Program.cs │ ├── xUnitToJUnit.csproj │ └── JUnitTransformer.cs └── JUnit.xslt ├── LICENSE ├── Directory.Packages.props ├── README.md ├── xUnitToJUnit.sln └── .editorconfig /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @gabrielweyer 2 | -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "xUnitToJUnit.sln" 4 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Rider 2 | *.DotSettings eol=lf 3 | 4 | *.ps1 linguist-vendored 5 | *.sh linguist-vendored 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.schemas": { 3 | "https://json.schemastore.org/github-workflow.json": "/.github/workflows/*.yml" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-dotnettools.csharp", 4 | "EditorConfig.EditorConfig", 5 | "redhat.vscode-yaml" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | feature: 3 | mode: ContinuousDeployment 4 | hotfix: 5 | mode: ContinuousDeployment 6 | tag: useBranchName 7 | ignore: 8 | sha: [] 9 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rider 2 | .idea/ 3 | *.DotSettings.user 4 | 5 | # Visual Studio 6 | .vs/ 7 | 8 | # Compilation detritus 9 | obj/ 10 | bin/ 11 | 12 | # Build 13 | artifacts/ 14 | 15 | # NUKE 16 | .nuke/temp/ 17 | 18 | # Git 19 | *.orig 20 | -------------------------------------------------------------------------------- /tests/XsltTests/expected/display-name-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/XsltTests/expected/passed-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /xUnitToJUnit.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "nuget" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | day: "saturday" 8 | ignore: 9 | - dependency-name: "FluentAssertions" 10 | update-types: ["version-update:semver-major"] 11 | groups: 12 | xunit: 13 | patterns: 14 | - xunit 15 | - xunit.analyzers 16 | - xunit.runner.visualstudio 17 | -------------------------------------------------------------------------------- /tests/XsltTests/expected/skipped-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/XsltTests/expected/inline-data-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/skipped-build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | paths: 7 | - .github/workflows/security-scanning-csharp.yml 8 | - .github/workflows/skipped-build.yml 9 | - .github/dependabot.yml 10 | - '.vscode/**' 11 | - .editorconfig 12 | - LICENSE 13 | - README.md 14 | jobs: 15 | build: 16 | name: Build 17 | runs-on: ubuntu-latest 18 | steps: 19 | - run: echo "Handling skipped but required check, see https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks" 20 | -------------------------------------------------------------------------------- /tests/XsltTests/input/display-name-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/XsltTests/expected/failed-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/XsltTests/input/passed-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/XsltTests/input/skipped-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/xUnitToJUnit/README.md: -------------------------------------------------------------------------------- 1 | # xUnit.net v2 XML Format to JUnit Format 2 | 3 | `.NET` [global tool][dotnet-global-tools] transforming a `xUnit.net v2 XML` test results file into a `JUnit` test results file. 4 | 5 | :rotating_light: If you're using this tool to process test results file(s) after having run `dotnet test`, I recommend using the [JUnit Test Logger][junit-logger] instead. The output is better (at least in CircleCI). If you can't install an additional NuGet package in your test project(s), can't modify your build or are processing test results file(s) that have previously been generated, please read on. 6 | 7 | ## Usage 8 | 9 | ```powershell 10 | dotnet xunit-to-junit "path-to-xunit-test-results.xml" "desired-path-to-junit-test-results.xml" 11 | ``` 12 | 13 | ## Release notes 14 | 15 | Release notes can be found on [GitHub][release-notes]. 16 | 17 | [dotnet-global-tools]: https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools 18 | [junit-logger]: https://github.com/spekt/junit.testlogger 19 | [release-notes]: https://github.com/gabrielweyer/xunit-to-junit/releases 20 | -------------------------------------------------------------------------------- /src/xUnitToJUnit/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Gabo.DotNet.xUnitToJUnit; 2 | 3 | internal static class Program 4 | { 5 | internal const int SuccessExitCode = 0; 6 | internal const int FailureExitCode = 1; 7 | 8 | internal static int Main(string[] args) 9 | { 10 | if (args.Length != 2) 11 | { 12 | Console.WriteLine("Two arguments should be provided:"); 13 | Console.WriteLine( 14 | "dotnet xunit-to-junit \"path-to-xunit-test-results.xml\" \"desired-path-to-junit-test-results.xml\""); 15 | return FailureExitCode; 16 | } 17 | 18 | var xUnitTestResultsFilePath = args[0]; 19 | var jUnitTestResultsFilePath = args[1]; 20 | 21 | var junitTransformer = new JUnitTransformer(); 22 | junitTransformer.Transform(xUnitTestResultsFilePath, jUnitTestResultsFilePath); 23 | 24 | Console.WriteLine( 25 | $"The xUnit test results file \"{xUnitTestResultsFilePath}\" has been converted to the JUnit test results file \"{jUnitTestResultsFilePath}\""); 26 | 27 | return SuccessExitCode; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gabriel Weyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/security-scanning-csharp.yml: -------------------------------------------------------------------------------- 1 | name: "C# Security scanning" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - .github/workflows/build.yml 9 | - .github/workflows/skipped-build.yml 10 | - .github/CODEOWNERS 11 | - .github/dependabot.yml 12 | - '.vscode/**' 13 | - .editorconfig 14 | - LICENSE 15 | - README.md 16 | schedule: 17 | - cron: '39 13 * * 5' 18 | 19 | jobs: 20 | analyze: 21 | name: Analyze 22 | runs-on: ubuntu-latest 23 | permissions: 24 | actions: read 25 | contents: read 26 | security-events: write 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'csharp' ] 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v4 36 | 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v3 39 | with: 40 | languages: ${{ matrix.language }} 41 | 42 | - name: Autobuild 43 | uses: github/codeql-action/autobuild@v3 44 | 45 | - name: Perform CodeQL Analysis 46 | uses: github/codeql-action/analyze@v3 47 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | AllEnabledByDefault 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/XsltTests/input/inline-data-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/XsltTests/input/failed-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/xUnitToJUnit/xUnitToJUnit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | Gabo.DotNet.xUnitToJUnit 6 | dotnet-xunit-to-junit 7 | latest 8 | enable 9 | enable 10 | true 11 | true 12 | true 13 | dotnet-xunit-to-junit 14 | Gabriel Weyer 15 | Transforms a xUnit.net v2 XML test results file into a JUnit test results file. 16 | xUnit JUnit CircleCI 17 | https://github.com/gabrielweyer/xunit-to-junit 18 | README.md 19 | MIT 20 | Copyright © xUnitToJUnit contributors 21 | true 22 | true 23 | snupkg 24 | CS7035;CA1303 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Always 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/XsltTests/ProgramTests.cs: -------------------------------------------------------------------------------- 1 | namespace Gabo.DotNet.xUnitToJUnit.Tests; 2 | 3 | public sealed class ProgramTests : IDisposable 4 | { 5 | private const string ExistingInputFilePath = "input/passed-test.xml"; 6 | private const string OutputDirectory = "output-program"; 7 | 8 | [Fact] 9 | public void GivenNoArgumentProvided_ThenFailureExitCode() 10 | { 11 | // Arrange 12 | var input = Array.Empty(); 13 | 14 | // Act 15 | var actualExitCode = Program.Main(input); 16 | 17 | // Assert 18 | actualExitCode.Should().Be(Program.FailureExitCode); 19 | } 20 | 21 | [Fact] 22 | public void GivenSingleArgumentProvided_ThenFailureExitCode() 23 | { 24 | // Arrange 25 | var input = new[] { ExistingInputFilePath }; 26 | 27 | // Act 28 | var actualExitCode = Program.Main(input); 29 | 30 | // Assert 31 | actualExitCode.Should().Be(Program.FailureExitCode); 32 | } 33 | 34 | [Fact] 35 | public void GivenMoreThanTwoArgumentsProvided_ThenFailureExitCode() 36 | { 37 | // Arrange 38 | var input = new[] { ExistingInputFilePath, ExistingInputFilePath, ExistingInputFilePath }; 39 | 40 | // Act 41 | var actualExitCode = Program.Main(input); 42 | 43 | // Assert 44 | actualExitCode.Should().Be(Program.FailureExitCode); 45 | } 46 | 47 | [Fact] 48 | public void GivenTwoValidArgumentsProvided_ThenSuccessExitCode() 49 | { 50 | // Arrange 51 | var input = new[] { ExistingInputFilePath, "valid-arguments.xml" }; 52 | 53 | // Act 54 | var actualExitCode = Program.Main(input); 55 | 56 | // Assert 57 | actualExitCode.Should().Be(Program.SuccessExitCode); 58 | } 59 | 60 | public void Dispose() 61 | { 62 | if (Directory.Exists(OutputDirectory)) 63 | { 64 | Directory.Delete(OutputDirectory, true); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/XsltTests/XsltTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Gabo.DotNet.xUnitToJUnit.Tests 5 | Gabo.DotNet.xUnitToJUnit.Tests 6 | latest 7 | enable 8 | enable 9 | false 10 | CS7035;CA1707;CA1515 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | 36 | 37 | Always 38 | 39 | 40 | Always 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/JUnit.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | T 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/xUnitToJUnit/JUnitTransformer.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Xml; 3 | using System.Xml.Xsl; 4 | 5 | namespace Gabo.DotNet.xUnitToJUnit; 6 | 7 | /// 8 | /// Transforms a `xUnit.net v2 XML` test results file into a `JUnit` test results file. 9 | /// 10 | internal class JUnitTransformer 11 | { 12 | private readonly XmlWriterSettings _writerSettings; 13 | private readonly XslCompiledTransform _xlsTransform; 14 | 15 | public JUnitTransformer() 16 | { 17 | _xlsTransform = new XslCompiledTransform(); 18 | var xsltPath = $"{AppContext.BaseDirectory}/JUnit.xslt"; 19 | _xlsTransform.Load(xsltPath); 20 | 21 | if (_xlsTransform.OutputSettings == null) 22 | { 23 | throw new InvalidOperationException($"The XSLT does not contain a xsl:output element ('{xsltPath}')."); 24 | } 25 | 26 | _writerSettings = _xlsTransform.OutputSettings.Clone(); 27 | _writerSettings.Encoding = new UTF8Encoding(false); 28 | } 29 | 30 | /// 31 | /// Transforms a `xUnit.net v2 XML` test results file into a `JUnit` test results file. 32 | /// 33 | /// The `xUnit.net v2 XML` test results file path. 34 | /// The `JUnit` test results file path, if the containing 35 | /// directory does not exist it will be created. 36 | public void Transform(string xUnitTestResultsFilePath, string jUnitTestResultsFilePath) 37 | { 38 | var jUnitTestResultsDirectory = Path.GetDirectoryName(jUnitTestResultsFilePath); 39 | 40 | if (!string.IsNullOrEmpty(jUnitTestResultsDirectory) && !Directory.Exists(jUnitTestResultsDirectory)) 41 | { 42 | Directory.CreateDirectory(jUnitTestResultsDirectory); 43 | } 44 | 45 | using (var stream = new FileStream(jUnitTestResultsFilePath, FileMode.Create, FileAccess.Write)) 46 | { 47 | Transform(xUnitTestResultsFilePath, stream); 48 | } 49 | } 50 | 51 | /// 52 | /// Transforms a `xUnit.net v2 XML` test results file into the `JUnit` format and write the result 53 | /// to a Stream. 54 | /// 55 | /// The `xUnit.net v2 XML` test results file path. 56 | /// The output Stream. 57 | public void Transform(string xUnitTestResultsFilePath, Stream stream) 58 | { 59 | using (var results = XmlWriter.Create(stream, _writerSettings)) 60 | { 61 | _xlsTransform.Transform(xUnitTestResultsFilePath, results); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xUnit.net v2 XML Format to JUnit Format # 2 | 3 | :rotating_light: If you're using this tool to process test results file(s) after having run `dotnet test`, I recommend using the [JUnit Test Logger][junit-logger] instead. The output is better (at least in CircleCI). If you can't install an additional NuGet package in your test project(s), can't modify your build or are processing test results file(s) that have previously been generated, please read on. 4 | 5 | | Package | Release | 6 | | ----------------------- | ------------------------------------------------ | 7 | | `dotnet-xunit-to-junit` | [![NuGet][nuget-tool-badge]][nuget-tool-command] | 8 | 9 | | CI | Status | Platform(s) | Framework(s) | Test Framework(s) | 10 | | ------------------------ | -------------------------------------------------------- | ----------- | ------------ | ----------------- | 11 | | [GitHub][github-actions] | [![Build Status][github-actions-shield]][github-actions] | `Ubuntu` | `net8.0` | `net8.0` | 12 | 13 | [CircleCI][circle-ci] can only parse test results in the [JUnit format][junit-format]. This `Extensible Stylesheet Language Transformations` can transform a `xUnit.net v2 XML` test results file into a `JUnit` test results file. 14 | 15 | **Note**: this only handles the easiest use case for the moment, as soon as I encounter issues in real life usage I'll add extra testing scenarios. 16 | 17 | ## Consume the transform ## 18 | 19 | ### Consume `JUnit.xslt` through the `dotnet-xunit-to-junit` `NuGet` package ### 20 | 21 | `dotnet-xunit-to-junit` is a `.NET` [global tool][dotnet-global-tools]: 22 | 23 | ```powershell 24 | dotnet tool install -g dotnet-xunit-to-junit 25 | dotnet xunit-to-junit "path-to-xunit-test-results.xml" "desired-path-to-junit-test-results.xml" 26 | ``` 27 | 28 | ### Consume `JUnit.xslt` directly from C# ### 29 | 30 | ```csharp 31 | // Required using statement 32 | using System.Xml.Xsl; 33 | 34 | // Change the value of these three variables 35 | const string inputFilePath = "C:/tmp/xunit.xml"; 36 | const string outputFilePath = "C:/tmp/junit.xml"; 37 | const string xsltFilePath = "C:/tmp/JUnit.xslt"; 38 | 39 | var xlsTransform = new XslCompiledTransform(); 40 | xlsTransform.Load(xsltFilePath); 41 | 42 | var writerSettings = xlsTransform.OutputSettings.Clone(); 43 | // Save without BOM, CircleCI can't read test results files starting with a BOM 44 | writerSettings.Encoding = new UTF8Encoding(false); 45 | 46 | using (var stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write)) 47 | using (var results = XmlWriter.Create(stream, writerSettings)) 48 | { 49 | xlsTransform.Transform(inputFilePath, results); 50 | } 51 | ``` 52 | 53 | ## Building locally ## 54 | 55 | Run this command to build on Windows: 56 | 57 | ```powershell 58 | .\build.ps1 59 | ``` 60 | 61 | Run this command to build on Linux / macOS: 62 | 63 | ```shell 64 | ./build.sh 65 | ``` 66 | 67 | If you want to pack the `.NET Global Tool`, you can run `.\build.ps1 --package`. 68 | 69 | [github-actions]: https://github.com/gabrielweyer/xunit-to-junit/actions/workflows/build.yml 70 | [github-actions-shield]: https://github.com/gabrielweyer/xunit-to-junit/actions/workflows/build.yml/badge.svg 71 | [circle-ci]: https://circleci.com/ 72 | [junit-format]: http://llg.cubic.org/docs/junit/ 73 | [nuget-tool-badge]: https://img.shields.io/nuget/v/dotnet-xunit-to-junit.svg?label=NuGet 74 | [nuget-tool-command]: https://www.nuget.org/packages/dotnet-xunit-to-junit 75 | [dotnet-global-tools]: https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools 76 | [junit-logger]: https://github.com/spekt/junit.testlogger 77 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - .github/workflows/security-scanning-csharp.yml 8 | - .github/workflows/skipped-build.yml 9 | - .github/dependabot.yml 10 | - '.vscode/**' 11 | - .editorconfig 12 | - LICENSE 13 | - README.md 14 | pull_request: 15 | branches: 16 | - main 17 | paths-ignore: 18 | - .github/workflows/security-scanning-csharp.yml 19 | - .github/workflows/skipped-build.yml 20 | - .github/dependabot.yml 21 | - '.vscode/**' 22 | - .editorconfig 23 | - LICENSE 24 | - README.md 25 | jobs: 26 | build: 27 | name: Build 28 | runs-on: ubuntu-latest 29 | env: 30 | CREATE_RELEASE: ${{ github.ref_name == 'main' && !contains(github.event.head_commit.message, '[skip-release]') && !contains(github.event.head_commit.message, 'dependabot[bot]') }} 31 | IS_FEATURE_PULL_REQUEST: ${{ github.event_name == 'pull_request' && startsWith(github.head_ref, 'features/') }} 32 | CREATE_PRERELEASE: 'false' 33 | PACKAGE_VERSION: 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v4 37 | with: 38 | fetch-depth: 0 39 | - name: Build 40 | run: ./build.sh --package 41 | - name: Upload NuGet packages 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: packages 45 | path: ./artifacts/packages 46 | if-no-files-found: error 47 | - name: Upload XSLT transform 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: transform 51 | path: ./src/JUnit.xslt 52 | if-no-files-found: error 53 | - name: Upload dotnet test results 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: tests-results 57 | path: ./artifacts/test-results/**/*.html 58 | if-no-files-found: error 59 | if: ${{ always() }} 60 | - name: Upload coverage report 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: coverage 64 | path: ./artifacts/coverage-report 65 | - name: Determine if we're skipping release on feature Pull Request 66 | if: ${{ env.IS_FEATURE_PULL_REQUEST == 'true' }} 67 | run: | 68 | headCommitMessage=$(git log ${{ github.event.pull_request.head.sha }} -n 1 --format=%B) 69 | echo "HEAD commit message is: $headCommitMessage" 70 | if [[ $headCommitMessage != *"[skip-release]"* ]]; then 71 | echo "CREATE_PRERELEASE=true" >> $GITHUB_ENV 72 | fi 73 | - name: Create GitHub release on main branch 74 | if: ${{ env.CREATE_RELEASE == 'true' }} 75 | env: 76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | run: | 78 | touch empty-release-notes.txt 79 | gh release create v${{ env.PACKAGE_VERSION }} --title v${{ env.PACKAGE_VERSION }} ./artifacts/packages/* --target ${{ github.sha }} --repo ${{ github.repository }} --notes-file empty-release-notes.txt 80 | - name: Create GitHub prerelease on feature Pull Request 81 | if: ${{ env.CREATE_PRERELEASE == 'true' }} 82 | env: 83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | run: | 85 | touch empty-release-notes.txt 86 | gh release create v${{ env.PACKAGE_VERSION }} --title v${{ env.PACKAGE_VERSION }} ./artifacts/packages/* --target ${{ github.sha }} --repo ${{ github.repository }} --notes-file empty-release-notes.txt --prerelease 87 | - name: Push NuGet package on main branch 88 | if: ${{ env.CREATE_RELEASE == 'true' }} 89 | run: dotnet nuget push ./artifacts/packages/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} 90 | -------------------------------------------------------------------------------- /xUnitToJUnit.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 16 3 | VisualStudioVersion = 16.0.28822.285 4 | MinimumVisualStudioVersion = 15.0.26124.0 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{139529A6-6260-4B32-BA29-D805AF2EBC91}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XsltTests", "tests\XsltTests\XsltTests.csproj", "{E59F55FD-E92F-4D0B-8654-CA00237BD758}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{14B6EC2B-DF74-4539-B284-466FABE7F70F}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xUnitToJUnit", "src\xUnitToJUnit\xUnitToJUnit.csproj", "{108F0E90-5660-4F79-9F55-1ABA43F787F8}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{BD7F1393-1CEF-4033-8FF5-07D5A93072AA}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Debug|x64 = Debug|x64 19 | Debug|x86 = Debug|x86 20 | Release|Any CPU = Release|Any CPU 21 | Release|x64 = Release|x64 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {BD7F1393-1CEF-4033-8FF5-07D5A93072AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {BD7F1393-1CEF-4033-8FF5-07D5A93072AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|x64.ActiveCfg = Debug|Any CPU 30 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|x64.Build.0 = Debug|Any CPU 31 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|x86.ActiveCfg = Debug|Any CPU 32 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Debug|x86.Build.0 = Debug|Any CPU 33 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|x64.ActiveCfg = Release|Any CPU 36 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|x64.Build.0 = Release|Any CPU 37 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|x86.ActiveCfg = Release|Any CPU 38 | {E59F55FD-E92F-4D0B-8654-CA00237BD758}.Release|x86.Build.0 = Release|Any CPU 39 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|x64.ActiveCfg = Debug|Any CPU 42 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|x64.Build.0 = Debug|Any CPU 43 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|x86.ActiveCfg = Debug|Any CPU 44 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Debug|x86.Build.0 = Debug|Any CPU 45 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|x64.ActiveCfg = Release|Any CPU 48 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|x64.Build.0 = Release|Any CPU 49 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|x86.ActiveCfg = Release|Any CPU 50 | {108F0E90-5660-4F79-9F55-1ABA43F787F8}.Release|x86.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(NestedProjects) = preSolution 56 | {E59F55FD-E92F-4D0B-8654-CA00237BD758} = {139529A6-6260-4B32-BA29-D805AF2EBC91} 57 | {108F0E90-5660-4F79-9F55-1ABA43F787F8} = {14B6EC2B-DF74-4539-B284-466FABE7F70F} 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {47A69549-61A7-4543-A51B-D96D9EB41663} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "definitions": { 4 | "Host": { 5 | "type": "string", 6 | "enum": [ 7 | "AppVeyor", 8 | "AzurePipelines", 9 | "Bamboo", 10 | "Bitbucket", 11 | "Bitrise", 12 | "GitHubActions", 13 | "GitLab", 14 | "Jenkins", 15 | "Rider", 16 | "SpaceAutomation", 17 | "TeamCity", 18 | "Terminal", 19 | "TravisCI", 20 | "VisualStudio", 21 | "VSCode" 22 | ] 23 | }, 24 | "ExecutableTarget": { 25 | "type": "string", 26 | "enum": [ 27 | "Clean", 28 | "Compile", 29 | "Coverage", 30 | "Pack", 31 | "Restore", 32 | "SetGitHubVersion", 33 | "Test", 34 | "VerifyFormat" 35 | ] 36 | }, 37 | "Verbosity": { 38 | "type": "string", 39 | "description": "", 40 | "enum": [ 41 | "Verbose", 42 | "Normal", 43 | "Minimal", 44 | "Quiet" 45 | ] 46 | }, 47 | "NukeBuild": { 48 | "properties": { 49 | "Continue": { 50 | "type": "boolean", 51 | "description": "Indicates to continue a previously failed build attempt" 52 | }, 53 | "Help": { 54 | "type": "boolean", 55 | "description": "Shows the help text for this build assembly" 56 | }, 57 | "Host": { 58 | "description": "Host for execution. Default is 'automatic'", 59 | "$ref": "#/definitions/Host" 60 | }, 61 | "NoLogo": { 62 | "type": "boolean", 63 | "description": "Disables displaying the NUKE logo" 64 | }, 65 | "Partition": { 66 | "type": "string", 67 | "description": "Partition to use on CI" 68 | }, 69 | "Plan": { 70 | "type": "boolean", 71 | "description": "Shows the execution plan (HTML)" 72 | }, 73 | "Profile": { 74 | "type": "array", 75 | "description": "Defines the profiles to load", 76 | "items": { 77 | "type": "string" 78 | } 79 | }, 80 | "Root": { 81 | "type": "string", 82 | "description": "Root directory during build execution" 83 | }, 84 | "Skip": { 85 | "type": "array", 86 | "description": "List of targets to be skipped. Empty list skips all dependencies", 87 | "items": { 88 | "$ref": "#/definitions/ExecutableTarget" 89 | } 90 | }, 91 | "Target": { 92 | "type": "array", 93 | "description": "List of targets to be invoked. Default is '{default_target}'", 94 | "items": { 95 | "$ref": "#/definitions/ExecutableTarget" 96 | } 97 | }, 98 | "Verbosity": { 99 | "description": "Logging verbosity during build execution. Default is 'Normal'", 100 | "$ref": "#/definitions/Verbosity" 101 | } 102 | } 103 | } 104 | }, 105 | "allOf": [ 106 | { 107 | "properties": { 108 | "Configuration": { 109 | "type": "string", 110 | "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", 111 | "enum": [ 112 | "Debug", 113 | "Release" 114 | ] 115 | }, 116 | "Package": { 117 | "type": "boolean", 118 | "description": "Whether we create the NuGet package - Default is false" 119 | }, 120 | "Solution": { 121 | "type": "string", 122 | "description": "Path to a solution file that is automatically loaded" 123 | } 124 | } 125 | }, 126 | { 127 | "$ref": "#/definitions/NukeBuild" 128 | } 129 | ] 130 | } 131 | -------------------------------------------------------------------------------- /tests/XsltTests/JUnitTransformerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Gabo.DotNet.xUnitToJUnit.Tests; 4 | 5 | public sealed class JUnitTransformerTests : IDisposable 6 | { 7 | private readonly JUnitTransformer _target; 8 | private const string ExistingInputFilePath = "input/passed-test.xml"; 9 | private const string OutputDirectory = "output-transformer"; 10 | 11 | public JUnitTransformerTests() 12 | { 13 | _target = new JUnitTransformer(); 14 | } 15 | 16 | [Fact] 17 | public void GivenOnlyFileNameForOutputPath_WhenTransform_ThenCreateFile() 18 | { 19 | // Act 20 | _target.Transform(ExistingInputFilePath, "junit.xml"); 21 | 22 | // Assert 23 | Assert.True(File.Exists("junit.xml")); 24 | } 25 | 26 | [Fact] 27 | public void GivenNonExistingOutputDirectory_WhenTransform_ThenCreateDirectory() 28 | { 29 | // Arrange 30 | const string outputFilePath = $"{OutputDirectory}/circle-ci/junit.xml"; 31 | Assert.False(Directory.Exists(OutputDirectory)); 32 | 33 | // Act 34 | _target.Transform(ExistingInputFilePath, outputFilePath); 35 | 36 | // Assert 37 | Assert.True(File.Exists(outputFilePath)); 38 | } 39 | 40 | [Fact] 41 | public void GivenValidInput_WhenTransform_ThenSaveWithoutBom() 42 | { 43 | // Arrange 44 | const string inputFileName = "passed-test"; 45 | 46 | // Act 47 | var actual = Transform(inputFileName); 48 | 49 | // Assert 50 | Assert.Equal('<', actual[0]); 51 | } 52 | 53 | [Fact] 54 | public void GivenPassedTest_WhenTransform_ThenGeneratePassedMarkup() 55 | { 56 | // Arrange 57 | const string inputFileName = "passed-test"; 58 | 59 | // Act 60 | var actual = Transform(inputFileName); 61 | 62 | // Assert 63 | var expected = GetExpected(inputFileName); 64 | Assert.Equal(expected, actual); 65 | } 66 | 67 | [Fact] 68 | public void GivenInlineData_WhenTransform_ThenIncludeInlineDataInName() 69 | { 70 | // Arrange 71 | const string inputFileName = "inline-data-test"; 72 | 73 | // Act 74 | var actual = Transform(inputFileName); 75 | 76 | // Assert 77 | var expected = GetExpected(inputFileName); 78 | Assert.Equal(expected, actual); 79 | } 80 | 81 | [Fact] 82 | public void GivenDisplayName_WhenTransform_ThenUseDisplayNameInsteadOfEmptyString() 83 | { 84 | // Arrange 85 | const string inputFileName = "display-name-test"; 86 | 87 | // Act 88 | var actual = Transform(inputFileName); 89 | 90 | // Assert 91 | var expected = GetExpected(inputFileName); 92 | Assert.Equal(expected, actual); 93 | } 94 | 95 | [Fact] 96 | public void GivenSkippedTest_WhenTransform_ThenGenerateSkippedMarkup() 97 | { 98 | // Arrange 99 | const string inputFileName = "skipped-test"; 100 | 101 | // Act 102 | var actual = Transform(inputFileName); 103 | 104 | // Assert 105 | var expected = GetExpected(inputFileName); 106 | Assert.Equal(expected, actual); 107 | } 108 | 109 | [Fact] 110 | public void GivenFailedTest_WhenTransform_ThenGenerateFailedMarkup() 111 | { 112 | // Arrange 113 | const string inputFileName = "failed-test"; 114 | 115 | // Act 116 | var actual = Transform(inputFileName); 117 | 118 | // Assert 119 | var expected = GetExpected(inputFileName); 120 | Assert.Equal(expected, actual); 121 | } 122 | 123 | private string Transform(string inputFileName) 124 | { 125 | using var stream = new MemoryStream(); 126 | _target.Transform($"./input/{inputFileName}.xml", stream); 127 | return Encoding.UTF8.GetString(stream.ToArray()); 128 | } 129 | 130 | private static string GetExpected(string inputFileName) 131 | { 132 | return File.ReadAllText($"./expected/{inputFileName}.xml"); 133 | } 134 | 135 | public void Dispose() 136 | { 137 | if (Directory.Exists(OutputDirectory)) 138 | { 139 | Directory.Delete(OutputDirectory, true); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.cs] 13 | indent_size = 4 14 | 15 | [*.{xml,csproj,xslt,props}] 16 | indent_size = 2 17 | 18 | [*.xml] 19 | insert_final_newline = false 20 | 21 | [*.json] 22 | indent_size = 2 23 | 24 | [*.ps1] 25 | indent_size = 4 26 | 27 | [*.sh] 28 | end_of_line = lf 29 | indent_size = 2 30 | 31 | # Dotnet code style settings: 32 | [*.cs] 33 | 34 | # Sort using and Import directives with System.* appearing first 35 | dotnet_sort_system_directives_first = true 36 | dotnet_separate_import_directive_groups = false 37 | 38 | # Avoid "this." and "Me." if not necessary 39 | dotnet_style_qualification_for_field = false:error 40 | dotnet_style_qualification_for_property = false:error 41 | dotnet_style_qualification_for_method = false:error 42 | dotnet_style_qualification_for_event = false:error 43 | 44 | # Use language keywords instead of framework type names for type references 45 | dotnet_style_predefined_type_for_locals_parameters_members = true:error 46 | dotnet_style_predefined_type_for_member_access = true:error 47 | 48 | # Suggest more modern language features when available 49 | dotnet_style_object_initializer = true:suggestion 50 | dotnet_style_collection_initializer = true:suggestion 51 | dotnet_style_coalesce_expression = true:suggestion 52 | dotnet_style_null_propagation = true:suggestion 53 | dotnet_style_explicit_tuple_names = true:suggestion 54 | 55 | # Whitespace options 56 | dotnet_style_allow_multiple_blank_lines_experimental = false 57 | dotnet_style_allow_statement_immediately_after_block_experimental = false 58 | 59 | # Non-private static fields are PascalCase 60 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion 61 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields 62 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style 63 | 64 | dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field 65 | dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 66 | dotnet_naming_symbols.non_private_static_fields.required_modifiers = static 67 | 68 | dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case 69 | 70 | # Non-private readonly fields are PascalCase 71 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion 72 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields 73 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style 74 | 75 | dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field 76 | dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 77 | dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly 78 | 79 | dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case 80 | 81 | # Constants are PascalCase 82 | dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion 83 | dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants 84 | dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style 85 | 86 | dotnet_naming_symbols.constants.applicable_kinds = field 87 | dotnet_naming_symbols.constants.required_modifiers = const 88 | 89 | dotnet_naming_style.constant_style.capitalization = pascal_case 90 | 91 | # Private instance fields are camelCase and start with _ 92 | dotnet_naming_symbols.private_instance_fields.applicable_kinds = field 93 | dotnet_naming_symbols.private_instance_fields.applicable_accessibilities = private 94 | 95 | dotnet_naming_style.private_instance_field_style.capitalization = camel_case 96 | dotnet_naming_style.private_instance_field_style.required_prefix = _ 97 | 98 | dotnet_naming_rule.private_instance_fields_should_be_camel_case.severity = warning 99 | dotnet_naming_rule.private_instance_fields_should_be_camel_case.symbols = private_instance_fields 100 | dotnet_naming_rule.private_instance_fields_should_be_camel_case.style = private_instance_field_style 101 | 102 | # Locals and parameters are camelCase 103 | dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion 104 | dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters 105 | dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style 106 | 107 | dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local 108 | 109 | dotnet_naming_style.camel_case_style.capitalization = camel_case 110 | 111 | # Local functions are PascalCase 112 | dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion 113 | dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions 114 | dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style 115 | 116 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 117 | 118 | dotnet_naming_style.local_function_style.capitalization = pascal_case 119 | 120 | # By default, name items with PascalCase 121 | dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion 122 | dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members 123 | dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style 124 | 125 | dotnet_naming_symbols.all_members.applicable_kinds = * 126 | 127 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 128 | 129 | # IDE0035: Remove unreachable code 130 | dotnet_diagnostic.IDE0035.severity = warning 131 | 132 | # IDE0036: Order modifiers 133 | dotnet_diagnostic.IDE0036.severity = warning 134 | 135 | # IDE0043: Format string contains invalid placeholder 136 | dotnet_diagnostic.IDE0043.severity = warning 137 | 138 | # IDE0044: Make field readonly 139 | dotnet_diagnostic.IDE0044.severity = warning 140 | 141 | # IDE0011: Add braces 142 | csharp_prefer_braces = true 143 | # NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201 144 | dotnet_diagnostic.IDE0011.severity = warning 145 | 146 | # IDE0040: Add accessibility modifiers 147 | dotnet_diagnostic.IDE0040.severity = warning 148 | 149 | # CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? 150 | # IDE0051: Remove unused private member 151 | dotnet_diagnostic.IDE0051.severity = warning 152 | 153 | # IDE0052: Remove unread private member 154 | dotnet_diagnostic.IDE0052.severity = warning 155 | 156 | # IDE0059: Unnecessary assignment to a value 157 | dotnet_diagnostic.IDE0059.severity = warning 158 | 159 | # IDE0060: Remove unused parameter 160 | dotnet_diagnostic.IDE0060.severity = warning 161 | 162 | # CA1012: Abstract types should not have public constructors 163 | dotnet_diagnostic.CA1012.severity = warning 164 | 165 | # CA1822: Make member static 166 | dotnet_diagnostic.CA1822.severity = warning 167 | 168 | # IDE0005: Using directive is unnecessary 169 | dotnet_diagnostic.IDE0005.severity = warning 170 | 171 | # dotnet_style_allow_multiple_blank_lines_experimental 172 | dotnet_diagnostic.IDE2000.severity = warning 173 | 174 | # csharp_style_allow_embedded_statements_on_same_line_experimental 175 | dotnet_diagnostic.IDE2001.severity = warning 176 | 177 | # csharp_style_allow_blank_lines_between_consecutive_braces_experimental 178 | dotnet_diagnostic.IDE2002.severity = warning 179 | 180 | # dotnet_style_allow_statement_immediately_after_block_experimental 181 | dotnet_diagnostic.IDE2003.severity = warning 182 | 183 | # csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental 184 | dotnet_diagnostic.IDE2004.severity = warning 185 | 186 | # CSharp code style settings: 187 | [*.cs] 188 | # Newline settings 189 | csharp_new_line_before_open_brace = all 190 | csharp_new_line_before_else = true 191 | csharp_new_line_before_catch = true 192 | csharp_new_line_before_finally = true 193 | csharp_new_line_before_members_in_object_initializers = true 194 | csharp_new_line_before_members_in_anonymous_types = true 195 | csharp_new_line_between_query_expression_clauses = true 196 | 197 | # Indentation preferences 198 | csharp_indent_block_contents = true 199 | csharp_indent_braces = false 200 | csharp_indent_case_contents = true 201 | csharp_indent_case_contents_when_block = true 202 | csharp_indent_switch_labels = true 203 | csharp_indent_labels = flush_left 204 | 205 | # Whitespace options 206 | csharp_style_allow_embedded_statements_on_same_line_experimental = false 207 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false 208 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false 209 | 210 | # Prefer "var" everywhere 211 | dotnet_diagnostic.IDE0007.severity = error 212 | csharp_style_var_for_built_in_types = true:error 213 | csharp_style_var_when_type_is_apparent = true:error 214 | csharp_style_var_elsewhere = true:error 215 | 216 | # Prefer method-like constructs to have a block body 217 | csharp_style_expression_bodied_methods = false:none 218 | csharp_style_expression_bodied_constructors = false:none 219 | csharp_style_expression_bodied_operators = false:none 220 | 221 | # Prefer property-like constructs to have an expression-body 222 | csharp_style_expression_bodied_properties = true:error 223 | csharp_style_expression_bodied_indexers = true:error 224 | csharp_style_expression_bodied_accessors = true:error 225 | 226 | # Suggest more modern language features when available 227 | csharp_style_pattern_matching_over_is_with_cast_check = true:error 228 | csharp_style_pattern_matching_over_as_with_null_check = true:error 229 | csharp_style_inlined_variable_declaration = true:suggestion 230 | csharp_style_throw_expression = true:error 231 | csharp_style_conditional_delegate_call = true:suggestion 232 | 233 | # Spacing 234 | csharp_space_after_cast = false 235 | csharp_space_after_colon_in_inheritance_clause = true 236 | csharp_space_after_comma = true 237 | csharp_space_after_dot = false 238 | csharp_space_after_keywords_in_control_flow_statements = true 239 | csharp_space_after_semicolon_in_for_statement = true 240 | csharp_space_around_binary_operators = before_and_after 241 | csharp_space_around_declaration_statements = do_not_ignore 242 | csharp_space_before_colon_in_inheritance_clause = true 243 | csharp_space_before_comma = false 244 | csharp_space_before_dot = false 245 | csharp_space_before_open_square_brackets = false 246 | csharp_space_before_semicolon_in_for_statement = false 247 | csharp_space_between_empty_square_brackets = false 248 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 249 | csharp_space_between_method_call_name_and_opening_parenthesis = false 250 | csharp_space_between_method_call_parameter_list_parentheses = false 251 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 252 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 253 | csharp_space_between_method_declaration_parameter_list_parentheses = false 254 | csharp_space_between_parentheses = false 255 | csharp_space_between_square_brackets = false 256 | 257 | # Blocks are allowed 258 | csharp_prefer_braces = true:silent 259 | csharp_preserve_single_line_blocks = true 260 | csharp_preserve_single_line_statements = true 261 | 262 | # Use file-scope namespace 263 | csharp_style_namespace_declarations = file_scoped:warning 264 | --------------------------------------------------------------------------------