Skip to content

Commit

Permalink
Support for nuget package with multiple Roslyn version analyzers (#616)
Browse files Browse the repository at this point in the history
* Support for nuget package with multiple Roslyn version analyzers
* fix warnings + format code

---------

Co-authored-by: JoC0de <[email protected]>
  • Loading branch information
hadashiA and JoC0de authored Mar 3, 2024
1 parent 75f6822 commit 0218352
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 133 deletions.
83 changes: 76 additions & 7 deletions src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ public void InstallRoslynAnalyzerTest([Values] InstallMode installMode)
AssetDatabase.Refresh();
var path = $"Assets/Packages/{analyzer.Id}.{analyzer.Version}/analyzers/dotnet/cs/ErrorProne.NET.Core.dll";
var meta = (PluginImporter)AssetImporter.GetAtPath(path);

#if UNITY_2022_3_OR_NEWER
// somehow unity doesn't import the .dll on newer unity version
var postprocessor = new NugetAssetPostprocessor() { assetPath = path };
postprocessor.GetType().GetMethod("OnPreprocessAsset", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(postprocessor, null);
#endif

meta.SaveAndReimport();
AssetDatabase.Refresh();

Expand All @@ -107,8 +114,8 @@ public void InstallRoslynAnalyzerTest([Values] InstallMode installMode)
// Verify analyzer dll import settings
meta = AssetImporter.GetAtPath(path) as PluginImporter;
Assert.IsNotNull(meta, "Get meta file");
Assert.IsFalse(meta.GetCompatibleWithAnyPlatform(), "Not compatible any platform");
Assert.IsFalse(meta.GetCompatibleWithEditor(), "Not compatible editor");
Assert.IsFalse(meta.GetCompatibleWithAnyPlatform(), "Expected to have set compatible with any platform to false");
Assert.IsFalse(meta.GetCompatibleWithEditor(), "Expected to have set compatible with editor to false");
foreach (var platform in Enum.GetValues(typeof(BuildTarget)))
{
Assert.IsFalse(
Expand All @@ -130,6 +137,70 @@ public void InstallRoslynAnalyzerTest([Values] InstallMode installMode)
}
}

[Test]
[TestCase("2020.2.1f1", "")]
[TestCase("2021.2.1f1", "")] // no version selected because it only supports <= 3.8
[TestCase("2022.2.1f1", "4.0")]
[TestCase("2022.3.12f1", "4.0")]
public void InstallRoslynAnalyzerWithMultipleVersionsTest(string unityVersion, string expectedEnabledRoslynAnalyzerVersion)
{
var jsonPackageId = new NugetPackageIdentifier("System.Text.Json", "7.0.1");
var roslynAnalyzerVersions = new[] { "3.11", "4.0", "4.4" };

var unityVersionType = typeof(UnityVersion);
var currentUnityVersionProperty = unityVersionType.GetProperty(nameof(UnityVersion.Current), BindingFlags.Public | BindingFlags.Static);
Assume.That(currentUnityVersionProperty, Is.Not.Null);
Assume.That(currentUnityVersionProperty.CanRead, Is.True);
Assume.That(currentUnityVersionProperty.CanWrite, Is.True);

var oldUnityVersion = currentUnityVersionProperty.GetValue(null);
try
{
currentUnityVersionProperty.SetValue(null, new UnityVersion(unityVersion));
NugetPackageInstaller.InstallIdentifier(jsonPackageId);
AssetDatabase.Refresh();

foreach (var roslynAnalyzerVersion in roslynAnalyzerVersions)
{
var path = $"Assets/Packages/{jsonPackageId.Id}.{jsonPackageId.Version}/analyzers/dotnet/roslyn{roslynAnalyzerVersion}/cs/System.Text.Json.SourceGeneration.dll";
Assert.That(path, Does.Exist.IgnoreDirectories);
var meta = (PluginImporter)AssetImporter.GetAtPath(path);
meta.SaveAndReimport();

#if UNITY_2022_3_OR_NEWER
// somehow unity doesn't import the .dll on newer unity version
var postprocessor = new NugetAssetPostprocessor() { assetPath = path };
postprocessor.GetType().GetMethod("OnPreprocessAsset", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(postprocessor, null);
#endif
}

AssetDatabase.Refresh();

foreach (var roslynAnalyzerVersion in roslynAnalyzerVersions)
{
var path = $"Assets/Packages/{jsonPackageId.Id}.{jsonPackageId.Version}/analyzers/dotnet/roslyn{roslynAnalyzerVersion}/cs/System.Text.Json.SourceGeneration.dll";
Assert.That(path, Does.Exist.IgnoreDirectories);
var meta = (PluginImporter)AssetImporter.GetAtPath(path);
Assert.IsNotNull(meta, "Get meta file");
Assert.That(AssetDatabase.GetLabels(meta), Does.Contain("NuGetForUnity"));
Assert.IsFalse(meta.GetCompatibleWithAnyPlatform(), "Expected to have set compatible with any platform to false");
Assert.IsFalse(meta.GetCompatibleWithEditor(), "Expected to have set compatible with editor to false");
if (roslynAnalyzerVersion == expectedEnabledRoslynAnalyzerVersion)
{
Assert.That(AssetDatabase.GetLabels(meta), Does.Contain("RoslynAnalyzer"), $"DLL of Roslyn analyzer version '{roslynAnalyzerVersion}' should have 'RoslynAnalyzer' label");
}
else
{
Assert.That(AssetDatabase.GetLabels(meta), Does.Not.Contain("RoslynAnalyzer"), $"DLL of Roslyn analyzer version '{roslynAnalyzerVersion}' should not have 'RoslynAnalyzer' label");
}
}
}
finally
{
currentUnityVersionProperty.SetValue(null, oldUnityVersion);
}
}

[Test]
public void InstallProtobufTest([Values] InstallMode installMode)
{
Expand Down Expand Up @@ -615,10 +686,8 @@ public void TryGetBestTargetFrameworkForCurrentSettingsTest(
bool supportsNetStandard21,
bool supportsNet48)
{
var unityVersionType = typeof(TargetFrameworkResolver).GetNestedType("UnityVersion", BindingFlags.NonPublic);
Assume.That(unityVersionType, Is.Not.Null);

var currentUnityVersionProperty = unityVersionType.GetProperty("Current", BindingFlags.Public | BindingFlags.Static);
var unityVersionType = typeof(UnityVersion);
var currentUnityVersionProperty = unityVersionType.GetProperty(nameof(UnityVersion.Current), BindingFlags.Public | BindingFlags.Static);
Assume.That(currentUnityVersionProperty, Is.Not.Null);
Assume.That(currentUnityVersionProperty.CanRead, Is.True);
Assume.That(currentUnityVersionProperty.CanWrite, Is.True);
Expand All @@ -635,7 +704,7 @@ public void TryGetBestTargetFrameworkForCurrentSettingsTest(

try
{
currentUnityVersionProperty.SetValue(null, Activator.CreateInstance(unityVersionType, unityVersion));
currentUnityVersionProperty.SetValue(null, new UnityVersion(unityVersion));

var expectedCompatibilityLevel = useNetStandard ? ApiCompatibilityLevel.NET_Standard_2_0 : ApiCompatibilityLevel.NET_4_6;
currentBuildTargetApiCompatibilityLevelProperty.SetValue(null, new Lazy<ApiCompatibilityLevel>(() => expectedCompatibilityLevel));
Expand Down
173 changes: 173 additions & 0 deletions src/NuGetForUnity/Editor/Models/UnityVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
using UnityEngine;

namespace NugetForUnity.Models
{
/// <summary>
/// Represents a unity version.
/// </summary>
internal readonly struct UnityVersion : IComparable<UnityVersion>
{
private readonly int build;

private readonly int major;

private readonly int minor;

private readonly char release;

private readonly int revision;

/// <summary>
/// Initializes a new instance of the <see cref="UnityVersion" /> struct.
/// </summary>
/// <param name="major">Major version number.</param>
/// <param name="minor">Minor version number.</param>
/// <param name="revision">Revision number.</param>
/// <param name="release">Release flag. If 'f', official release. If 'p' patch release.</param>
/// <param name="build">Build number.</param>
public UnityVersion(int major, int minor, int revision, char release, int build)
{
this.major = major;
this.minor = minor;
this.revision = revision;
this.release = release;
this.build = build;
}

/// <summary>
/// Initializes a new instance of the <see cref="UnityVersion" /> struct.
/// </summary>
/// <param name="version">A string representation of Unity version.</param>
/// <exception cref="ArgumentException">Cannot parse version.</exception>
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local", Justification = "Called by Unit Test.")]
public UnityVersion(string version)
{
var match = Regex.Match(version, @"(\d+)\.(\d+)\.(\d+)([fpba])(\d+)");
if (!match.Success)
{
throw new ArgumentException("Invalid unity version");
}

major = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
minor = int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
revision = int.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture);
release = match.Groups[4].Value[0];
build = int.Parse(match.Groups[5].Value, CultureInfo.InvariantCulture);
}

/// <summary>
/// Gets current version from Application.unityVersion.
/// </summary>
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local", Justification = "Property setter needed for unit test")]
public static UnityVersion Current { get; private set; } = new UnityVersion(Application.unityVersion);

/// <summary>
/// Checks to see if the left <see cref="UnityVersion" /> is less than the right.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>True if left is less than the right.</returns>
public static bool operator <(in UnityVersion left, in UnityVersion right)
{
return left.CompareTo(right) < 0;
}

/// <summary>
/// Checks to see if the left <see cref="UnityVersion" /> is less than or equal to the right.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>True if left is less than or equal to the right.</returns>
public static bool operator <=(in UnityVersion left, in UnityVersion right)
{
return left.CompareTo(right) <= 0;
}

/// <summary>
/// Checks to see if the left <see cref="UnityVersion" /> is greater than the right.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>True if left is greater than the right.</returns>
public static bool operator >(in UnityVersion left, in UnityVersion right)
{
return left.CompareTo(right) > 0;
}

/// <summary>
/// Checks to see if the left <see cref="UnityVersion" /> is greater than or equal to the right.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>True if left is greater than or equal to the right.</returns>
public static bool operator >=(in UnityVersion left, in UnityVersion right)
{
return left.CompareTo(right) >= 0;
}

/// <inheritdoc />
public int CompareTo(UnityVersion other)
{
return Compare(this, other);
}

private static int Compare(in UnityVersion a, in UnityVersion b)
{
if (a.major < b.major)
{
return -1;
}

if (a.major > b.major)
{
return 1;
}

if (a.minor < b.minor)
{
return -1;
}

if (a.minor > b.minor)
{
return 1;
}

if (a.revision < b.revision)
{
return -1;
}

if (a.revision > b.revision)
{
return 1;
}

if (a.release < b.release)
{
return -1;
}

if (a.release > b.release)
{
return 1;
}

if (a.build < b.build)
{
return -1;
}

if (a.build > b.build)
{
return 1;
}

return 0;
}
}
}
3 changes: 3 additions & 0 deletions src/NuGetForUnity/Editor/Models/UnityVersion.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0218352

Please sign in to comment.