Skip to content

Commit 8cb9469

Browse files
Add support for using different editions of .NET in unit tests
1 parent a153182 commit 8cb9469

File tree

10 files changed

+136
-75
lines changed

10 files changed

+136
-75
lines changed

ICSharpCode.Decompiler.Tests/Helpers/RoslynToolset.cs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@
1818

1919
using System;
2020
using System.Collections.Generic;
21+
using System.Diagnostics;
2122
using System.IO;
2223
using System.Linq;
2324
using System.Threading;
2425
using System.Threading.Tasks;
2526

26-
using ICSharpCode.Decompiler.Util;
27+
using ICSharpCode.Decompiler.Metadata;
2728

2829
using NuGet.Common;
2930
using NuGet.Packaging;
3031
using NuGet.Protocol;
3132
using NuGet.Protocol.Core.Types;
32-
using NuGet.Versioning;
33+
34+
using NUnit.Framework;
3335

3436
namespace ICSharpCode.Decompiler.Tests.Helpers
3537
{
@@ -50,17 +52,12 @@ public AbstractToolset(string baseDir)
5052

5153
protected async Task FetchPackage(string packageName, string version, string sourcePath, string outputPath)
5254
{
55+
if (!Directory.Exists(Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "nuget")))
56+
Assert.Fail("No nuget cache found!");
57+
5358
ILogger logger = NullLogger.Instance;
5459
CancellationToken cancellationToken = CancellationToken.None;
55-
using MemoryStream packageStream = new MemoryStream();
56-
57-
await resource.CopyNupkgToStreamAsync(
58-
packageName,
59-
NuGetVersion.Parse(version),
60-
packageStream,
61-
cache,
62-
logger,
63-
cancellationToken).ConfigureAwait(false);
60+
using var packageStream = File.OpenRead(Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "nuget", $"{packageName}-{version}.nupkg"));
6461

6562
using PackageArchiveReader packageReader = new PackageArchiveReader(packageStream);
6663
NuspecReader nuspecReader = await packageReader.GetNuspecReaderAsync(cancellationToken).ConfigureAwait(false);
@@ -145,4 +142,44 @@ public async Task Fetch()
145142

146143
public string GetVsWhere() => vswherePath;
147144
}
145+
146+
class RefAssembliesToolset : AbstractToolset
147+
{
148+
readonly Dictionary<string, string> installedFrameworks = new Dictionary<string, string> {
149+
{ "legacy", Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "dotnet", "legacy") },
150+
{ "2.2.0", Path.Combine(Roundtrip.RoundtripAssembly.TestDir, "dotnet", "netcore-2.2") },
151+
};
152+
153+
public RefAssembliesToolset()
154+
: base(Path.Combine(AppContext.BaseDirectory, "netfx"))
155+
{
156+
}
157+
158+
public async Task Fetch(string version, string packageName = "Microsoft.NETCore.App.Ref", string sourcePath = "ref/net5.0")
159+
{
160+
string path = Path.Combine(baseDir, version, sourcePath);
161+
if (!Directory.Exists(path))
162+
{
163+
await FetchPackage(packageName, version, sourcePath, Path.Combine(baseDir, version)).ConfigureAwait(false);
164+
}
165+
166+
installedFrameworks.Add(RoslynToolset.SanitizeVersion(version), path);
167+
}
168+
169+
internal string GetPath(string targetFramework)
170+
{
171+
var (id, version) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework);
172+
string path;
173+
if (id == TargetFrameworkIdentifier.NETFramework)
174+
{
175+
path = installedFrameworks["legacy"];
176+
}
177+
else
178+
{
179+
path = installedFrameworks[version.ToString(3)];
180+
}
181+
Debug.Assert(Path.Exists(path));
182+
return path;
183+
}
184+
}
148185
}

ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818
using System;
19-
using System.CodeDom.Compiler;
2019
using System.Collections.Generic;
21-
using System.Diagnostics;
2220
using System.IO;
2321
using System.Linq;
24-
using System.Text;
2522
using System.Text.RegularExpressions;
2623
using System.Threading.Tasks;
2724

@@ -49,27 +46,30 @@ public static async Task<CompilerResults> CompileVB(string sourceFileName, Compi
4946
CompilerResults results = new CompilerResults();
5047
results.PathToAssembly = outputFileName;
5148

52-
var (roslynVersion, languageVersion) = (flags & CompilerOptions.UseRoslynMask) switch {
53-
0 => ("legacy", "11"),
54-
CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "14"),
55-
CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest"),
56-
CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest"),
57-
_ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest")
49+
bool targetNet40 = (flags & CompilerOptions.TargetNet40) == 0;
50+
51+
var (roslynVersion, languageVersion, targetFramework) = (flags & CompilerOptions.UseRoslynMask) switch {
52+
0 => ("legacy", "11", null),
53+
CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "14", null),
54+
CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v2.2"),
55+
CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v5.0"),
56+
_ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest", targetNet40 ? null : ".NETCoreApp,Version=v10.0")
5857
};
5958

6059
var vbcPath = roslynToolset.GetVBCompiler(roslynVersion);
6160

6261
IEnumerable<string> references;
6362
string libPath;
64-
if ((flags & CompilerOptions.UseRoslynMask) != 0 && (flags & CompilerOptions.TargetNet40) == 0)
63+
if ((flags & CompilerOptions.UseRoslynMask) != 0 && targetFramework != null)
6564
{
65+
var coreRefAsmPath = RefAssembliesToolset.GetPath(targetFramework);
6666
references = coreDefaultReferences.Select(r => "-r:\"" + r + "\"");
6767
libPath = coreRefAsmPath;
6868
}
6969
else
7070
{
7171
references = defaultReferences.Select(r => "-r:\"" + r + "\"");
72-
libPath = RefAsmPath;
72+
libPath = RefAssembliesToolset.GetPath("legacy");
7373
}
7474
if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic))
7575
{

ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public static partial class Tester
9898
static readonly string roslynLatestVersion;
9999
static readonly RoslynToolset roslynToolset;
100100
static readonly VsWhereToolset vswhereToolset;
101+
internal static readonly RefAssembliesToolset RefAssembliesToolset;
101102

102103
static Tester()
103104
{
@@ -119,6 +120,7 @@ static Tester()
119120

120121
roslynToolset = new RoslynToolset();
121122
vswhereToolset = new VsWhereToolset();
123+
RefAssembliesToolset = new RefAssembliesToolset();
122124
}
123125

124126
internal static async Task Initialize()
@@ -129,6 +131,9 @@ internal static async Task Initialize()
129131
await roslynToolset.Fetch(roslynLatestVersion).ConfigureAwait(false);
130132

131133
await vswhereToolset.Fetch().ConfigureAwait(false);
134+
await RefAssembliesToolset.Fetch("5.0.0", sourcePath: "ref/net5.0").ConfigureAwait(false);
135+
await RefAssembliesToolset.Fetch("10.0.0-preview.4.25258.110", sourcePath: "ref/net10.0").ConfigureAwait(false);
136+
132137

133138
#if DEBUG
134139
await BuildTestRunner("win-x86", "Debug").ConfigureAwait(false);
@@ -275,21 +280,14 @@ private static string ReplacePrivImplDetails(string il)
275280
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
276281
}
277282

278-
static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NET,
279-
new Version(10, 0), "Microsoft.NETCore.App")
280-
.GetReferenceAssemblyPath(".NETCoreApp,Version=v10.0");
281-
282-
public static readonly string RefAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
283-
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
284-
285283
static readonly string[] defaultReferences = new[] {
286284
"System.dll",
287285
"System.Core.dll",
288286
"System.Xml.dll",
289287
"Microsoft.CSharp.dll"
290288
};
291289

292-
static readonly string[] coreDefaultReferences = new[]
290+
static readonly string[] core220DefaultReferences = new[]
293291
{
294292
"netstandard.dll",
295293
"mscorlib.dll",
@@ -302,9 +300,12 @@ private static string ReplacePrivImplDetails(string il)
302300
"System.Linq.Queryable.dll",
303301
"System.IO.FileSystem.Watcher.dll",
304302
"System.Memory.dll",
303+
"System.Private.CoreLib.dll",
304+
"System.Private.Xml.dll",
305305
"System.Threading.dll",
306306
"System.Threading.Thread.dll",
307307
"System.Runtime.dll",
308+
"System.Runtime.Extensions.dll",
308309
"System.Runtime.InteropServices.dll",
309310
"System.Xml.dll",
310311
"System.Xml.ReaderWriter.dll",
@@ -313,19 +314,45 @@ private static string ReplacePrivImplDetails(string il)
313314
"Microsoft.VisualBasic.dll",
314315
};
315316

316-
const string targetFrameworkAttributeSnippet = @"
317-
318-
[assembly: System.Runtime.Versioning.TargetFramework("".NETCoreApp,Version=v10.0"", FrameworkDisplayName = """")]
319-
320-
";
317+
static readonly string[] coreDefaultReferences = new[]
318+
{
319+
"netstandard.dll",
320+
"mscorlib.dll",
321+
"System.dll",
322+
"System.Collections.dll",
323+
"System.Console.dll",
324+
"System.Core.dll",
325+
"System.Linq.dll",
326+
"System.Linq.Expressions.dll",
327+
"System.Linq.Queryable.dll",
328+
"System.IO.FileSystem.Watcher.dll",
329+
"System.Memory.dll",
330+
"System.Threading.dll",
331+
"System.Threading.Thread.dll",
332+
"System.Runtime.dll",
333+
"System.Runtime.InteropServices.dll",
334+
"System.Xml.dll",
335+
"System.Xml.ReaderWriter.dll",
336+
"System.ValueTuple.dll",
337+
"Microsoft.CSharp.dll",
338+
"Microsoft.VisualBasic.dll",
339+
};
321340

322-
static readonly Lazy<string> targetFrameworkAttributeSnippetFile = new Lazy<string>(GetTargetFrameworkAttributeSnippetFile);
341+
static readonly Dictionary<string, Lazy<string>> targetFrameworkAttributeSnippetFiles = new() {
342+
{ ".NETCoreApp,Version=v10.0", new Lazy<string>(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v10.0")) },
343+
{ ".NETCoreApp,Version=v5.0", new Lazy<string>(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v5.0")) },
344+
{ ".NETCoreApp,Version=v2.2", new Lazy<string>(() => GetTargetFrameworkAttributeSnippetFile(".NETCoreApp,Version=v2.2")) },
345+
};
323346

324-
static string GetTargetFrameworkAttributeSnippetFile()
347+
static string GetTargetFrameworkAttributeSnippetFile(string targetFrameworkMoniker)
325348
{
326349
// Note: this leaks a temporary file, we're not attempting to delete it, because it is only one.
327350
var tempFile = Path.GetTempFileName();
328-
File.WriteAllText(tempFile, targetFrameworkAttributeSnippet);
351+
File.WriteAllText(tempFile, $@"
352+
353+
[assembly: System.Runtime.Versioning.TargetFramework(""{targetFrameworkMoniker}"")]
354+
355+
");
329356
return tempFile;
330357
}
331358

@@ -452,10 +479,6 @@ public static async Task<CompilerResults> CompileCSharp(string sourceFileName, C
452479
}
453480
bool targetNet40 = (flags & CompilerOptions.TargetNet40) != 0;
454481
bool useRoslyn = (flags & CompilerOptions.UseRoslynMask) != 0;
455-
if (useRoslyn && !targetNet40)
456-
{
457-
sourceFileNames.Add(targetFrameworkAttributeSnippetFile.Value);
458-
}
459482

460483
if (targetNet40)
461484
{
@@ -469,35 +492,42 @@ public static async Task<CompilerResults> CompileCSharp(string sourceFileName, C
469492
CompilerResults results = new CompilerResults();
470493
results.PathToAssembly = outputFileName;
471494

472-
var (roslynVersion, languageVersion) = (flags & CompilerOptions.UseRoslynMask) switch {
473-
0 => ("legacy", "5"),
474-
CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "6"),
475-
CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest"),
476-
CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest"),
477-
_ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest")
495+
var (roslynVersion, languageVersion, targetFramework) = (flags & CompilerOptions.UseRoslynMask) switch {
496+
0 => ("legacy", "5", targetNet40 ? null : ".NETCoreApp,Version=v10.0"),
497+
CompilerOptions.UseRoslyn1_3_2 => ("1.3.2", "6", null),
498+
CompilerOptions.UseRoslyn2_10_0 => ("2.10.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v2.2"),
499+
CompilerOptions.UseRoslyn3_11_0 => ("3.11.0", "latest", targetNet40 ? null : ".NETCoreApp,Version=v5.0"),
500+
_ => (roslynLatestVersion, flags.HasFlag(CompilerOptions.Preview) ? "preview" : "latest", targetNet40 ? null : ".NETCoreApp,Version=v10.0")
478501
};
479502

480503
var cscPath = roslynToolset.GetCSharpCompiler(roslynVersion);
481504

482-
string libPath;
505+
string libPath, refAsmPath;
483506
IEnumerable<string> references;
484-
if (useRoslyn && !targetNet40)
507+
if (useRoslyn && targetFramework != null)
485508
{
486-
libPath = "\"" + coreRefAsmPath + "\"";
487-
references = coreDefaultReferences.Select(r => "-r:\"" + Path.Combine(coreRefAsmPath, r) + "\"");
509+
refAsmPath = RefAssembliesToolset.GetPath(targetFramework);
510+
if (targetFramework == ".NETCoreApp,Version=v2.2")
511+
references = core220DefaultReferences;
512+
else
513+
references = coreDefaultReferences;
514+
libPath = "\"" + refAsmPath + "\"";
515+
references = references.Select(r => "-r:\"" + Path.Combine(refAsmPath, r) + "\"");
516+
sourceFileNames.Add(targetFrameworkAttributeSnippetFiles[targetFramework].Value);
488517
}
489518
else
490519
{
491-
libPath = "\"" + RefAsmPath + "\",\"" + Path.Combine(RefAsmPath, "Facades") + "\"";
492-
references = defaultReferences.Select(r => "-r:\"" + Path.Combine(RefAsmPath, r) + "\"");
520+
refAsmPath = RefAssembliesToolset.GetPath("legacy");
521+
libPath = "\"" + refAsmPath + "\"";
522+
references = defaultReferences.Select(r => "-r:\"" + Path.Combine(refAsmPath, r) + "\"");
493523
}
494524
if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic))
495525
{
496-
references = references.Concat(new[] { "-r:\"Microsoft.VisualBasic.dll\"" });
526+
references = references.Concat(new[] { "-r:\"" + Path.Combine(refAsmPath, "Microsoft.VisualBasic.dll") + "\"" });
497527
}
498528
if (useRoslyn && !targetNet40 && flags.HasFlag(CompilerOptions.ReferenceUnsafe))
499529
{
500-
references = references.Concat(new[] { "-r:\"System.Runtime.CompilerServices.Unsafe.dll\"" });
530+
references = references.Concat(new[] { "-r:\"" + Path.Combine(refAsmPath, "System.Runtime.CompilerServices.Unsafe.dll") + "\"" });
501531
}
502532
string otherOptions = $"-nologo -noconfig " +
503533
$"-langversion:{languageVersion} " +
@@ -674,7 +704,7 @@ public static void CompileCSharpWithPdb(string assemblyName, Dictionary<string,
674704
}
675705

676706
var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(assemblyName),
677-
syntaxTrees, coreDefaultReferences.Select(r => MetadataReference.CreateFromFile(Path.Combine(coreRefAsmPath, r))),
707+
syntaxTrees, coreDefaultReferences.Select(r => MetadataReference.CreateFromFile(Path.Combine(RefAssembliesToolset.GetPath(".NETCoreApp,Version=v10.0"), r))),
678708
new CSharpCompilationOptions(
679709
OutputKind.DynamicallyLinkedLibrary,
680710
platform: Platform.AnyCpu,
@@ -756,7 +786,7 @@ public static Task<string> DecompileCSharp(string assemblyFileName, DecompilerSe
756786
string targetFramework = module.Metadata.DetectTargetFrameworkId();
757787
var resolver = new UniversalAssemblyResolver(assemblyFileName, false,
758788
targetFramework, null, PEStreamOptions.PrefetchMetadata);
759-
resolver.AddSearchDirectory(targetFramework.Contains(".NETFramework") ? RefAsmPath : coreRefAsmPath);
789+
resolver.AddSearchDirectory(RefAssembliesToolset.GetPath(targetFramework));
760790
var typeSystem = new DecompilerTypeSystem(module, resolver, settings);
761791
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings);
762792
decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAttributes());

ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ public void AllFilesHaveTests()
5959

6060
static readonly CompilerOptions[] roslynOnlyWithNet40Options =
6161
{
62-
CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
63-
CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
6462
CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
6563
CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
6664
CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40,
@@ -145,8 +143,6 @@ public void AllFilesHaveTests()
145143
{
146144
CompilerOptions.None,
147145
CompilerOptions.Optimize,
148-
CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
149-
CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
150146
CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
151147
CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
152148
CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40,
@@ -167,8 +163,6 @@ public void AllFilesHaveTests()
167163
{
168164
CompilerOptions.None,
169165
CompilerOptions.Optimize,
170-
CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
171-
CompilerOptions.Optimize | CompilerOptions.UseRoslyn1_3_2 | CompilerOptions.TargetNet40,
172166
CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
173167
CompilerOptions.Optimize | CompilerOptions.UseRoslyn2_10_0 | CompilerOptions.TargetNet40,
174168
CompilerOptions.UseRoslyn3_11_0 | CompilerOptions.TargetNet40,

ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.il

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
// .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
3232

3333
.custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V
34-
65 72 73 69 6F 6E 3D 76 32 2E 31 01 00 54 0E 14 // ersion=v2.1..T..
34+
65 72 73 69 6F 6E 3D 76 32 2E 32 01 00 54 0E 14 // ersion=v2.2..T..
3535
46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 // FrameworkDisplay
3636
4E 61 6D 65 00 ) // Name.
3737
.custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 05 44 65 62 75 67 00 00 ) // ...Debug..

0 commit comments

Comments
 (0)