Skip to content

Commit

Permalink
Add coding standard package
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou committed Feb 23, 2024
1 parent 7726a5a commit 09b626f
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 56 deletions.
10 changes: 10 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Workleap.DotNet.CodingStandards" Version="0.1.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,4 @@ class Sample
Assert.False(data.HasError("IDE1006"));
Assert.False(data.HasWarning("IDE1006"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ internal static class PathHelpers
public static string GetRootDirectory()
{
var directory = Environment.CurrentDirectory;
while (!Directory.Exists(Path.Combine(directory, ".git")))
while (directory != null && !Directory.Exists(Path.Combine(directory, ".git")))
{
directory = Path.GetDirectoryName(directory);
}

return directory;
return directory ?? throw new InvalidOperationException("Cannot find the root of the git repository");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ internal sealed class ProjectBuilder : IDisposable

public ProjectBuilder(PackageFixture fixture, ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
this._testOutputHelper = testOutputHelper;

_directory = TemporaryDirectory.Create();
_directory.CreateTextFile("NuGet.config", $"""
this._directory = TemporaryDirectory.Create();
this._directory.CreateTextFile("NuGet.config", $"""
<configuration>
<config>
<add key="globalPackagesFolder" value="{fixture.PackageDirectory}/packages" />
Expand All @@ -38,16 +38,15 @@ public ProjectBuilder(PackageFixture fixture, ITestOutputHelper testOutputHelper
</configuration>
""");

File.Copy(Path.Combine(PathHelpers.GetRootDirectory(), "global.json"), _directory.GetPath("global.json"));
File.Copy(Path.Combine(PathHelpers.GetRootDirectory(), "global.json"), this._directory.GetPath("global.json"));
}

public ProjectBuilder AddFile(string relativePath, string content)
public void AddFile(string relativePath, string content)
{
File.WriteAllText(_directory.GetPath(relativePath), content);
return this;
File.WriteAllText(this._directory.GetPath(relativePath), content);
}

public ProjectBuilder AddCsprojFile(Dictionary<string, string> properties = null)
public void AddCsprojFile(Dictionary<string, string>? properties = null)
{
var element = new XElement("PropertyGroup");
if (properties != null)
Expand Down Expand Up @@ -75,28 +74,27 @@ public ProjectBuilder AddCsprojFile(Dictionary<string, string> properties = null
</Project>
""";

File.WriteAllText(_directory.GetPath("test.csproj"), content);
return this;
File.WriteAllText(this._directory.GetPath("test.csproj"), content);
}

public async Task<SarifFile> BuildAndGetOutput(string[] buildArguments = null)
public async Task<SarifFile> BuildAndGetOutput(string[]? buildArguments = null)
{
var result = await Cli.Wrap("dotnet")
.WithWorkingDirectory(_directory.FullPath)
.WithWorkingDirectory(this._directory.FullPath)
.WithArguments(["build", .. (buildArguments ?? [])])
.WithEnvironmentVariables(env => env.Set("CI", null).Set("GITHUB_ACTIONS", null))
.WithStandardOutputPipe(PipeTarget.ToDelegate(_testOutputHelper.WriteLine))
.WithStandardErrorPipe(PipeTarget.ToDelegate(_testOutputHelper.WriteLine))
.WithStandardOutputPipe(PipeTarget.ToDelegate(this._testOutputHelper.WriteLine))
.WithStandardErrorPipe(PipeTarget.ToDelegate(this._testOutputHelper.WriteLine))
.WithValidation(CommandResultValidation.None)
.ExecuteAsync();

_testOutputHelper.WriteLine("Process exit code: " + result.ExitCode);
this._testOutputHelper.WriteLine("Process exit code: " + result.ExitCode);

var bytes = File.ReadAllBytes(_directory.GetPath(SarifFileName));
var sarif = JsonSerializer.Deserialize<SarifFile>(bytes);
_testOutputHelper.WriteLine("Sarif result:\n" + string.Join("\n", sarif.AllResults().Select(r => r.ToString())));
var bytes = await File.ReadAllBytesAsync(this._directory.GetPath(SarifFileName));
var sarif = JsonSerializer.Deserialize<SarifFile>(bytes) ?? throw new InvalidOperationException("The sarif file is invalid");
this._testOutputHelper.WriteLine("Sarif result:\n" + string.Join("\n", sarif.AllResults().Select(r => r.ToString())));
return sarif;
}

public void Dispose() => _directory.Dispose();
public void Dispose() => this._directory.Dispose();
}
14 changes: 7 additions & 7 deletions tests/Workleap.DotNet.CodingStandards.Tests/Helpers/SarifFile.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace Workleap.DotNet.CodingStandards.Tests.Helpers;

internal sealed class SarifFile
{
[JsonPropertyName("runs")]
public SarifFileRun[] Runs { get; set; }
public SarifFileRun[]? Runs { get; set; }

public IEnumerable<SarifFileRunResult> AllResults() => Runs.SelectMany(r => r.Results);
public IEnumerable<SarifFileRunResult> AllResults() => this.Runs?.SelectMany(r => r.Results ?? []) ?? [];

public bool HasError() => AllResults().Any(r => r.Level == "error");
public bool HasError(string ruleId) => AllResults().Any(r => r.Level == "error" && r.RuleId == ruleId);
public bool HasWarning(string ruleId) => AllResults().Any(r => r.Level == "warning" && r.RuleId == ruleId);
public bool HasNote(string ruleId) => AllResults().Any(r => r.Level == "note" && r.RuleId == ruleId);
public bool HasError() => this.AllResults().Any(r => r.Level == "error");
public bool HasError(string ruleId) => this.AllResults().Any(r => r.Level == "error" && r.RuleId == ruleId);
public bool HasWarning(string ruleId) => this.AllResults().Any(r => r.Level == "warning" && r.RuleId == ruleId);
public bool HasNote(string ruleId) => this.AllResults().Any(r => r.Level == "note" && r.RuleId == ruleId);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace Workleap.DotNet.CodingStandards.Tests.Helpers;

internal sealed class SarifFileRun
{
[JsonPropertyName("results")]
public SarifFileRunResult[] Results { get; set; }
public SarifFileRunResult[]? Results { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace Workleap.DotNet.CodingStandards.Tests.Helpers;

internal sealed class SarifFileRunResult
{
[JsonPropertyName("ruleId")]
public string RuleId { get; set; }
public string? RuleId { get; set; }

[JsonPropertyName("level")]
public string Level { get; set; }
public string? Level { get; set; }

[JsonPropertyName("message")]
public SarifFileRunResultMessage Message { get; set; }
public SarifFileRunResultMessage? Message { get; set; }

public override string ToString()
{
return $"{Level}:{RuleId} {Message}";
return $"{this.Level}:{this.RuleId} {this.Message}";
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace Workleap.DotNet.CodingStandards.Tests.Helpers;

internal sealed class SarifFileRunResultMessage
{
[JsonPropertyName("text")]
public string Text { get; set; }
public string? Text { get; set; }

public override string ToString()
{
return Text;
return this.Text ?? "";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#nullable enable
namespace Workleap.DotNet.CodingStandards.Tests.Helpers;
internal static class SharedHttpClient
{
Expand All @@ -18,7 +17,7 @@ private sealed class HttpRetryMessageHandler(HttpMessageHandler handler) : Deleg
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
const int MaxRetries = 5;
const int maxRetries = 5;
var defaultDelay = TimeSpan.FromMilliseconds(200);
for (var i = 1; ; i++, defaultDelay *= 2)
{
Expand Down Expand Up @@ -52,18 +51,22 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
{
result?.Dispose();
if (IsLastAttempt(i))
{
throw;
}
}
catch (TaskCanceledException ex) when (ex.CancellationToken != cancellationToken) // catch "The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing"
{
result?.Dispose();
if (IsLastAttempt(i))
{
throw;
}
}

await Task.Delay(delayHint is { } someDelay && someDelay > TimeSpan.Zero ? someDelay : defaultDelay, cancellationToken).ConfigureAwait(false);

static bool IsLastAttempt(int i) => i >= MaxRetries;
static bool IsLastAttempt(int i) => i >= maxRetries;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@ namespace Workleap.DotNet.CodingStandards.Tests.Helpers;

internal sealed class TemporaryDirectory : IDisposable
{
private TemporaryDirectory(string fullPath) => FullPath = fullPath;
private TemporaryDirectory(string fullPath) => this.FullPath = fullPath;

public string FullPath { get; }

public static TemporaryDirectory Create()
{
var path = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
Directory.CreateDirectory(path);
_ = Directory.CreateDirectory(path);
return new TemporaryDirectory(path);
}

public string GetPath(string relativePath)
{
return Path.Combine(FullPath, relativePath);
return Path.Combine(this.FullPath, relativePath);
}

public void CreateTextFile(string relativePath, string content)
{
var path = GetPath(relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(path));
var path = this.GetPath(relativePath);
_ = Directory.CreateDirectory(Path.GetDirectoryName(path)!);
File.WriteAllText(path, content);
}

public void Dispose()
{
try
{
Directory.Delete(FullPath, recursive: true);
Directory.Delete(this.FullPath, recursive: true);
}
catch
{
Expand Down
14 changes: 8 additions & 6 deletions tests/Workleap.DotNet.CodingStandards.Tests/PackageFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ public sealed class PackageFixture : IAsyncLifetime
{
private readonly TemporaryDirectory _packageDirectory = TemporaryDirectory.Create();

public string PackageDirectory => _packageDirectory.FullPath;
public string PackageDirectory => this._packageDirectory.FullPath;

public async Task InitializeAsync()
{
var nuspecPath = Path.Combine(PathHelpers.GetRootDirectory(), "Workleap.DotNet.CodingStandards.nuspec");
string[] args = ["pack", nuspecPath, "-ForceEnglishOutput", "-Version", "999.9.9", "-OutputDirectory", _packageDirectory.FullPath];
string[] args = ["pack", nuspecPath, "-ForceEnglishOutput", "-Version", "999.9.9", "-OutputDirectory", this._packageDirectory.FullPath];

if (OperatingSystem.IsWindows())
{
var exe = Path.Combine(Path.GetTempPath(), $"nuget-{Guid.NewGuid()}.exe");
await DownloadFileAsync("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", exe);

await Cli.Wrap(exe)
_ = await Cli.Wrap(exe)
.WithArguments(args)
.ExecuteAsync();
}
Expand All @@ -33,22 +33,24 @@ await Cli.Wrap(exe)
psi.ArgumentList.Add(arg);
}

var p = Process.Start(psi);
var p = Process.Start(psi)!;
await p.WaitForExitAsync();
if (p.ExitCode != 0)
{
throw new InvalidOperationException("Error when running creating the NuGet package");
}
}
}

public Task DisposeAsync()
{
_packageDirectory.Dispose();
this._packageDirectory.Dispose();
return Task.CompletedTask;
}

private static async Task DownloadFileAsync(string url, string path)
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
_ = Directory.CreateDirectory(Path.GetDirectoryName(path)!);
await using var nugetStream = await SharedHttpClient.Instance.GetStreamAsync(url);
await using var fileStream = File.Create(path);
await nugetStream.CopyToAsync(fileStream);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
Expand Down

0 comments on commit 09b626f

Please sign in to comment.