Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for nuget package with multiple Roslyn version analyzers #616

Merged
merged 11 commits into from
Mar 3, 2024
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
Loading