381 lines
16 KiB
C#
381 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using NUnit.Framework;
|
|
using UnityEditor.Build.Content;
|
|
using UnityEditor.Build.Pipeline.Injector;
|
|
using UnityEditor.Build.Pipeline.Interfaces;
|
|
using UnityEditor.Build.Pipeline.Tasks;
|
|
using UnityEditor.Build.Pipeline.Utilities;
|
|
using UnityEditor.Build.Player;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEditor.U2D;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace UnityEditor.Build.Pipeline.Tests
|
|
{
|
|
/// <summary>
|
|
/// CalculateSceneDependencyTests
|
|
/// </summary>
|
|
public class CalculateSceneDependencyTests
|
|
{
|
|
class TestParams : TestBuildParametersBase
|
|
{
|
|
// Inputs
|
|
public override bool UseCache { get; set; }
|
|
public override BuildTarget Target { get => BuildTarget.NoTarget; }
|
|
public override BuildTargetGroup Group { get => BuildTargetGroup.Unknown; }
|
|
public override TypeDB ScriptInfo { get => null; }
|
|
public override ContentBuildFlags ContentBuildFlags { get => ContentBuildFlags.None; }
|
|
public override bool NonRecursiveDependencies { get; set; }
|
|
|
|
public override BuildSettings GetContentBuildSettings()
|
|
{
|
|
return new BuildSettings
|
|
{
|
|
group = Group,
|
|
target = Target,
|
|
typeDB = ScriptInfo,
|
|
buildFlags = ContentBuildFlags
|
|
};
|
|
}
|
|
}
|
|
|
|
class TestContent : TestBundleBuildContent
|
|
{
|
|
public List<GUID> TestScenes = new List<GUID>();
|
|
public List<GUID> TestAssets = new List<GUID>();
|
|
|
|
// Inputs
|
|
public override List<GUID> Scenes => TestScenes;
|
|
public override List<GUID> Assets => TestAssets;
|
|
}
|
|
|
|
class TestDependencyData : TestDependencyDataBase
|
|
{
|
|
public Dictionary<GUID, SceneDependencyInfo> TestSceneInfo = new Dictionary<GUID, SceneDependencyInfo>();
|
|
public Dictionary<GUID, BuildUsageTagSet> TestSceneUsage = new Dictionary<GUID, BuildUsageTagSet>();
|
|
public Dictionary<GUID, Hash128> TestDependencyHash = new Dictionary<GUID, Hash128>();
|
|
|
|
// Inputs
|
|
public override BuildUsageCache DependencyUsageCache => null;
|
|
|
|
// Outputs
|
|
public override Dictionary<GUID, SceneDependencyInfo> SceneInfo => TestSceneInfo;
|
|
public override Dictionary<GUID, BuildUsageTagSet> SceneUsage => TestSceneUsage;
|
|
public override Dictionary<GUID, Hash128> DependencyHash => TestDependencyHash;
|
|
}
|
|
|
|
const string k_FolderPath = "Test";
|
|
const string k_TmpPath = "tmp";
|
|
|
|
const string k_ScenePath = "Assets/testScene.unity";
|
|
const string k_TestAssetsPath = "Assets/TestAssetsOnlyWillBeDeleted";
|
|
const string k_CubePath = k_TestAssetsPath + "/Cube.prefab";
|
|
const string k_CubePath2 = k_TestAssetsPath + "/Cube2.prefab";
|
|
const string k_SpriteAtlasPath = k_TestAssetsPath + "/SpriteAtlas.spriteatlasv2";
|
|
const string k_BlackTexturePath = k_TestAssetsPath + "/BlackTexture.png";
|
|
|
|
static CalculateSceneDependencyData CreateDefaultBuildTask(List<GUID> scenes, BuildCache optionalCache, bool nonRecursive = false, List<GUID> assets = null)
|
|
{
|
|
var task = new CalculateSceneDependencyData();
|
|
var testParams = new TestParams();
|
|
testParams.UseCache = optionalCache != null;
|
|
testParams.NonRecursiveDependencies = nonRecursive;
|
|
var testContent = new TestContent { TestScenes = scenes };
|
|
if (assets != null) testContent.TestAssets = assets;
|
|
var testData = new TestDependencyData();
|
|
var buildResult = new BundleBuildResults();
|
|
IBuildContext context = new BuildContext(testParams, testContent, testData, optionalCache, buildResult);
|
|
ContextInjector.Inject(context, task);
|
|
return task;
|
|
}
|
|
|
|
static void ExtractTestData(IBuildTask task, out TestDependencyData dependencyData)
|
|
{
|
|
IBuildContext context = new BuildContext();
|
|
ContextInjector.Extract(context, task);
|
|
dependencyData = (TestDependencyData)context.GetContextObject<IDependencyData>();
|
|
}
|
|
|
|
static ObjectIdentifier MakeObjectId(string guid, long localIdentifierInFile, FileType fileType, string filePath)
|
|
{
|
|
var objectId = new ObjectIdentifier();
|
|
var boxed = (object)objectId;
|
|
var type = typeof(ObjectIdentifier);
|
|
type.GetField("m_GUID", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(boxed, new GUID(guid));
|
|
type.GetField("m_LocalIdentifierInFile", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(boxed, localIdentifierInFile);
|
|
type.GetField("m_FileType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(boxed, fileType);
|
|
type.GetField("m_FilePath", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(boxed, filePath);
|
|
return (ObjectIdentifier)boxed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setup
|
|
/// </summary>
|
|
[OneTimeSetUp]
|
|
public void Setup()
|
|
{
|
|
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
|
|
Directory.CreateDirectory(k_TestAssetsPath);
|
|
#if UNITY_2018_3_OR_NEWER
|
|
PrefabUtility.SaveAsPrefabAsset(GameObject.CreatePrimitive(PrimitiveType.Cube), k_CubePath);
|
|
PrefabUtility.SaveAsPrefabAsset(GameObject.CreatePrimitive(PrimitiveType.Cube), k_CubePath2);
|
|
#else
|
|
PrefabUtility.CreatePrefab(k_CubePath, GameObject.CreatePrimitive(PrimitiveType.Cube));
|
|
PrefabUtility.CreatePrefab(k_CubePath2, GameObject.CreatePrimitive(PrimitiveType.Cube));
|
|
#endif
|
|
AssetDatabase.ImportAsset(k_CubePath);
|
|
AssetDatabase.ImportAsset(k_CubePath2);
|
|
}
|
|
|
|
#if UNITY_2021_1_OR_NEWER
|
|
/// <summary>
|
|
/// SetupSceneWithSpritesForTest
|
|
/// </summary>
|
|
public void SetupSceneWithSpritesForTest(out Scene scene)
|
|
{
|
|
var spriteAtlasAsset = new SpriteAtlasAsset();
|
|
SpriteAtlasAsset.Save(spriteAtlasAsset, k_SpriteAtlasPath);
|
|
|
|
File.WriteAllBytes(k_BlackTexturePath, Texture2D.blackTexture.EncodeToPNG());
|
|
AssetDatabase.ImportAsset(k_BlackTexturePath);
|
|
var blackTextureImporter = (TextureImporter)AssetImporter.GetAtPath(k_BlackTexturePath);
|
|
var blackTextureImporterSettings = new TextureImporterSettings();
|
|
blackTextureImporterSettings.ApplyTextureType(TextureImporterType.Sprite);
|
|
blackTextureImporterSettings.spriteMode = (int)SpriteImportMode.Single;
|
|
blackTextureImporterSettings.textureShape = TextureImporterShape.Texture2D;
|
|
blackTextureImporter.SetTextureSettings(blackTextureImporterSettings);
|
|
blackTextureImporter.SaveAndReimport();
|
|
|
|
scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
|
|
|
|
var blackSprite = AssetDatabase.LoadAssetAtPath<Sprite>(k_BlackTexturePath);
|
|
var newGameObject = new GameObject("ObjectWithSprite");
|
|
var spriteRenderer = newGameObject.AddComponent<SpriteRenderer>();
|
|
spriteRenderer.sprite = blackSprite;
|
|
|
|
EditorSceneManager.SaveScene(scene, k_ScenePath);
|
|
}
|
|
#endif
|
|
/// <summary>
|
|
/// Cleanup
|
|
/// </summary>
|
|
[OneTimeTearDown]
|
|
public void Cleanup()
|
|
{
|
|
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
|
|
AssetDatabase.DeleteAsset(k_ScenePath);
|
|
AssetDatabase.DeleteAsset(k_CubePath);
|
|
AssetDatabase.DeleteAsset(k_CubePath2);
|
|
AssetDatabase.DeleteAsset(k_TestAssetsPath);
|
|
|
|
if (Directory.Exists(k_FolderPath))
|
|
Directory.Delete(k_FolderPath, true);
|
|
if (Directory.Exists(k_TmpPath))
|
|
Directory.Delete(k_TmpPath, true);
|
|
}
|
|
|
|
static void SetupSceneForTest(out Scene scene, out GameObject prefab)
|
|
{
|
|
scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
|
|
|
|
prefab = AssetDatabase.LoadAssetAtPath<GameObject>(k_CubePath);
|
|
prefab.transform.position = new Vector3(0, 0, 0);
|
|
EditorUtility.SetDirty(prefab);
|
|
AssetDatabase.SaveAssets();
|
|
PrefabUtility.InstantiatePrefab(prefab);
|
|
|
|
EditorSceneManager.SaveScene(scene, k_ScenePath);
|
|
}
|
|
|
|
static object[] DependencyHashTestCases =
|
|
{
|
|
new object[] { true, (Action<object, object>)Assert.AreNotEqual },
|
|
new object[] { false, (Action<object, object>)Assert.AreEqual },
|
|
};
|
|
|
|
/// <summary>
|
|
/// CalculateSceneDependencyData_DependencyHashTests
|
|
/// </summary>
|
|
/// <param name="modifyPrefab">modifyPrefab</param>
|
|
/// <param name="assertType">assertType</param>
|
|
[TestCaseSource("DependencyHashTestCases")]
|
|
[Test]
|
|
public void CalculateSceneDependencyData_DependencyHashTests(bool modifyPrefab, Action<object, object> assertType)
|
|
{
|
|
SetupSceneForTest(out Scene scene, out GameObject prefab);
|
|
|
|
List<GUID> scenes = new List<GUID>();
|
|
GUID sceneGuid = new GUID(AssetDatabase.AssetPathToGUID(scene.path));
|
|
scenes.Add(sceneGuid);
|
|
|
|
TestDependencyData dependencyData1;
|
|
using (BuildCache cache = new BuildCache())
|
|
{
|
|
var buildTask = CreateDefaultBuildTask(scenes, cache);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData1);
|
|
}
|
|
BuildCache.PurgeCache(false);
|
|
|
|
if (modifyPrefab)
|
|
prefab.transform.position = new Vector3(1, 1, 1);
|
|
EditorUtility.SetDirty(prefab);
|
|
AssetDatabase.SaveAssets();
|
|
|
|
TestDependencyData dependencyData2;
|
|
using (BuildCache cache = new BuildCache())
|
|
{
|
|
var buildTask = CreateDefaultBuildTask(scenes, cache);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData2);
|
|
}
|
|
BuildCache.PurgeCache(false);
|
|
|
|
assertType(dependencyData1.DependencyHash[sceneGuid], dependencyData2.DependencyHash[sceneGuid]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// CalculateSceneDependencyData_DependencyHash_IsZeroWhenNotUsingCashing
|
|
/// </summary>
|
|
[Test]
|
|
public void CalculateSceneDependencyData_DependencyHash_IsZeroWhenNotUsingCashing()
|
|
{
|
|
SetupSceneForTest(out Scene scene, out var _);
|
|
|
|
List<GUID> scenes = new List<GUID>();
|
|
GUID sceneGuid = new GUID(AssetDatabase.AssetPathToGUID(scene.path));
|
|
scenes.Add(sceneGuid);
|
|
|
|
TestDependencyData dependencyData;
|
|
var buildTask = CreateDefaultBuildTask(scenes, null);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData);
|
|
|
|
Assert.AreEqual(new Hash128(), dependencyData.DependencyHash[sceneGuid]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// CalcualteSceneDependencyData_ReturnsNonEmptyUsage_ForNonRecursiveDependencies
|
|
/// </summary>
|
|
[Test]
|
|
public void CalcualteSceneDependencyData_ReturnsNonEmptyUsage_ForNonRecursiveDependencies()
|
|
{
|
|
SetupSceneForTest(out Scene scene, out var _);
|
|
|
|
List<GUID> scenes = new List<GUID>();
|
|
GUID sceneGuid = new GUID(AssetDatabase.AssetPathToGUID(scene.path));
|
|
scenes.Add(sceneGuid);
|
|
|
|
TestDependencyData dependencyData;
|
|
var buildTask = CreateDefaultBuildTask(scenes, null, true);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData);
|
|
|
|
BuildUsageTagSet usagetSet = dependencyData.SceneUsage[sceneGuid];
|
|
var method = typeof(BuildUsageTagSet).GetMethod("SerializeToJson", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
var json = method.Invoke(usagetSet, new object[0]) as string;
|
|
|
|
Assert.AreNotEqual(json, "{\"m_objToUsage\":[]}");
|
|
}
|
|
|
|
#if UNITY_2022_3_OR_NEWER
|
|
/// <summary>
|
|
/// CalculateSceneDependencyData_WhenSpriteDependencyChanges_RecalculatesDependencies
|
|
/// </summary>
|
|
[Test]
|
|
public void CalculateSceneDependencyData_WhenSpriteDependencyChanges_RecalculatesDependencies()
|
|
{
|
|
var prevSpritePackerModeMode = EditorSettings.spritePackerMode;
|
|
EditorSettings.spritePackerMode = SpritePackerMode.SpriteAtlasV2;
|
|
SetupSceneWithSpritesForTest(out var scene);
|
|
|
|
List<GUID> scenes = new List<GUID>();
|
|
GUID sceneGuid = new GUID(AssetDatabase.AssetPathToGUID(scene.path));
|
|
scenes.Add(sceneGuid);
|
|
|
|
TestDependencyData dependencyData1;
|
|
using (BuildCache cache = new BuildCache())
|
|
{
|
|
var buildTask = CreateDefaultBuildTask(scenes, cache);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData1);
|
|
}
|
|
|
|
var spriteAtlas = SpriteAtlasAsset.Load(k_SpriteAtlasPath);
|
|
var blackSprite = AssetDatabase.LoadAssetAtPath<Sprite>(k_BlackTexturePath);
|
|
spriteAtlas.Add(new UnityEngine.Object[] { blackSprite });
|
|
SpriteAtlasAsset.Save(spriteAtlas, k_SpriteAtlasPath);
|
|
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
|
|
|
|
TestDependencyData dependencyData2;
|
|
using (BuildCache cache = new BuildCache())
|
|
{
|
|
var buildTask = CreateDefaultBuildTask(scenes, cache);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData2);
|
|
}
|
|
|
|
Assert.AreNotEqual(dependencyData1.SceneInfo[sceneGuid].referencedObjects.Count, dependencyData2.SceneInfo[sceneGuid].referencedObjects.Count);
|
|
|
|
EditorSettings.spritePackerMode = prevSpritePackerModeMode;
|
|
}
|
|
|
|
[Test]
|
|
public void CalculateSceneDependencyData_PackedSprites_DontReferenceSourceTexture([Values] bool nonRecursive, [Values] bool packed)
|
|
{
|
|
var prevSpritePackerModeMode = EditorSettings.spritePackerMode;
|
|
EditorSettings.spritePackerMode = SpritePackerMode.SpriteAtlasV2;
|
|
SetupSceneWithSpritesForTest(out var scene);
|
|
|
|
List<GUID> scenes = new List<GUID>();
|
|
GUID sceneGuid = new GUID(AssetDatabase.AssetPathToGUID(scene.path));
|
|
scenes.Add(sceneGuid);
|
|
|
|
List<GUID> assets = new List<GUID>();
|
|
var spriteGuid = new GUID(AssetDatabase.AssetPathToGUID(k_BlackTexturePath));
|
|
assets.Add(spriteGuid);
|
|
|
|
if (packed)
|
|
{
|
|
var spriteAtlas = SpriteAtlasAsset.Load(k_SpriteAtlasPath);
|
|
var blackSprite = AssetDatabase.LoadAssetAtPath<Sprite>(k_BlackTexturePath);
|
|
spriteAtlas.Add(new UnityEngine.Object[] { blackSprite });
|
|
SpriteAtlasAsset.Save(spriteAtlas, k_SpriteAtlasPath);
|
|
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
|
|
}
|
|
|
|
TestDependencyData dependencyData;
|
|
using (BuildCache cache = new BuildCache())
|
|
{
|
|
var buildTask = CreateDefaultBuildTask(scenes, null, nonRecursive, assets);
|
|
buildTask.Run();
|
|
ExtractTestData(buildTask, out dependencyData);
|
|
}
|
|
|
|
// Clean-up before the assert
|
|
if (packed)
|
|
{
|
|
AssetDatabase.DeleteAsset(k_SpriteAtlasPath);
|
|
}
|
|
|
|
var texRefCount = 0;
|
|
foreach (var dep in dependencyData.SceneInfo[sceneGuid].referencedObjects)
|
|
{
|
|
if (dep.guid == spriteGuid)
|
|
texRefCount++;
|
|
}
|
|
// If packed, only needs to reference the Sprite
|
|
// Not packed, so we need to reference the Sprite and the Texture2D
|
|
Assert.AreEqual(packed ? 1 : 2, texRefCount);
|
|
|
|
EditorSettings.spritePackerMode = prevSpritePackerModeMode;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|