418 строки
14 KiB
C#
418 строки
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
using NUnit.Framework;
|
|
using Xamarin.Bundler;
|
|
using Xamarin.Utils;
|
|
|
|
using Mono.Tuner;
|
|
using MonoMac.Tuner;
|
|
|
|
namespace Xamarin.MMP.Tests.Unit
|
|
{
|
|
[TestFixture]
|
|
public class AotTests
|
|
{
|
|
const string TestRootDir = "/a/non/sense/dir/";
|
|
|
|
class TestFileEnumerator : IFileEnumerator
|
|
{
|
|
public IEnumerable<string> Files { get; }
|
|
public string RootDir { get; } = TestRootDir;
|
|
|
|
public TestFileEnumerator (IEnumerable <string> files)
|
|
{
|
|
Files = files;
|
|
}
|
|
}
|
|
|
|
List<Tuple<string, IList<string>>> commandsRun;
|
|
|
|
[SetUp]
|
|
public void Init ()
|
|
{
|
|
// Make sure this is cleared between every test
|
|
commandsRun = new List<Tuple<string, IList<string>>> ();
|
|
}
|
|
|
|
void Compile (AOTOptions options, TestFileEnumerator files, AOTCompilerType compilerType = AOTCompilerType.Bundled64, RunCommandDelegate onRunDelegate = null, bool isRelease = false, bool isModern = false)
|
|
{
|
|
AOTCompiler compiler = new AOTCompiler (options, compilerType, isModern, isRelease)
|
|
{
|
|
RunCommand = onRunDelegate != null ? onRunDelegate : OnRunCommand,
|
|
ParallelOptions = new ParallelOptions () { MaxDegreeOfParallelism = 1 },
|
|
XamarinMacPrefix = Driver.WalkUpDirHierarchyLookingForLocalBuild (), // HACK - AOT test shouldn't need this from driver.cs
|
|
};
|
|
try {
|
|
Profile.Current = new XamarinMacProfile ();
|
|
compiler.Compile (files);
|
|
} finally {
|
|
Profile.Current = null;
|
|
}
|
|
}
|
|
|
|
void ClearCommandsRun ()
|
|
{
|
|
commandsRun.Clear ();
|
|
}
|
|
|
|
int OnRunCommand (string path, IList<string> args, Dictionary<string, string> env, StringBuilder output, bool suppressPrintOnErrors)
|
|
{
|
|
commandsRun.Add (Tuple.Create <string, IList<string>>(path, args));
|
|
if (path != AOTCompiler.StripCommand && path != AOTCompiler.DeleteDebugSymbolCommand) {
|
|
Assert.AreEqual (TestRootDir, env ["MONO_PATH"], "MONO_PATH should be set to our expected value");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
string GetExpectedMonoCommand (AOTCompilerType compilerType)
|
|
{
|
|
switch (compilerType) {
|
|
case AOTCompilerType.Bundled64:
|
|
return "mono-sgen";
|
|
case AOTCompilerType.System64:
|
|
return "mono64";
|
|
default:
|
|
Assert.Fail ("GetMonoPath with invalid option");
|
|
return "";
|
|
}
|
|
}
|
|
|
|
List<string> GetFiledAOTed (AOTCompilerType compilerType = AOTCompilerType.Bundled64, AOTKind kind = AOTKind.Standard, bool isModern = false, bool expectStripping = false, bool expectSymbolDeletion = false)
|
|
{
|
|
List<string> filesAOTed = new List<string> ();
|
|
|
|
foreach (var command in commandsRun) {
|
|
if (expectStripping && command.Item1 == AOTCompiler.StripCommand)
|
|
continue;
|
|
if (expectSymbolDeletion && command.Item1 == AOTCompiler.DeleteDebugSymbolCommand)
|
|
continue;
|
|
Assert.IsTrue (command.Item1.EndsWith (GetExpectedMonoCommand (compilerType)), "Unexpected command: " + command.Item1);
|
|
var argParts = command.Item2;
|
|
|
|
if (kind == AOTKind.Hybrid)
|
|
Assert.AreEqual (argParts[0], "--aot=hybrid", "First arg should be --aot=hybrid");
|
|
else
|
|
Assert.AreEqual (argParts[0], "--aot", "First arg should be --aot");
|
|
|
|
if (isModern)
|
|
Assert.AreEqual (argParts[1], "--runtime=mobile", "Second arg should be --runtime=mobile");
|
|
else
|
|
Assert.AreNotEqual (argParts[1], "--runtime=mobile", "Second arg should not be --runtime=mobile");
|
|
|
|
|
|
var fileName = command.Item2 [1];
|
|
if (isModern)
|
|
fileName = command.Item2 [2];
|
|
|
|
filesAOTed.Add (fileName);
|
|
}
|
|
return filesAOTed;
|
|
}
|
|
|
|
IEnumerable<string> GetFilesStripped ()
|
|
{
|
|
return commandsRun.Where (x => x.Item1 == AOTCompiler.StripCommand).SelectMany (x => x.Item2);
|
|
}
|
|
|
|
void AssertFilesStripped (IEnumerable <string> expectedFiles)
|
|
{
|
|
var filesStripped = GetFilesStripped ();
|
|
|
|
Func<string> getErrorDetails = () => $"\n {FormatDebugList (filesStripped)} \nvs\n {FormatDebugList (expectedFiles)}\n{AllCommandsRun}";
|
|
|
|
Assert.AreEqual (filesStripped.Count (), expectedFiles.Count (), "Different number of files stripped than expected: " + getErrorDetails ());
|
|
Assert.IsTrue (filesStripped.All (x => expectedFiles.Contains (x)), "Different files stripped than expected: " + getErrorDetails ());
|
|
}
|
|
|
|
List<string> GetDeletedSymbols ()
|
|
{
|
|
// Chop off -r prefix and quotes around filename
|
|
return commandsRun.Where (x => x.Item1 == AOTCompiler.DeleteDebugSymbolCommand).Select (x => x.Item2 [1]).ToList ();
|
|
}
|
|
|
|
string AllCommandsRun => "\nCommands Run:\n\t" + String.Join ("\n\t", commandsRun.Select (x => $"{x.Item1} {x.Item2}"));
|
|
string FormatDebugList (IEnumerable <string> list) => String.Join (" ", list.Select (x => "\"" + x + "\""));
|
|
|
|
void AssertSymbolsDeleted (IEnumerable <string> expectedFiles)
|
|
{
|
|
expectedFiles = expectedFiles.Select (x => x + ".dylib.dSYM/").ToList ();
|
|
List<string> symbolsDeleted = GetDeletedSymbols ();
|
|
|
|
Func<string> getErrorDetails = () => $"\n {FormatDebugList (symbolsDeleted)} \nvs\n {FormatDebugList (expectedFiles)}\n{AllCommandsRun}";
|
|
|
|
Assert.AreEqual (symbolsDeleted.Count, expectedFiles.Count (), "Different number of symbols deleted than expected: " + getErrorDetails ());
|
|
Assert.IsTrue (symbolsDeleted.All (x => expectedFiles.Contains (x)), "Different files deleted than expected: " + getErrorDetails ());
|
|
}
|
|
|
|
void AssertFilesAOTed (IEnumerable <string> expectedFiles, AOTCompilerType compilerType = AOTCompilerType.Bundled64, AOTKind kind = AOTKind.Standard, bool isModern = false, bool expectStripping = false, bool expectSymbolDeletion = false)
|
|
{
|
|
List<string> filesAOTed = GetFiledAOTed (compilerType, kind, isModern: isModern, expectStripping: expectStripping, expectSymbolDeletion : expectSymbolDeletion);
|
|
|
|
Func<string> getErrorDetails = () => $"\n {FormatDebugList (filesAOTed)} \nvs\n {FormatDebugList (expectedFiles)}\n{AllCommandsRun}";
|
|
|
|
Assert.AreEqual (filesAOTed.Count, expectedFiles.Count (), "Different number of files AOT than expected: " + getErrorDetails ());
|
|
Assert.IsTrue (filesAOTed.All (x => expectedFiles.Contains (x)), "Different files AOT than expected: " + getErrorDetails ());
|
|
}
|
|
|
|
void AssertThrowErrorWithCode (Action action, int code)
|
|
{
|
|
try {
|
|
action ();
|
|
}
|
|
catch (ProductException e) {
|
|
Assert.AreEqual (e.Code, code, $"Got code {e.Code} but expected {code}");
|
|
return;
|
|
}
|
|
catch (AggregateException e) {
|
|
Assert.AreEqual (e.InnerExceptions.Count, 1, "Got AggregateException but more than one exception");
|
|
ProductException innerException = e.InnerExceptions[0] as ProductException;
|
|
Assert.IsNotNull (innerException, "Got AggregateException but inner not ProductException");
|
|
Assert.AreEqual (innerException.Code, code, $"Got code {innerException.Code} but expected {code}");
|
|
return;
|
|
}
|
|
Assert.Fail ($"We should have thrown ProductException with code: {code}");
|
|
}
|
|
|
|
readonly string [] FullAppFileList = {
|
|
"Foo Bar.exe", "libMonoPosixHelper.dylib", "mscorlib.dll", "Xamarin.Mac.dll", "System.dll", "System.Core.dll"
|
|
};
|
|
|
|
readonly string [] CoreXMFileList = { "mscorlib.dll", "Xamarin.Mac.dll", "System.dll" };
|
|
readonly string [] SDKFileList = { "mscorlib.dll", "Xamarin.Mac.dll", "System.dll", "System.Core.dll" };
|
|
|
|
[Test]
|
|
public void ParsingNone_DoesNoAOT ()
|
|
{
|
|
var options = new AOTOptions ("none");
|
|
Assert.IsFalse (options.IsAOT, "Parsing none should not be IsAOT");
|
|
AssertThrowErrorWithCode (() => Compile (options, new TestFileEnumerator (FullAppFileList)), 99);
|
|
}
|
|
|
|
[Test]
|
|
public void All_AOTAllFiles ()
|
|
{
|
|
var options = new AOTOptions ("all");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
|
|
var expectedFiles = FullAppFileList.Where (x => x.EndsWith (".exe") || x.EndsWith (".dll"));
|
|
AssertFilesAOTed (expectedFiles);
|
|
}
|
|
|
|
[Test]
|
|
public void Core_ParsingJustCoreFiles()
|
|
{
|
|
var options = new AOTOptions ("core");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
|
|
AssertFilesAOTed (CoreXMFileList);
|
|
}
|
|
|
|
[Test]
|
|
public void SDK_ParsingJustSDKFiles()
|
|
{
|
|
var options = new AOTOptions ("sdk");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
|
|
AssertFilesAOTed (SDKFileList);
|
|
}
|
|
|
|
[Test]
|
|
public void ExplicitAssembly_JustAOTExplicitFile ()
|
|
{
|
|
var options = new AOTOptions ("+System.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
|
|
AssertFilesAOTed (new string [] { "System.dll" });
|
|
}
|
|
|
|
[Test]
|
|
public void CoreWithInclusionAndSubtraction ()
|
|
{
|
|
var options = new AOTOptions ("core,+Foo.dll,-Xamarin.Mac.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
string [] testFiles = {
|
|
"Foo.dll", "Foo Bar.exe", "libMonoPosixHelper.dylib", "mscorlib.dll", "Xamarin.Mac.dll", "System.dll"
|
|
};
|
|
Compile (options, new TestFileEnumerator (testFiles));
|
|
|
|
AssertFilesAOTed (new string [] { "Foo.dll", "mscorlib.dll", "System.dll" });
|
|
}
|
|
|
|
[Test]
|
|
public void CoreWithFullPath_GivesFullPathCommands ()
|
|
{
|
|
var options = new AOTOptions ("core,-Xamarin.Mac.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList.Select (x => TestRootDir + x)));
|
|
|
|
AssertFilesAOTed (new string [] { TestRootDir + "mscorlib.dll", TestRootDir + "System.dll" });
|
|
}
|
|
|
|
[Test]
|
|
public void ExplicitNegativeFileWithNonExistantFiles_ThrowError ()
|
|
{
|
|
var options = new AOTOptions ("core,-NonExistant.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
AssertThrowErrorWithCode (() => Compile (options, new TestFileEnumerator (FullAppFileList)), 3010);
|
|
}
|
|
|
|
[Test]
|
|
public void ExplicitPositiveFileWithNonExistantFiles_ThrowError ()
|
|
{
|
|
var options = new AOTOptions ("core,+NonExistant.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
AssertThrowErrorWithCode (() => Compile (options, new TestFileEnumerator (FullAppFileList)), 3009);
|
|
}
|
|
|
|
[Test]
|
|
public void ExplicitNegativeWithNoAssemblies_ShouldNoOp()
|
|
{
|
|
var options = new AOTOptions ("-System.dll");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
AssertFilesAOTed (new string [] {});
|
|
}
|
|
|
|
[Test]
|
|
public void ParsingSimpleOptions_InvalidOption ()
|
|
{
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("FooBar"), 20);
|
|
}
|
|
|
|
[Test]
|
|
public void WhenAOTFails_ShouldReturnError ()
|
|
{
|
|
RunCommandDelegate runThatErrors = (path, args, env, output, suppressPrintOnErrors) => 42;
|
|
var options = new AOTOptions ("all");
|
|
|
|
AssertThrowErrorWithCode (() => Compile (options, new TestFileEnumerator (FullAppFileList), onRunDelegate : runThatErrors), 3001);
|
|
}
|
|
|
|
[Test]
|
|
public void DifferentMonoTypes_ShouldInvokeCorrectMono ()
|
|
{
|
|
foreach (var compilerType in new List<AOTCompilerType> () { AOTCompilerType.Bundled64, AOTCompilerType.System64 })
|
|
{
|
|
ClearCommandsRun ();
|
|
var options = new AOTOptions ("sdk");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList), compilerType);
|
|
|
|
AssertFilesAOTed (SDKFileList, compilerType);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PipeFileName_ShouldNotHybridCompiler ()
|
|
{
|
|
foreach (var testCase in new string [] { "+|hybrid.dll", "core,+|hybrid.dll,-Xamarin.Mac.dll" }){
|
|
ClearCommandsRun ();
|
|
var options = new AOTOptions (testCase);
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
Assert.IsFalse (options.IsHybridAOT, "Should not be IsHybridAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (new string [] { "|hybrid.dll", "Xamarin.Mac.dll" }));
|
|
AssertFilesAOTed (new string [] {"|hybrid.dll"});
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void InvalidHybridOptions_ShouldThrow ()
|
|
{
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("|"), 20);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("|hybrid"), 20);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("core|"), 20);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("foo|hybrid"), 20);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("core|foo"), 20);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("|hybrid,+Foo.dll"), 20);
|
|
}
|
|
|
|
[Test]
|
|
public void HybridOption_ShouldInvokeHybridCompiler ()
|
|
{
|
|
var options = new AOTOptions ("all|hybrid");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
Assert.IsTrue (options.IsHybridAOT, "Should be IsHybridAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList));
|
|
|
|
var expectedFiles = FullAppFileList.Where (x => x.EndsWith (".exe") || x.EndsWith (".dll"));
|
|
AssertFilesAOTed (expectedFiles, kind : AOTKind.Hybrid);
|
|
}
|
|
|
|
[Test]
|
|
public void AllReleaseHybrid_AOTStripAndDelete ()
|
|
{
|
|
var options = new AOTOptions ("all|hybrid");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList), isRelease : true);
|
|
|
|
var expectedFiles = FullAppFileList.Where (x => x.EndsWith (".exe") || x.EndsWith (".dll"));
|
|
AssertFilesAOTed (expectedFiles, kind : AOTKind.Hybrid, expectStripping : true, expectSymbolDeletion : true);
|
|
AssertFilesStripped (expectedFiles);
|
|
AssertSymbolsDeleted (expectedFiles);
|
|
}
|
|
|
|
[Test]
|
|
public void AllReleaseNonHybrid_ShouldNotStripButDelete ()
|
|
{
|
|
var options = new AOTOptions ("all");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList), isRelease : true);
|
|
|
|
var expectedFiles = FullAppFileList.Where (x => x.EndsWith (".exe") || x.EndsWith (".dll"));
|
|
AssertFilesAOTed (expectedFiles, expectStripping : false, expectSymbolDeletion : true);
|
|
AssertFilesStripped (new string [] {});
|
|
AssertSymbolsDeleted (expectedFiles);
|
|
}
|
|
|
|
[Test]
|
|
public void WhenAssemblyStrippingFails_ShouldThrowError ()
|
|
{
|
|
RunCommandDelegate runThatErrors = (path, args, env, output, suppressPrintOnErrors) => path.Contains ("mono-cil-strip") ? 42 : 0;
|
|
|
|
var options = new AOTOptions ("all|hybrid");
|
|
|
|
AssertThrowErrorWithCode (() => Compile (options, new TestFileEnumerator (FullAppFileList), onRunDelegate : runThatErrors, isRelease : true), 3001);
|
|
}
|
|
|
|
[Test]
|
|
public void HybridOption_MustAlsoHaveAll_ThrowsIfNot ()
|
|
{
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("core|hybrid"), 114);
|
|
AssertThrowErrorWithCode (() => new AOTOptions ("sdk|hybrid"), 114);
|
|
var options = new AOTOptions ("all|hybrid");
|
|
}
|
|
|
|
[Test]
|
|
public void All_AOTAllFiles_Modern ()
|
|
{
|
|
var options = new AOTOptions ("all");
|
|
Assert.IsTrue (options.IsAOT, "Should be IsAOT");
|
|
|
|
Compile (options, new TestFileEnumerator (FullAppFileList), isModern : true);
|
|
|
|
var expectedFiles = FullAppFileList.Where (x => x.EndsWith (".exe") || x.EndsWith (".dll"));
|
|
AssertFilesAOTed (expectedFiles, isModern : true);
|
|
}
|
|
}
|
|
}
|