Skip to content

Commit

Permalink
.NET API review parser tests (#8881)
Browse files Browse the repository at this point in the history
* .NET API review parser tests
  • Loading branch information
praveenkuttappan committed Aug 26, 2024
1 parent bd0fc39 commit 4f63562
Showing 1 changed file with 150 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,167 @@
using Newtonsoft.Json.Linq;
using System.Text.Json.Serialization;
using APIView.Model.V2;
using Microsoft.CodeAnalysis;
using NuGet.ContentModel;


namespace CSharpAPIParserTests
{
public class CodeFileTests
{
private readonly CodeFile codeFile;
public Assembly assembly { get; set; }
static CodeFile templateCodeFile;
static Assembly templateAssembly { get; set; }
static CodeFile storageCodeFile;
static Assembly storageAssembly { get; set; }
static CodeFile coreCodeFile;
static Assembly coreAssembly { get; set; }

public CodeFileTests()
public CodeFileTests() { }
static CodeFileTests()
{
assembly = Assembly.Load("Azure.Template");
var dllStream = assembly.GetFile("Azure.Template.dll");
templateAssembly = Assembly.Load("Azure.Template");
var dllStream = templateAssembly.GetFile("Azure.Template.dll");
var assemblySymbol = CompilationFactory.GetCompilation(dllStream, null);
this.codeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, true, null);
templateCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, true, null);

storageAssembly = Assembly.Load("Azure.Storage.Blobs");
dllStream = storageAssembly.GetFile("Azure.Storage.Blobs.dll");
assemblySymbol = CompilationFactory.GetCompilation(dllStream, null);
storageCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, true, null);

coreAssembly = Assembly.Load("Azure.Core");
dllStream = coreAssembly.GetFile("Azure.Core.dll");
assemblySymbol = CompilationFactory.GetCompilation(dllStream, null);
coreCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, true, null);
}

public static IEnumerable<object[]> CodeFiles => new List<object[]>
{
new object[] { templateCodeFile, "Azure.Template" , "1.0.3.0", 8},
new object[] { storageCodeFile , "Azure.Storage.Blobs", "12.21.2.0", 14},
new object[] { coreCodeFile, "Azure.Core", "1.42.0.0", 26},
};

[Theory]
[MemberData(nameof(CodeFiles))]
public void TestPackageMetadata(CodeFile codeFile, string expectedPackageName, string expectedVersion, int expectedNumberOfTopLines)
{
Assert.Equal(expectedPackageName, codeFile.PackageName);
Assert.Equal(expectedVersion, codeFile.PackageVersion);
Assert.Equal("C#", codeFile.Language);
Assert.Equal(expectedNumberOfTopLines, codeFile.ReviewLines.Count);
}

[Fact]
public void TestClassReviewLineWithoutBase()
{
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient").FirstOrDefault();
Assert.NotNull(classLine);
Assert.Equal(4, classLine.Tokens.Count());
Assert.Equal("public class BlobServiceClient {", classLine.ToString().Trim());
}

[Fact]
public void TestClassReviewLineWithBase()
{
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs.Models").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.Models.BlobDownloadInfo").FirstOrDefault();
Assert.NotNull(classLine);
Assert.Equal(6, classLine.Tokens.Count());
Assert.Equal("public class BlobDownloadInfo : IDisposable {", classLine.ToString().Trim());
}

[Fact]
public void TestPackageName()
public void TestMultipleKeywords()
{
Assert.Equal("Azure.Template", codeFile.PackageName);
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs.Models").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.Models.AccessTier").FirstOrDefault();
Assert.NotNull(classLine);
Assert.Equal(10, classLine.Tokens.Count());
Assert.Equal("public readonly struct AccessTier : IEquatable<AccessTier> {", classLine.ToString().Trim());
}

[Fact]
public void TestTopLevelReviewLineCount()
public void TestApiReviewLine()
{
Assert.Equal(8, codeFile.ReviewLines.Count());
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient").FirstOrDefault();
Assert.NotNull(classLine);
var methodLine = classLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient.BlobServiceClient(System.String)").FirstOrDefault();
Assert.Equal(7, methodLine.Tokens.Count());
Assert.Equal("public BlobServiceClient(string connectionString);", methodLine.ToString().Trim());
}

[Fact]
public void TestApiReviewLineMoreParams()
{
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient").FirstOrDefault();
Assert.NotNull(classLine);
var methodLine = classLine.Children.Where(lines => lines.LineId.Contains("UndeleteBlobContainerAsync")).FirstOrDefault();
Assert.NotNull(methodLine);
Assert.Equal(23, methodLine.Tokens.Count);
Assert.Equal("public virtual Task<Response<BlobContainerClient>> UndeleteBlobContainerAsync(string deletedContainerName, string deletedContainerVersion, CancellationToken cancellationToken = default);", methodLine.ToString().Trim());
}

public static IEnumerable<object[]> PackageCodeFiles => new List<object[]>
{
new object[] { templateCodeFile },
new object[] { storageCodeFile },
new object[] { coreCodeFile }
};

[Theory]
[MemberData(nameof(PackageCodeFiles))]
public void TestAllClassesHaveEndOfContextLine(CodeFile codeFile)
{
// If current line is for class then next line at same level is expected to be a end of context line
var lines = codeFile.ReviewLines;
foreach(var namespaceLine in lines)
{
Assert.NotNull(namespaceLine);
bool expectEndOfContext = false;
var classLines = namespaceLine.Children;
for (int i = 0; i < classLines.Count; i++)
{
if (expectEndOfContext)
{
Assert.True(classLines[i].IsContextEndLine == true);
expectEndOfContext = false;
continue;
}

expectEndOfContext = classLines[i].Tokens.Any(t => (t.RenderClasses.Contains("class") ||
t.RenderClasses.Contains("struct") ||
t.RenderClasses.Contains("interface")) && !classLines[i].Tokens.Any(t => t.Value == "abstract"));
}
}
}

[Fact]
public void TestHiddenAPI()
{
var apiText = "protected static BlobServiceClient CreateClient(Uri serviceUri, BlobClientOptions options, HttpPipelinePolicy authentication, HttpPipeline pipeline);";
var lines = storageCodeFile.ReviewLines;
var namespaceLine = lines.Where(lines => lines.LineId == "Azure.Storage.Blobs").FirstOrDefault();
Assert.NotNull(namespaceLine);
var classLine = namespaceLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient").FirstOrDefault();
Assert.NotNull(classLine);
var hiddenApis = classLine.Children.Where(lines => lines.LineId == "Azure.Storage.Blobs.BlobServiceClient.CreateClient(System.Uri, Azure.Storage.Blobs.BlobClientOptions, Azure.Core.Pipeline.HttpPipelinePolicy, Azure.Core.Pipeline.HttpPipeline)").FirstOrDefault();
Assert.NotNull(hiddenApis);
Assert.Equal(18, hiddenApis.Tokens.Count());
Assert.Equal(apiText, hiddenApis.ToString().Trim());
}

[Fact]
Expand Down Expand Up @@ -77,29 +211,18 @@ public static class TemplateClientBuilderExtensions {
}
}
";
Assert.Equal(expected, codeFile.GetApiText());
Assert.Equal(expected, templateCodeFile.GetApiText());
}

[Fact]
public void TestCodeFileJsonSchema()
[Theory]
[MemberData(nameof(PackageCodeFiles))]
public void TestCodeFileJsonSchema(CodeFile codeFile)
{
//Verify JSON file generated for Azure.Template
var isValid = validateSchema(codeFile);
Assert.True(isValid);
}

[Fact]
public void TestCodeFileJsonSchema2()
{
//Verify JSON file generated for Azure.Storage.Blobs
var storageAssembly = Assembly.Load("Azure.Storage.Blobs");
var dllStream = storageAssembly.GetFile("Azure.Storage.Blobs.dll");
var assemblySymbol = CompilationFactory.GetCompilation(dllStream, null);
var storageCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, true, null);
var isValid = validateSchema(storageCodeFile);
Assert.True(isValid);
}

private bool validateSchema(CodeFile codeFile)
{
var json = JsonSerializer.Serialize(codeFile, new JsonSerializerOptions
Expand Down Expand Up @@ -129,9 +252,9 @@ private bool validateSchema(CodeFile codeFile)
}

[Fact]
public void TestNavigatonNodeHasRenderingClass()
public void TestNavigationNodeHasRenderingClass()
{
var jsonString = JsonSerializer.Serialize(codeFile);
var jsonString = JsonSerializer.Serialize(templateCodeFile);
var parsedCodeFile = JsonSerializer.Deserialize<CodeFile>(jsonString);
Assert.Equal(8, CountNavigationNodes(parsedCodeFile.ReviewLines));
}
Expand Down

0 comments on commit 4f63562

Please sign in to comment.