Skip to content

Commit

Permalink
Testing setup
Browse files Browse the repository at this point in the history
  • Loading branch information
martinothamar committed May 24, 2024
1 parent 6d558e9 commit ec604a7
Show file tree
Hide file tree
Showing 52 changed files with 2,116 additions and 95 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ csharp_style_expression_bodied_local_functions = false:silent
csharp_indent_labels = one_less_than_current
csharp_style_prefer_primary_constructors = false:suggestion

# For https://github.com/shuebner/ClosedTypeHierarchyDiagnosticSuppressor
dotnet_diagnostic.CTH001.suppress_on_record_hierarchies = true

# CA1848: Use the LoggerMessage delegates
dotnet_diagnostic.CA1848.severity = none

Expand Down
7 changes: 7 additions & 0 deletions AppLibDotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Altinn.App.Core", "src\Alti
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.App.Analyzers", "src\Altinn.App.Analyzers\Altinn.App.Analyzers.csproj", "{9F956488-F123-48A2-B21A-2C2918E8B416}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.App.Analyzers.Tests", "test\Altinn.App.Analyzers.Tests\Altinn.App.Analyzers.Tests.csproj", "{B7E436DE-AF76-4979-9837-4243BAD94281}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -46,6 +48,10 @@ Global
{9F956488-F123-48A2-B21A-2C2918E8B416}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F956488-F123-48A2-B21A-2C2918E8B416}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F956488-F123-48A2-B21A-2C2918E8B416}.Release|Any CPU.Build.0 = Release|Any CPU
{B7E436DE-AF76-4979-9837-4243BAD94281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7E436DE-AF76-4979-9837-4243BAD94281}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7E436DE-AF76-4979-9837-4243BAD94281}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7E436DE-AF76-4979-9837-4243BAD94281}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -56,6 +62,7 @@ Global
{2FD56505-1DB2-4AE1-8911-E076E535EAC6} = {6C8EB054-1747-4BAC-A637-754F304BCAFA}
{1745B251-BD5C-43B7-BA7D-9C4BFAB37535} = {7AD5FADE-607F-4D5F-8511-6647D0C1AA1C}
{9F956488-F123-48A2-B21A-2C2918E8B416} = {7AD5FADE-607F-4D5F-8511-6647D0C1AA1C}
{B7E436DE-AF76-4979-9837-4243BAD94281} = {6C8EB054-1747-4BAC-A637-754F304BCAFA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4584C6E1-D5B4-40B1-A8C4-CF4620EB0896}
Expand Down
16 changes: 14 additions & 2 deletions src/Altinn.App.Analyzers/Altinn.App.Analyzers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<NoPackageAnalysis>true</NoPackageAnalysis>
<DevelopmentDependency>true</DevelopmentDependency>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand All @@ -22,11 +23,22 @@
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
</ItemGroup>

<!--
NOTE:
InternalsVisibleTo messes with PolySharp, which we use to support newer C# features in analyzer code (such as records)
So it is disabled currently and relevant members sin analyzer code is public instead
-->
<!-- <ItemGroup>
<InternalsVisibleTo Include="Altinn.App.Analyzers.Tests" />
</ItemGroup> -->

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Pack="false" PrivateAssets="all" Version="4.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" PrivateAssets="all" Version="4.1.0" />
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageReference Include="SvSoft.Analyzers.ClosedTypeHierarchyDiagnosticSuppression" Version="1.1.1" PrivateAssets="All" />

<PackageReference Include="Newtonsoft.Json" Pack="false" GeneratePathProperty="true" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" GeneratePathProperty="true" Version="13.0.3" />
<None Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

Expand Down
5 changes: 3 additions & 2 deletions src/Altinn.App.Analyzers/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Rule ID | Category | Severity | Notes
--------|----------|----------|-------
ALTINN001 | General | Warning | Project not found
ALTINN002 | Metadata | Warning | App metadata file not found
ALTINN003 | Metadata | Warning | App metadata file parsing
ALTINN004 | Metadata | Warning | Classref not resolved
ALTINN003 | Metadata | Warning | App metadata file could not be read
ALTINN004 | Metadata | Warning | App metadata file parsing
ALTINN005 | Metadata | Warning | Classref not resolved
ALTINN999 | General | Warning | Unknown error
60 changes: 60 additions & 0 deletions src/Altinn.App.Analyzers/ApplicationMetadataFileReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Text;
using Microsoft.CodeAnalysis.Text;

namespace Altinn.App.Analyzers;

public abstract record ApplicationMetadataResult
{
private ApplicationMetadataResult() { }

public sealed record Content(ApplicationMetadata Value, SourceText SourceText, string FilePath)
: ApplicationMetadataResult;

public sealed record FileNotFound(string FilePath) : ApplicationMetadataResult;

public sealed record CouldNotReadFile(Exception Exception, string FilePath) : ApplicationMetadataResult;

public sealed record CouldNotParse(Exception Exception, SourceText SourceText, string FilePath)
: ApplicationMetadataResult;
}

public static class ApplicationMetadataFileReader
{
public static readonly string ApplicationMetadataFilePath = Path.Combine("config", "applicationmetadata.json");

public static ApplicationMetadataResult Read(in MetadataAnalyzerContext context)
{
var file = Path.Combine(context.ProjectDir, ApplicationMetadataFilePath);

SourceText sourceText;
string jsonText;
try
{
context.OnApplicationMetadataReadBefore?.Invoke();

if (!File.Exists(file))
return new ApplicationMetadataResult.FileNotFound(file);

jsonText = File.ReadAllText(file, Encoding.UTF8);
sourceText = SourceText.From(jsonText, Encoding.UTF8);
}
catch (Exception ex)
{
return new ApplicationMetadataResult.CouldNotReadFile(ex, file);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

ApplicationMetadata metadata;
try
{
context.OnApplicationMetadataDeserializationBefore?.Invoke();

metadata = ApplicationMetadataJsonReader.Read(jsonText);
}
catch (Exception ex)
{
return new ApplicationMetadataResult.CouldNotParse(ex, sourceText, file);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

return new ApplicationMetadataResult.Content(metadata, sourceText, file);
}
}
71 changes: 71 additions & 0 deletions src/Altinn.App.Analyzers/ApplicationMetadataJsonReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Altinn.App.Analyzers;

public readonly record struct ParsedJsonValue<T>(T Value, IJsonLineInfo LineInfo);

public sealed record ApplicationMetadata(ParsedJsonValue<IReadOnlyList<DataTypeInfo>> DataTypes);

public sealed record DataTypeInfo(ParsedJsonValue<string> Id, ParsedJsonValue<AppLogicInfo>? AppLogic);

public sealed record AppLogicInfo(ParsedJsonValue<string> ClassRef);

public static class ApplicationMetadataJsonReader
{
public static ApplicationMetadata Read(string json)
{
var dataTypeInfo = new List<DataTypeInfo>();

using var reader = new JsonTextReader(new StringReader(json));

JsonLoadSettings loadSettings = new JsonLoadSettings()
{
CommentHandling = CommentHandling.Ignore,
LineInfoHandling = LineInfoHandling.Load
};

var metadata = JObject.Load(reader, loadSettings);
var dataTypesArr = metadata.GetValue("dataTypes") as JArray;
if (dataTypesArr is null)
throw new JsonException("Failed to parse 'dataTypes'");
var dataTypes = dataTypesArr.ReadWithLineInfo((IReadOnlyList<DataTypeInfo>)dataTypeInfo, "dataTypes");

foreach (var dataTypeToken in dataTypesArr)
{
var dataType = (dataTypeToken as JObject) ?? throw new JsonException("Failed to parse 'dataType'");

var dataTypeId = dataType.GetStringValue("id");

var appLogicObj = dataType.GetValue("appLogic") as JObject;
if (appLogicObj is null)
{
dataTypeInfo.Add(new DataTypeInfo(dataTypeId, AppLogic: null));
continue;
}

var classRef = appLogicObj.GetStringValue("classRef");
var appLogic = appLogicObj.ReadWithLineInfo(new AppLogicInfo(classRef), "appLogic");
dataTypeInfo.Add(new DataTypeInfo(dataTypeId, appLogic));
}

return new ApplicationMetadata(dataTypes);
}

private static ParsedJsonValue<string> GetStringValue(this JObject obj, string propertyName)
{
var token = obj.GetValue(propertyName) ?? throw new JsonException($"Failed to parse '{propertyName}'");
var jvalue = (token as JValue) ?? throw new JsonException($"Failed to parse '{propertyName}'");
var value = (jvalue.Value as string) ?? throw new JsonException($"Failed to parse '{propertyName}'");

return jvalue.ReadWithLineInfo(value, propertyName);
}

private static ParsedJsonValue<T> ReadWithLineInfo<T>(this JToken token, T value, string propertyName)
{
if (token is not IJsonLineInfo lineInfo || !lineInfo.HasLineInfo())
throw new JsonException($"Could not read line info while parsing '{propertyName}'");

return new ParsedJsonValue<T>(value, lineInfo);
}
}
31 changes: 19 additions & 12 deletions src/Altinn.App.Analyzers/Diagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,57 @@

namespace Altinn.App.Analyzers;

internal static class Diagnostics
public static class Diagnostics
{
internal static readonly DiagnosticDescriptor UnknownError = Warning(
public static readonly DiagnosticDescriptor UnknownError = Warning(
"ALTINN999",
Category.General,
"Unknown analyzer error",
"Unknown error occurred during analysis: '{0}' {1}"
"Unknown error occurred during analysis, contact support: '{0}' {1}"
);

internal static readonly DiagnosticDescriptor ProjectNotFound = Warning(
public static readonly DiagnosticDescriptor ProjectNotFound = Warning(
"ALTINN001",
Category.General,
"Altinn app project not found",
"While starting analysis, we couldn't find the project directory - contact support"
);

internal static readonly DiagnosticDescriptor ApplicationMetadataFileNotFound = Warning(
public static readonly DiagnosticDescriptor ApplicationMetadataFileNotFound = Warning(
"ALTINN002",
Category.Metadata,
"Altinn app metadata file not found",
"Could not find application metadata file at 'config/applicationmetadata.json'"
"Could not find application metadata file at '{0}'"
);

internal static readonly DiagnosticDescriptor FailedToParseApplicationMetadata = Warning(
public static readonly DiagnosticDescriptor ApplicationMetadataFileNotReadable = Warning(
"ALTINN003",
Category.Metadata,
"Altinn app metadata file couldn't be parsed",
"Could not parse application metadata file at 'config/applicationmetadata.json': '{0}' {1}"
"Altinn app metadata file could not be opened/read",
"Could not open and read the application metdata file: '{0}' {1}"
);

internal static readonly DiagnosticDescriptor DataTypeClassRefInvalid = Warning(
public static readonly DiagnosticDescriptor FailedToParseApplicationMetadata = Warning(
"ALTINN004",
Category.Metadata,
"Altinn app metadata file couldn't be parsed",
"Could not parse application metadata file: '{0}' {1}"
);

public static readonly DiagnosticDescriptor DataTypeClassRefInvalid = Warning(
"ALTINN005",
Category.Metadata,
"Data type class reference could not be found",
"Class reference '{0}' for data type '{1}' could not be found"
);

internal static readonly ImmutableArray<DiagnosticDescriptor> All;
public static readonly ImmutableArray<DiagnosticDescriptor> All;

static Diagnostics()

Check warning on line 51 in src/Altinn.App.Analyzers/Diagnostics.cs

View workflow job for this annotation

GitHub Actions / Static code analysis

Initialize all 'static fields' inline and remove the 'static constructor'. (https://rules.sonarsource.com/csharp/RSPEC-3963)
{
All = ImmutableArray.CreateRange(
typeof(Diagnostics)
.GetFields(BindingFlags.NonPublic | BindingFlags.Static)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.FieldType == typeof(DiagnosticDescriptor))
.Select(f => (DiagnosticDescriptor)f.GetValue(null))
);
Expand Down
Loading

0 comments on commit ec604a7

Please sign in to comment.