Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ csharp_space_between_square_brackets = false
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false

# C# formatting settings - Namespace options
csharp_style_namespace_declarations = file_scoped:warning

########## name all private fields using camelCase with underscore prefix ##########
# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019
# dotnet_naming_rule.<namingRuleTitle>.symbols = <symbolTitle>
Expand Down
109 changes: 54 additions & 55 deletions src/GraphQLParser.ApiTests/ApiApprovalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,74 +8,73 @@
using Shouldly;
using Xunit;

namespace GraphQLParser.ApiTests
namespace GraphQLParser.ApiTests;

/// <summary>
/// Tests to verify public API surface.
/// </summary>
public class ApiApprovalTests
{
/// <summary>
/// Tests to verify public API surface.
/// </summary>
public class ApiApprovalTests
[Theory]
[InlineData(typeof(Lexer))]
public void Public_Api_Should_Not_Change_Inadvertently(Type type)
{
[Theory]
[InlineData(typeof(Lexer))]
public void Public_Api_Should_Not_Change_Inadvertently(Type type)
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string projectName = type.Assembly.GetName().Name!;
string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..");
string projectDir = Path.Combine(testDir, "..");
string buildDir = Path.Combine(projectDir, projectName, "bin", "Debug");
Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist");
string csProject = Path.Combine(projectDir, projectName, projectName + ".csproj");
var project = XDocument.Load(csProject);
string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries);
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string projectName = type.Assembly.GetName().Name!;
string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..");
string projectDir = Path.Combine(testDir, "..");
string buildDir = Path.Combine(projectDir, projectName, "bin", "Debug");
Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist");
string csProject = Path.Combine(projectDir, projectName, projectName + ".csproj");
var project = XDocument.Load(csProject);
string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries);

// There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out
string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray();
Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}");
// There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out
string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray();
Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}");

(string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions
{
IncludeAssemblyAttributes = false,
//WhitelistedNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" },
ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }
}))).ToArray();
(string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions
{
IncludeAssemblyAttributes = false,
//WhitelistedNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" },
ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }
}))).ToArray();

if (publicApi.DistinctBy(item => item.content).Count() == 1)
if (publicApi.DistinctBy(item => item.content).Count() == 1)
{
AutoApproveOrFail(publicApi[0].content, "");
}
else
{
foreach (var item in publicApi.ToLookup(item => item.content))
{
AutoApproveOrFail(publicApi[0].content, "");
AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x)));
}
else
}

// Approval test should (re)generate approved.txt files locally if needed.
// Approval test should fail on CI.
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
void AutoApproveOrFail(string publicApi, string folder)
{
string file = null!;

try
{
foreach (var item in publicApi.ToLookup(item => item.content))
{
AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x)));
}
publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}"));
}

// Approval test should (re)generate approved.txt files locally if needed.
// Approval test should fail on CI.
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
void AutoApproveOrFail(string publicApi, string folder)
catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null)
{
string file = null!;

try
string? received = Path.Combine(testDir, folder, file);
string? approved = received.Replace(".received.txt", ".approved.txt");
if (File.Exists(received) && File.Exists(approved))
{
publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}"));
File.Copy(received, approved, overwrite: true);
File.Delete(received);
}
catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null)
else
{
string? received = Path.Combine(testDir, folder, file);
string? approved = received.Replace(".received.txt", ".approved.txt");
if (File.Exists(received) && File.Exists(approved))
{
File.Copy(received, approved, overwrite: true);
File.Delete(received);
}
else
{
throw;
}
throw;
}
}
}
Expand Down
91 changes: 45 additions & 46 deletions src/GraphQLParser.Benchmarks/Benchmarks/BenchmarkBase.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,55 @@
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;

namespace GraphQLParser.Benchmarks
{
public abstract class BenchmarkBase : IBenchmark
{
private string _hero = null!;
private string _escapes = null!;
private string _kitchen = null!;
private string _introspection = null!;
private string _params = null!;
private string _variables = null!;
private string _github = null!;
namespace GraphQLParser.Benchmarks;

[GlobalSetup]
public virtual void GlobalSetup()
{
_hero = "hero".ReadGraphQLFile();
_escapes = "query_with_many_escape_symbols".ReadGraphQLFile();
_kitchen = "kitchenSink".ReadGraphQLFile();
_introspection = "introspectionQuery".ReadGraphQLFile();
_params = "params".ReadGraphQLFile();
_variables = "variables".ReadGraphQLFile();
_github = "github".ReadGraphQLFile();
}
public abstract class BenchmarkBase : IBenchmark
{
private string _hero = null!;
private string _escapes = null!;
private string _kitchen = null!;
private string _introspection = null!;
private string _params = null!;
private string _variables = null!;
private string _github = null!;

public string GetQueryByName(string name)
{
return name switch
{
"hero" => _hero,
"escapes" => _escapes,
"kitchen" => _kitchen,
"introspection" => _introspection,
"params" => _params,
"variables" => _variables,
"github" => _github,
_ => throw new System.Exception(name)
};
}
[GlobalSetup]
public virtual void GlobalSetup()
{
_hero = "hero".ReadGraphQLFile();
_escapes = "query_with_many_escape_symbols".ReadGraphQLFile();
_kitchen = "kitchenSink".ReadGraphQLFile();
_introspection = "introspectionQuery".ReadGraphQLFile();
_params = "params".ReadGraphQLFile();
_variables = "variables".ReadGraphQLFile();
_github = "github".ReadGraphQLFile();
}

public IEnumerable<string> Names()
public string GetQueryByName(string name)
{
return name switch
{
yield return "hero";
yield return "escapes";
yield return "kitchen";
yield return "introspection";
yield return "params";
yield return "variables";
yield return "github";
}
"hero" => _hero,
"escapes" => _escapes,
"kitchen" => _kitchen,
"introspection" => _introspection,
"params" => _params,
"variables" => _variables,
"github" => _github,
_ => throw new System.Exception(name)
};
}

public abstract void Run();
public IEnumerable<string> Names()
{
yield return "hero";
yield return "escapes";
yield return "kitchen";
yield return "introspection";
yield return "params";
yield return "variables";
yield return "github";
}

public abstract void Run();
}
11 changes: 5 additions & 6 deletions src/GraphQLParser.Benchmarks/Benchmarks/IBenchmark.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
namespace GraphQLParser.Benchmarks
namespace GraphQLParser.Benchmarks;

internal interface IBenchmark
{
internal interface IBenchmark
{
void GlobalSetup();
void GlobalSetup();

void Run();
}
void Run();
}
29 changes: 14 additions & 15 deletions src/GraphQLParser.Benchmarks/Benchmarks/LexerBenchmark.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
using BenchmarkDotNet.Attributes;

namespace GraphQLParser.Benchmarks
namespace GraphQLParser.Benchmarks;

[MemoryDiagnoser]
public class LexerBenchmark : BenchmarkBase
{
[MemoryDiagnoser]
public class LexerBenchmark : BenchmarkBase
[Benchmark]
[ArgumentsSource(nameof(Names))]
public void Lex(string name)
{
[Benchmark]
[ArgumentsSource(nameof(Names))]
public void Lex(string name)
var source = GetQueryByName(name);
int resetPosition = 0;
Token token;
while ((token = Lexer.Lex(source, resetPosition)).Kind != TokenKind.EOF)
{
var source = GetQueryByName(name);
int resetPosition = 0;
Token token;
while ((token = Lexer.Lex(source, resetPosition)).Kind != TokenKind.EOF)
{
resetPosition = token.End;
}
resetPosition = token.End;
}

public override void Run() => Lex("github");
}

public override void Run() => Lex("github");
}
Loading