Skip to content

Commit a9e5db0

Browse files
authored
Merge pull request bdovaz#222 from bucurb/master
Handle nuget packages with per-platform runtimes
2 parents 614b8d5 + 6f664d8 commit a9e5db0

8 files changed

Lines changed: 1037 additions & 161 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using NUnit.Framework;
6+
7+
namespace UnityNuGet.Tests
8+
{
9+
public class PlatformDefinitionTests
10+
{
11+
[Test]
12+
public void CanFindDefinitions()
13+
{
14+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
15+
16+
// Look-up by OS should return the most general configuration
17+
var win = platformDefs.Find(UnityOs.Windows);
18+
Assert.IsNotNull(win);
19+
Assert.AreEqual(win.Cpu, UnityCpu.AnyCpu);
20+
21+
// Look-up explicit configuration
22+
var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64);
23+
Assert.IsNotNull(win64);
24+
Assert.AreEqual(win64.Os, win.Os);
25+
Assert.AreEqual(win64.Cpu, UnityCpu.X64);
26+
Assert.True(win.Children.Contains(win64));
27+
28+
// Look-up invalid configuration
29+
var and = platformDefs.Find(UnityOs.Android, UnityCpu.None);
30+
Assert.IsNull(and);
31+
}
32+
33+
[Test]
34+
public void RemainingPlatforms_NoneVisited()
35+
{
36+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
37+
var visited = new HashSet<PlatformDefinition>();
38+
39+
// If no platform was visited, the remaining platforms should be the (AnyOS, AnyCPU) config.
40+
var remaining = platformDefs.GetRemainingPlatforms(visited);
41+
Assert.IsNotNull(remaining);
42+
Assert.AreEqual(1, remaining.Count);
43+
Assert.AreEqual(remaining.First(), platformDefs);
44+
}
45+
46+
[Test]
47+
public void RemainingPlatforms_OneVisited()
48+
{
49+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
50+
51+
foreach (var child in platformDefs.Children)
52+
{
53+
var visited = new HashSet<PlatformDefinition>() { child };
54+
var remaining = platformDefs.GetRemainingPlatforms(visited);
55+
56+
// We should get all other children, except the one already visited
57+
Assert.AreEqual(platformDefs.Children.Count, remaining.Count + 1);
58+
foreach (var r in remaining)
59+
{
60+
Assert.AreNotEqual(r, child);
61+
Assert.IsTrue(platformDefs.Children.Contains(r));
62+
}
63+
}
64+
}
65+
66+
[Test]
67+
public void RemainingPlatforms_LeafVisited()
68+
{
69+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
70+
var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64);
71+
var visited = new HashSet<PlatformDefinition>() { win64 };
72+
73+
// The remaining platforms should be all non-windows, as well as all !x64 windows
74+
var expected = platformDefs.Children
75+
.Except(new[] { win64.Parent })
76+
.Concat(
77+
win64.Parent.Children
78+
.Except(new[] { win64 }))
79+
.ToHashSet();
80+
var actual = platformDefs.GetRemainingPlatforms(visited);
81+
Assert.IsTrue(expected.SetEquals(actual));
82+
}
83+
84+
[TestCase("")]
85+
[TestCase("base")]
86+
public void TestConfigPath_Root(string basePath)
87+
{
88+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
89+
var file = new PlatformFile("a/b/c.dll", platformDefs);
90+
91+
// We don't use extra paths for the (AnyOS, AnyCPU) configuration
92+
var actual = file.GetDestinationPath(basePath);
93+
var expected = Path.Combine(
94+
basePath,
95+
Path.GetFileName(file.SourcePath));
96+
Assert.AreEqual(actual, expected);
97+
}
98+
99+
[TestCase("")]
100+
[TestCase("base")]
101+
public void TestConfigPath_OsOnly(string basePath)
102+
{
103+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
104+
var win = platformDefs.Find(UnityOs.Windows);
105+
var file = new PlatformFile("a/b/c.dll", win);
106+
107+
var actual = file.GetDestinationPath(basePath);
108+
var expected = Path.Combine(
109+
basePath,
110+
"Windows",
111+
Path.GetFileName(file.SourcePath));
112+
Assert.AreEqual(actual, expected);
113+
}
114+
115+
[TestCase("")]
116+
[TestCase("base")]
117+
public void TestConfigPath_Full(string basePath)
118+
{
119+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
120+
var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64);
121+
var file = new PlatformFile("a/b/c.dll", win64);
122+
123+
var actual = file.GetDestinationPath(basePath);
124+
var expected = Path.Combine(
125+
basePath,
126+
"Windows",
127+
"x86_64",
128+
Path.GetFileName(file.SourcePath));
129+
Assert.AreEqual(actual, expected);
130+
}
131+
}
132+
}

src/UnityNuGet.Tests/RegistryTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using NuGet.Protocol.Core.Types;
1111
using NUnit.Framework;
1212
using static NuGet.Frameworks.FrameworkConstants;
13+
using System.IO;
1314

1415
namespace UnityNuGet.Tests
1516
{
@@ -34,6 +35,62 @@ public void Ensure_That_Packages_Already_Included_In_Net_Standard_Are_not_Includ
3435
Assert.IsEmpty(packageNames);
3536
}
3637

38+
[Test]
39+
public async Task CanParse_PackageWithRuntimes()
40+
{
41+
var logger = NullLogger.Instance;
42+
var cancellationToken = CancellationToken.None;
43+
44+
var cache = new SourceCacheContext();
45+
var settings = Settings.LoadDefaultSettings(root: null);
46+
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
47+
48+
// Fetch a package that has runtime overrides as described here: https://learn.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks
49+
var downloadResult = await PackageDownloader.GetDownloadResourceResultAsync(
50+
new SourceRepository[] { repository },
51+
new PackageIdentity("System.Security.Cryptography.ProtectedData", new NuGet.Versioning.NuGetVersion(6, 0, 0)),
52+
new PackageDownloadContext(cache),
53+
SettingsUtility.GetGlobalPackagesFolder(settings),
54+
logger, cancellationToken);
55+
56+
// Make sure we have runtime libraries
57+
var runtimeLibs = await RuntimeLibraries
58+
.GetSupportedRuntimeLibsAsync(downloadResult.PackageReader, CommonFrameworks.NetStandard20, logger)
59+
.ToListAsync();
60+
Assert.IsTrue(runtimeLibs.Any());
61+
62+
// Make sure these runtime libraries are only for Windows
63+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
64+
var win = platformDefs.Find(UnityOs.Windows);
65+
foreach (var (file, os, cpu) in runtimeLibs)
66+
{
67+
Assert.AreEqual(platformDefs.Find(os, cpu), win);
68+
}
69+
70+
// Get the lib files
71+
var versions = await downloadResult.PackageReader.GetLibItemsAsync(cancellationToken);
72+
var closestVersions = NuGetHelper.GetClosestFrameworkSpecificGroups(
73+
versions,
74+
new RegistryTargetFramework[]
75+
{
76+
new RegistryTargetFramework
77+
{
78+
Framework = CommonFrameworks.NetStandard20,
79+
},
80+
});
81+
var libFiles = closestVersions
82+
.Single()
83+
.Item1.Items
84+
.Select(i => Path.GetFileName(i))
85+
.ToHashSet();
86+
87+
// Make sure the runtime files fully replace the lib files (note that this is generally not a requirement)
88+
var runtimeFiles = runtimeLibs
89+
.Select(l => Path.GetFileName(l.file))
90+
.ToHashSet();
91+
Assert.IsTrue(libFiles.SetEquals(runtimeFiles));
92+
}
93+
3794
[Test]
3895
public async Task Ensure_Min_Version_Is_Correct_Ignoring_Analyzers_And_Native_Libs()
3996
{

src/UnityNuGet.Tests/UnityMetaTests.cs

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
24
using NUnit.Framework;
35

46
namespace UnityNuGet.Tests
@@ -8,7 +10,9 @@ public class UnityMetaTests
810
[Test]
911
public void GetMetaForDll_FormatsDefineConstraintsProperly_WithoutConstraints()
1012
{
11-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), true, Array.Empty<string>(), Array.Empty<string>());
13+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
14+
var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
15+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty<string>(), Array.Empty<string>());
1216
StringAssert.DoesNotContain("defineConstraints", output);
1317

1418
// This is on the same line in the template, so ensure it's intact
@@ -18,7 +22,9 @@ public void GetMetaForDll_FormatsDefineConstraintsProperly_WithoutConstraints()
1822
[Test]
1923
public void GetMetaForDll_FormatsLabelsProperly_WithoutLabels()
2024
{
21-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), true, Array.Empty<string>(), Array.Empty<string>());
25+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
26+
var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
27+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty<string>(), Array.Empty<string>());
2228
StringAssert.DoesNotContain("labels", output);
2329

2430
// This is on the same line in the template, so ensure it's intact
@@ -30,7 +36,9 @@ public void GetMetaForDll_FormatsLabelsProperly_WithoutLabels()
3036
public void GetMetaForDll_FormatsDefineConstraintsProperly_WithConstraints(
3137
string[] constraints, string expected)
3238
{
33-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), true, Array.Empty<string>(), constraints);
39+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
40+
var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
41+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty<string>(), constraints);
3442

3543
StringAssert.Contains(expected, output);
3644

@@ -43,7 +51,9 @@ public void GetMetaForDll_FormatsDefineConstraintsProperly_WithConstraints(
4351
public void GetMetaForDll_FormatsLabelsProperly_WithLabels(
4452
string[] labels, string expected)
4553
{
46-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), true, labels, Array.Empty<string>());
54+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
55+
var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
56+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, labels, Array.Empty<string>());
4757

4858
StringAssert.Contains(expected, output);
4959

@@ -55,16 +65,98 @@ public void GetMetaForDll_FormatsLabelsProperly_WithLabels(
5565
[TestCase(false, "0")]
5666
public void GetMetaForDll_FormatsAnyPlatformEnabledProperly(bool value, string expected)
5767
{
58-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), value, Array.Empty<string>(), Array.Empty<string>());
68+
PlatformDefinition platformDef;
69+
70+
if (value)
71+
{
72+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
73+
platformDef = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
74+
}
75+
else
76+
{
77+
platformDef = new PlatformDefinition(UnityOs.AnyOs, UnityCpu.None, isEditorConfig: false);
78+
}
79+
80+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), platformDef, Array.Empty<string>(), Array.Empty<string>());
5981

6082
StringAssert.Contains($"\n platformData:\n - first:\n Any:\n second:\n enabled: {expected}\n", output);
6183
}
6284

6385
[Test]
6486
public void GetMetaForDll_ContainsNoWindowsNewlines()
6587
{
66-
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), true, Array.Empty<string>(), new[] { "TEST" });
88+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
89+
var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu);
90+
var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty<string>(), new[] { "TEST" });
6791
StringAssert.DoesNotContain("\r", output);
6892
}
93+
94+
[TestCase(UnityOs.Android, "Android", "Android")]
95+
[TestCase(UnityOs.WebGL, "WebGL", "WebGL")]
96+
[TestCase(UnityOs.iOS, "iPhone", "iOS")]
97+
public void GetMetaForDll_NonEditor(UnityOs os, string platformName, string osName)
98+
{
99+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
100+
var output = UnityMeta.GetMetaForDll(
101+
Guid.NewGuid(),
102+
platformDefs.Find(os),
103+
Array.Empty<string>(),
104+
Array.Empty<string>());
105+
106+
// There should be a single 'Exclude Android: 0' match
107+
var excludeRegex = new Regex("Exclude (.*): 0");
108+
var excludeMatches = excludeRegex.Matches(output);
109+
Assert.IsNotNull(excludeMatches);
110+
Assert.AreEqual(excludeMatches.Count, 1);
111+
Assert.AreEqual(excludeMatches.Single().Groups.Count, 2);
112+
Assert.AreEqual(excludeMatches.Single().Groups[1].Value, osName);
113+
114+
// There should be a single 'enabled: 1' match
115+
var enableRegex = new Regex("enabled: 1");
116+
var enableMatches = enableRegex.Matches(output);
117+
Assert.IsNotNull(enableMatches);
118+
Assert.AreEqual(enableMatches.Count, 1);
119+
120+
StringAssert.Contains($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n", output);
121+
}
122+
123+
[TestCase(UnityOs.Windows, new[] { "Win", "Win64" })]
124+
[TestCase(UnityOs.Linux, new[] { "Linux64" })]
125+
[TestCase(UnityOs.OSX, new[] { "OSXUniversal" })]
126+
public void GetMetaForDll_Editor(UnityOs os, string[] osNames)
127+
{
128+
var platformDefs = PlatformDefinition.CreateAllPlatforms();
129+
var pDef = platformDefs.Find(os);
130+
var output = UnityMeta.GetMetaForDll(
131+
Guid.NewGuid(),
132+
pDef,
133+
Array.Empty<string>(),
134+
Array.Empty<string>());
135+
136+
// There should be only 'Exclude Editor: 0' and 'Exclude {{ osName }}: 0' matches
137+
var excludeRegex = new Regex("Exclude (.*): 0");
138+
var excludeMatches = excludeRegex.Matches(output);
139+
Assert.IsNotNull(excludeMatches);
140+
var actualExcludes = excludeMatches
141+
.Select(match => match.Groups[1].Value)
142+
.ToHashSet();
143+
144+
var expectedExcludes = osNames
145+
.Append("Editor")
146+
.ToHashSet();
147+
Assert.IsTrue(actualExcludes.SetEquals(expectedExcludes));
148+
149+
// There should be as many 'enabled: 1' matches as exclude matches
150+
var enableRegex = new Regex("enabled: 1");
151+
var enableMatches = enableRegex.Matches(output);
152+
Assert.IsNotNull(enableMatches);
153+
Assert.AreEqual(enableMatches.Count, excludeMatches.Count);
154+
155+
foreach (var osName in actualExcludes)
156+
{
157+
var platformName = (osName == "Editor") ? osName : "Standalone";
158+
StringAssert.Contains($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n", output);
159+
}
160+
}
69161
}
70162
}

0 commit comments

Comments
 (0)