using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tests
{
///
/// LinkXMLGeneratorTests
///
[TestFixture]
public class LinkXMLGeneratorTests
{
const string k_LinkFile = "link.xml";
///
/// Teardown
///
[TearDown]
public void OnTearDown()
{
if (File.Exists(k_LinkFile))
File.Delete(k_LinkFile);
}
///
/// Read the LinkXML file
///
/// linkFile
/// assemblyCount
/// typeCount
/// The contents of the LinkXML file
public static string ReadLinkXML(string linkFile, out int assemblyCount, out int typeCount)
{
FileAssert.Exists(linkFile);
var fileText = File.ReadAllText(linkFile);
assemblyCount = Regex.Matches(fileText, "
/// AssertTypePreserved
///
/// input
/// type
public static void AssertTypePreserved(string input, Type t)
{
StringAssert.IsMatch($"type.*?{t.FullName}.*?preserve=\"all\"", input);
}
///
/// AssertTypeWithAttributePreserved
///
/// input
/// fullName
public static void AssertTypeWithAttributePreserved(string input, string fullName)
{
StringAssert.IsMatch($"type.*?{fullName}.*? preserve=\"nothing\" serialized=\"true\"", input);
}
///
/// AssertAssemblyPreserved
///
/// input
/// assemlby
public static void AssertAssemblyPreserved(string input, Assembly a)
{
StringAssert.IsMatch($"assembly.*?{a.FullName}.*?preserve=\"all\"", input);
}
///
/// AssertAssemblyFullName
///
/// input
/// assemblyName
public static void AssertAssemblyFullName(string input, string assemblyName)
{
StringAssert.IsMatch($"assembly.*?{assemblyName}", input);
}
///
/// CreateDefault_Converts_ExpectedUnityEditorTypes
///
[Test]
public void CreateDefault_Converts_ExpectedUnityEditorTypes()
{
var types = LinkXmlGenerator.GetEditorTypeConversions();
var editorTypes = types.Select(x => x.Key).ToArray();
var runtimeTypes = types.Select(x => x.Value).ToArray();
var assemblies = runtimeTypes.Select(x => x.Assembly).Distinct().ToArray();
var link = LinkXmlGenerator.CreateDefault();
link.AddTypes(editorTypes);
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount);
Assert.AreEqual(assemblyCount, assemblies.Length);
Assert.AreEqual(typeCount, runtimeTypes.Length);
foreach (var t in runtimeTypes)
AssertTypePreserved(xml, t);
}
///
/// CreateDefault_DoesNotConvert_UnexpectedUnityEditorTypes
///
[Test]
public void CreateDefault_DoesNotConvert_UnexpectedUnityEditorTypes()
{
var unexpectedType = typeof(UnityEditor.BuildPipeline);
var link = LinkXmlGenerator.CreateDefault();
link.AddTypes(new[] { unexpectedType });
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount);
Assert.AreEqual(assemblyCount, 1);
Assert.AreEqual(typeCount, 1);
AssertTypePreserved(xml, unexpectedType);
}
///
/// LinkXML_Preserves_MultipleTypes_FromMultipleAssemblies
///
[Test]
public void LinkXML_Preserves_MultipleTypes_FromMultipleAssemblies()
{
var types = new[] { typeof(UnityEngine.MonoBehaviour), typeof(UnityEngine.Build.Pipeline.CompatibilityAssetBundleManifest) };
var link = new LinkXmlGenerator();
link.AddTypes(types);
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount);
Assert.AreEqual(assemblyCount, 2);
Assert.AreEqual(typeCount, types.Length);
foreach (var t in types)
AssertTypePreserved(xml, t);
}
///
/// LinkXML_Preserves_Assemblies
///
[Test]
public void LinkXML_Preserves_Assemblies()
{
var assemblies = new[] { typeof(UnityEngine.MonoBehaviour).Assembly, typeof(UnityEngine.Build.Pipeline.CompatibilityAssetBundleManifest).Assembly };
var link = new LinkXmlGenerator();
link.AddAssemblies(assemblies);
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount);
Assert.AreEqual(assemblyCount, assemblies.Length);
Assert.AreEqual(typeCount, 0);
foreach (var a in assemblies)
AssertAssemblyPreserved(xml, a);
}
///
/// LinkXML_Preserves_SerializeClasses
///
[Test]
public void LinkXML_Preserves_SerializeClasses()
{
var serializedRefClasses = new[] { "FantasticAssembly:AwesomeNS.Foo", "FantasticAssembly:AwesomeNS.Bar", "SuperFantasticAssembly:SuperAwesomeNS.Bar"};
var link = new LinkXmlGenerator();
link.AddSerializedClass(serializedRefClasses);
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount);
Assert.AreEqual(assemblyCount, 2);
AssertAssemblyFullName(xml, "FantasticAssembly");
AssertAssemblyFullName(xml, "SuperFantasticAssembly");
Assert.AreEqual(typeCount,3);
AssertTypeWithAttributePreserved(xml, "AwesomeNS.Foo");
AssertTypeWithAttributePreserved(xml, "AwesomeNS.Bar");
AssertTypeWithAttributePreserved(xml, "SuperAwesomeNS.Bar");
}
///
/// LinkXML_Is_Sorted
///
[Test]
public void LinkXML_Is_Sorted()
{
// For incremental builds to perform well, we need link xml files
// to be deterministic - if contents don't change, they should be bit-wise
// identical. So we sort contents, since HashSets/Dictionary have no deterministic order.
// This test verfies that assemblies and classes are sorted alphabetically.
// Note that types added via AddSerializedClass will always come after types and assemblies
// added via AddType/AddAssembly, regardless of alphabetical order.
var serializedRefClasses = new[]
{
"AssemblyC:NamespaceA.ClassA",
"AssemblyB:NamespaceC.ClassB",
"AssemblyA:NamespaceB.ClassC",
"AssemblyC:NamespaceC.ClassC",
"AssemblyB:NamespaceB.ClassA",
"AssemblyA:NamespaceA.ClassB",
"AssemblyA:NamespaceA.ClassC",
"AssemblyC:NamespaceB.ClassA",
"AssemblyB:NamespaceC.ClassA",
};
var types = new[] { typeof(UnityEngine.Texture), typeof(UnityEngine.MonoBehaviour) , typeof(UnityEngine.Build.Pipeline.CompatibilityAssetBundleManifest) };
var link = new LinkXmlGenerator();
link.AddSerializedClass(serializedRefClasses);
link.AddTypes(types);
link.Save(k_LinkFile);
var xml = ReadLinkXML(k_LinkFile, out int assemblyCount, out int typeCount)
.Replace(" ", "")
.Replace("\n", "")
.Replace("\r", "");
Console.WriteLine(xml);
var sorted = @"
".Replace(" ", "").Replace("\n", "").Replace("\r", "");
Assert.AreEqual(sorted, xml);
}
}
}